summaryrefslogtreecommitdiffstats
path: root/dom/base/nsStyledElement.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/base/nsStyledElement.cpp')
-rw-r--r--dom/base/nsStyledElement.cpp223
1 files changed, 223 insertions, 0 deletions
diff --git a/dom/base/nsStyledElement.cpp b/dom/base/nsStyledElement.cpp
new file mode 100644
index 0000000000..01672eec84
--- /dev/null
+++ b/dom/base/nsStyledElement.cpp
@@ -0,0 +1,223 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsStyledElement.h"
+#include "mozAutoDocUpdate.h"
+#include "nsGkAtoms.h"
+#include "nsAttrValue.h"
+#include "nsAttrValueInlines.h"
+#include "mozilla/dom/BindContext.h"
+#include "mozilla/dom/CustomElementRegistry.h"
+#include "mozilla/dom/ElementInlines.h"
+#include "mozilla/dom/MutationEventBinding.h"
+#include "mozilla/dom/MutationObservers.h"
+#include "mozilla/InternalMutationEvent.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "nsDOMCSSDeclaration.h"
+#include "nsDOMCSSAttrDeclaration.h"
+#include "nsServiceManagerUtils.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/DeclarationBlock.h"
+#include "mozilla/css/Loader.h"
+#include "nsXULElement.h"
+#include "nsContentUtils.h"
+#include "nsStyleUtil.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+// Use the CC variant of this, even though this class does not define
+// a new CC participant, to make QIing to the CC interfaces faster.
+NS_IMPL_QUERY_INTERFACE_CYCLE_COLLECTION_INHERITED(nsStyledElement,
+ nsStyledElementBase,
+ nsStyledElement)
+
+//----------------------------------------------------------------------
+// nsIContent methods
+
+bool nsStyledElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
+ const nsAString& aValue,
+ nsIPrincipal* aMaybeScriptedPrincipal,
+ nsAttrValue& aResult) {
+ if (aAttribute == nsGkAtoms::style && aNamespaceID == kNameSpaceID_None) {
+ ParseStyleAttribute(aValue, aMaybeScriptedPrincipal, aResult, false);
+ return true;
+ }
+
+ return nsStyledElementBase::ParseAttribute(aNamespaceID, aAttribute, aValue,
+ aMaybeScriptedPrincipal, aResult);
+}
+
+nsresult nsStyledElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
+ const nsAttrValueOrString* aValue,
+ bool aNotify) {
+ if (aNamespaceID == kNameSpaceID_None) {
+ if (aName == nsGkAtoms::style) {
+ if (aValue) {
+ SetMayHaveStyle();
+ }
+ }
+ }
+
+ return nsStyledElementBase::BeforeSetAttr(aNamespaceID, aName, aValue,
+ aNotify);
+}
+
+void nsStyledElement::InlineStyleDeclarationWillChange(
+ MutationClosureData& aData) {
+ MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript());
+ MOZ_ASSERT(OwnerDoc()->UpdateNestingLevel() > 0,
+ "Should be inside document update!");
+ bool modification = false;
+ if (MayHaveStyle()) {
+ bool needsOldValue = !StaticPrefs::dom_mutation_events_cssom_disabled() &&
+ nsContentUtils::HasMutationListeners(
+ this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this);
+
+ if (!needsOldValue) {
+ CustomElementDefinition* definition = GetCustomElementDefinition();
+ if (definition &&
+ definition->IsInObservedAttributeList(nsGkAtoms::style)) {
+ needsOldValue = true;
+ }
+ }
+
+ if (needsOldValue) {
+ nsAutoString oldValueStr;
+ modification = GetAttr(kNameSpaceID_None, nsGkAtoms::style, oldValueStr);
+ if (modification) {
+ aData.mOldValue.emplace();
+ aData.mOldValue->SetTo(oldValueStr);
+ }
+ } else {
+ modification = HasAttr(kNameSpaceID_None, nsGkAtoms::style);
+ }
+ }
+
+ aData.mModType =
+ modification ? static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION)
+ : static_cast<uint8_t>(MutationEvent_Binding::ADDITION);
+ MutationObservers::NotifyAttributeWillChange(
+ this, kNameSpaceID_None, nsGkAtoms::style, aData.mModType);
+
+ // XXXsmaug In order to make attribute handling more consistent, consider to
+ // call BeforeSetAttr and pass kCallAfterSetAttr to
+ // SetAttrAndNotify in SetInlineStyleDeclaration.
+ // Handling of mozAutoDocUpdate may require changes in that case.
+}
+
+nsresult nsStyledElement::SetInlineStyleDeclaration(
+ DeclarationBlock& aDeclaration, MutationClosureData& aData) {
+ MOZ_ASSERT(OwnerDoc()->UpdateNestingLevel(),
+ "Should be inside document update!");
+
+ bool hasListeners = !StaticPrefs::dom_mutation_events_cssom_disabled() &&
+ nsContentUtils::HasMutationListeners(
+ this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this);
+
+ nsAttrValue attrValue(do_AddRef(&aDeclaration), nullptr);
+ SetMayHaveStyle();
+
+ Document* document = GetComposedDoc();
+ mozAutoDocUpdate updateBatch(document, true);
+ return SetAttrAndNotify(kNameSpaceID_None, nsGkAtoms::style, nullptr,
+ aData.mOldValue.ptrOr(nullptr), attrValue, nullptr,
+ aData.mModType, hasListeners, true,
+ kDontCallAfterSetAttr, document, updateBatch);
+}
+
+// ---------------------------------------------------------------
+// Others and helpers
+
+nsICSSDeclaration* nsStyledElement::Style() {
+ Element::nsDOMSlots* slots = DOMSlots();
+
+ if (!slots->mStyle) {
+ // Just in case...
+ ReparseStyleAttribute(/* aForceInDataDoc */ true);
+
+ slots->mStyle = new nsDOMCSSAttributeDeclaration(this, false);
+ SetMayHaveStyle();
+ }
+
+ return slots->mStyle;
+}
+
+nsresult nsStyledElement::ReparseStyleAttribute(bool aForceInDataDoc) {
+ if (!MayHaveStyle()) {
+ return NS_OK;
+ }
+ const nsAttrValue* oldVal = mAttrs.GetAttr(nsGkAtoms::style);
+ if (oldVal && oldVal->Type() != nsAttrValue::eCSSDeclaration) {
+ nsAttrValue attrValue;
+ nsAutoString stringValue;
+ oldVal->ToString(stringValue);
+ ParseStyleAttribute(stringValue, nullptr, attrValue, aForceInDataDoc);
+ // Don't bother going through SetInlineStyleDeclaration; we don't
+ // want to fire off mutation events or document notifications anyway
+ bool oldValueSet;
+ nsresult rv =
+ mAttrs.SetAndSwapAttr(nsGkAtoms::style, attrValue, &oldValueSet);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+nsICSSDeclaration* nsStyledElement::GetExistingStyle() {
+ Element::nsDOMSlots* slots = GetExistingDOMSlots();
+ if (!slots) {
+ return nullptr;
+ }
+
+ return slots->mStyle;
+}
+
+void nsStyledElement::ParseStyleAttribute(const nsAString& aValue,
+ nsIPrincipal* aMaybeScriptedPrincipal,
+ nsAttrValue& aResult,
+ bool aForceInDataDoc) {
+ Document* doc = OwnerDoc();
+ bool isNativeAnon = IsInNativeAnonymousSubtree();
+
+ if (!isNativeAnon &&
+ !nsStyleUtil::CSPAllowsInlineStyle(this, doc, aMaybeScriptedPrincipal, 0,
+ 0, aValue, nullptr))
+ return;
+
+ if (aForceInDataDoc || !doc->IsLoadedAsData() || GetExistingStyle() ||
+ doc->IsStaticDocument()) {
+ bool isCSS = true; // assume CSS until proven otherwise
+
+ if (!isNativeAnon) { // native anonymous content always assumes CSS
+ nsAutoString styleType;
+ doc->GetHeaderData(nsGkAtoms::headerContentStyleType, styleType);
+ if (!styleType.IsEmpty()) {
+ isCSS = StringBeginsWith(styleType, u"text/css"_ns,
+ nsASCIICaseInsensitiveStringComparator);
+ }
+ }
+
+ if (isCSS &&
+ aResult.ParseStyleAttribute(aValue, aMaybeScriptedPrincipal, this)) {
+ return;
+ }
+ }
+
+ aResult.SetTo(aValue);
+}
+
+nsresult nsStyledElement::BindToTree(BindContext& aContext, nsINode& aParent) {
+ nsresult rv = nsStyledElementBase::BindToTree(aContext, aParent);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (HasAttr(nsGkAtoms::autofocus) && aContext.AllowsAutoFocus() &&
+ (!IsSVGElement() || IsFocusable())) {
+ aContext.OwnerDoc().SetAutoFocusElement(this);
+ }
+
+ return NS_OK;
+}