1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsStyledElement.h"
8 #include "mozAutoDocUpdate.h"
10 #include "nsAttrValue.h"
11 #include "nsAttrValueInlines.h"
12 #include "mozilla/dom/BindContext.h"
13 #include "mozilla/dom/CustomElementRegistry.h"
14 #include "mozilla/dom/ElementInlines.h"
15 #include "mozilla/dom/MutationEventBinding.h"
16 #include "mozilla/dom/MutationObservers.h"
17 #include "mozilla/InternalMutationEvent.h"
18 #include "mozilla/StaticPrefs_dom.h"
19 #include "nsDOMCSSDeclaration.h"
20 #include "nsDOMCSSAttrDeclaration.h"
21 #include "nsServiceManagerUtils.h"
22 #include "mozilla/dom/Document.h"
23 #include "mozilla/DeclarationBlock.h"
24 #include "mozilla/css/Loader.h"
25 #include "nsXULElement.h"
26 #include "nsContentUtils.h"
27 #include "nsStyleUtil.h"
29 using namespace mozilla
;
30 using namespace mozilla::dom
;
32 // Use the CC variant of this, even though this class does not define
33 // a new CC participant, to make QIing to the CC interfaces faster.
34 NS_IMPL_QUERY_INTERFACE_CYCLE_COLLECTION_INHERITED(nsStyledElement
,
38 //----------------------------------------------------------------------
41 bool nsStyledElement::ParseAttribute(int32_t aNamespaceID
, nsAtom
* aAttribute
,
42 const nsAString
& aValue
,
43 nsIPrincipal
* aMaybeScriptedPrincipal
,
44 nsAttrValue
& aResult
) {
45 if (aAttribute
== nsGkAtoms::style
&& aNamespaceID
== kNameSpaceID_None
) {
46 ParseStyleAttribute(aValue
, aMaybeScriptedPrincipal
, aResult
, false);
50 return nsStyledElementBase::ParseAttribute(aNamespaceID
, aAttribute
, aValue
,
51 aMaybeScriptedPrincipal
, aResult
);
54 void nsStyledElement::BeforeSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
55 const nsAttrValue
* aValue
, bool aNotify
) {
56 if (aNamespaceID
== kNameSpaceID_None
&& aName
== nsGkAtoms::style
&&
61 return nsStyledElementBase::BeforeSetAttr(aNamespaceID
, aName
, aValue
,
65 void nsStyledElement::InlineStyleDeclarationWillChange(
66 MutationClosureData
& aData
) {
67 MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript());
68 MOZ_ASSERT(OwnerDoc()->UpdateNestingLevel() > 0,
69 "Should be inside document update!");
70 bool modification
= false;
72 bool needsOldValue
= !StaticPrefs::dom_mutation_events_cssom_disabled() &&
73 nsContentUtils::HasMutationListeners(
74 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED
, this);
77 CustomElementDefinition
* definition
= GetCustomElementDefinition();
79 definition
->IsInObservedAttributeList(nsGkAtoms::style
)) {
85 nsAutoString oldValueStr
;
86 modification
= GetAttr(nsGkAtoms::style
, oldValueStr
);
88 aData
.mOldValue
.emplace();
89 aData
.mOldValue
->SetTo(oldValueStr
);
92 modification
= HasAttr(nsGkAtoms::style
);
97 modification
? static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION
)
98 : static_cast<uint8_t>(MutationEvent_Binding::ADDITION
);
99 MutationObservers::NotifyAttributeWillChange(
100 this, kNameSpaceID_None
, nsGkAtoms::style
, aData
.mModType
);
102 // XXXsmaug In order to make attribute handling more consistent, consider to
103 // call BeforeSetAttr and pass kCallAfterSetAttr to
104 // SetAttrAndNotify in SetInlineStyleDeclaration.
105 // Handling of mozAutoDocUpdate may require changes in that case.
108 nsresult
nsStyledElement::SetInlineStyleDeclaration(
109 DeclarationBlock
& aDeclaration
, MutationClosureData
& aData
) {
110 MOZ_ASSERT(OwnerDoc()->UpdateNestingLevel(),
111 "Should be inside document update!");
113 bool hasListeners
= !StaticPrefs::dom_mutation_events_cssom_disabled() &&
114 nsContentUtils::HasMutationListeners(
115 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED
, this);
117 nsAttrValue
attrValue(do_AddRef(&aDeclaration
), nullptr);
120 Document
* document
= GetComposedDoc();
121 mozAutoDocUpdate
updateBatch(document
, true);
122 return SetAttrAndNotify(kNameSpaceID_None
, nsGkAtoms::style
, nullptr,
123 aData
.mOldValue
.ptrOr(nullptr), attrValue
, nullptr,
124 aData
.mModType
, hasListeners
, true,
125 kDontCallAfterSetAttr
, document
, updateBatch
);
128 // ---------------------------------------------------------------
129 // Others and helpers
131 nsICSSDeclaration
* nsStyledElement::Style() {
132 Element::nsDOMSlots
* slots
= DOMSlots();
134 if (!slots
->mStyle
) {
136 ReparseStyleAttribute(/* aForceInDataDoc */ true);
138 slots
->mStyle
= new nsDOMCSSAttributeDeclaration(this, false);
142 return slots
->mStyle
;
145 nsresult
nsStyledElement::ReparseStyleAttribute(bool aForceInDataDoc
) {
146 if (!MayHaveStyle()) {
149 const nsAttrValue
* oldVal
= mAttrs
.GetAttr(nsGkAtoms::style
);
150 if (oldVal
&& oldVal
->Type() != nsAttrValue::eCSSDeclaration
) {
151 nsAttrValue attrValue
;
152 nsAutoString stringValue
;
153 oldVal
->ToString(stringValue
);
154 ParseStyleAttribute(stringValue
, nullptr, attrValue
, aForceInDataDoc
);
155 // Don't bother going through SetInlineStyleDeclaration; we don't
156 // want to fire off mutation events or document notifications anyway
159 mAttrs
.SetAndSwapAttr(nsGkAtoms::style
, attrValue
, &oldValueSet
);
160 NS_ENSURE_SUCCESS(rv
, rv
);
166 nsICSSDeclaration
* nsStyledElement::GetExistingStyle() {
167 Element::nsDOMSlots
* slots
= GetExistingDOMSlots();
172 return slots
->mStyle
;
175 void nsStyledElement::ParseStyleAttribute(const nsAString
& aValue
,
176 nsIPrincipal
* aMaybeScriptedPrincipal
,
177 nsAttrValue
& aResult
,
178 bool aForceInDataDoc
) {
179 Document
* doc
= OwnerDoc();
180 bool isNativeAnon
= IsInNativeAnonymousSubtree();
183 !nsStyleUtil::CSPAllowsInlineStyle(this, doc
, aMaybeScriptedPrincipal
, 0,
187 if (aForceInDataDoc
|| !doc
->IsLoadedAsData() || GetExistingStyle() ||
188 doc
->IsStaticDocument()) {
189 bool isCSS
= true; // assume CSS until proven otherwise
191 if (!isNativeAnon
) { // native anonymous content always assumes CSS
192 nsAutoString styleType
;
193 doc
->GetHeaderData(nsGkAtoms::headerContentStyleType
, styleType
);
194 if (!styleType
.IsEmpty()) {
195 isCSS
= StringBeginsWith(styleType
, u
"text/css"_ns
,
196 nsASCIICaseInsensitiveStringComparator
);
201 aResult
.ParseStyleAttribute(aValue
, aMaybeScriptedPrincipal
, this)) {
206 aResult
.SetTo(aValue
);
209 nsresult
nsStyledElement::BindToTree(BindContext
& aContext
, nsINode
& aParent
) {
210 nsresult rv
= nsStyledElementBase::BindToTree(aContext
, aParent
);
211 NS_ENSURE_SUCCESS(rv
, rv
);
213 if (HasAttr(nsGkAtoms::autofocus
) && aContext
.AllowsAutoFocus() &&
214 (!IsSVGElement() || IsFocusableWithoutStyle())) {
215 aContext
.OwnerDoc().ElementWithAutoFocusInserted(this);