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/CustomElementRegistry.h"
13 #include "mozilla/dom/ElementInlines.h"
14 #include "mozilla/dom/MutationEventBinding.h"
15 #include "mozilla/dom/MutationObservers.h"
16 #include "mozilla/InternalMutationEvent.h"
17 #include "mozilla/StaticPrefs_dom.h"
18 #include "nsDOMCSSDeclaration.h"
19 #include "nsDOMCSSAttrDeclaration.h"
20 #include "nsServiceManagerUtils.h"
21 #include "mozilla/dom/Document.h"
22 #include "mozilla/DeclarationBlock.h"
23 #include "mozilla/css/Loader.h"
24 #include "nsXULElement.h"
25 #include "nsContentUtils.h"
26 #include "nsStyleUtil.h"
28 using namespace mozilla
;
29 using namespace mozilla::dom
;
31 // Use the CC variant of this, even though this class does not define
32 // a new CC participant, to make QIing to the CC interfaces faster.
33 NS_IMPL_QUERY_INTERFACE_CYCLE_COLLECTION_INHERITED(nsStyledElement
,
37 //----------------------------------------------------------------------
40 bool nsStyledElement::ParseAttribute(int32_t aNamespaceID
, nsAtom
* aAttribute
,
41 const nsAString
& aValue
,
42 nsIPrincipal
* aMaybeScriptedPrincipal
,
43 nsAttrValue
& aResult
) {
44 if (aAttribute
== nsGkAtoms::style
&& aNamespaceID
== kNameSpaceID_None
) {
45 ParseStyleAttribute(aValue
, aMaybeScriptedPrincipal
, aResult
, false);
49 return nsStyledElementBase::ParseAttribute(aNamespaceID
, aAttribute
, aValue
,
50 aMaybeScriptedPrincipal
, aResult
);
53 nsresult
nsStyledElement::BeforeSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
54 const nsAttrValueOrString
* aValue
,
56 if (aNamespaceID
== kNameSpaceID_None
) {
57 if (aName
== nsGkAtoms::style
) {
64 return nsStyledElementBase::BeforeSetAttr(aNamespaceID
, aName
, aValue
,
68 void nsStyledElement::InlineStyleDeclarationWillChange(
69 MutationClosureData
& aData
) {
70 MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript());
71 MOZ_ASSERT(OwnerDoc()->UpdateNestingLevel() > 0,
72 "Should be inside document update!");
73 bool modification
= false;
75 bool needsOldValue
= !StaticPrefs::dom_mutation_events_cssom_disabled() &&
76 nsContentUtils::HasMutationListeners(
77 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED
, this);
80 CustomElementDefinition
* definition
= GetCustomElementDefinition();
82 definition
->IsInObservedAttributeList(nsGkAtoms::style
)) {
88 nsAutoString oldValueStr
;
89 modification
= GetAttr(kNameSpaceID_None
, nsGkAtoms::style
, oldValueStr
);
91 aData
.mOldValue
.emplace();
92 aData
.mOldValue
->SetTo(oldValueStr
);
95 modification
= HasAttr(kNameSpaceID_None
, nsGkAtoms::style
);
100 modification
? static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION
)
101 : static_cast<uint8_t>(MutationEvent_Binding::ADDITION
);
102 MutationObservers::NotifyAttributeWillChange(
103 this, kNameSpaceID_None
, nsGkAtoms::style
, aData
.mModType
);
105 // XXXsmaug In order to make attribute handling more consistent, consider to
106 // call BeforeSetAttr and pass kCallAfterSetAttr to
107 // SetAttrAndNotify in SetInlineStyleDeclaration.
108 // Handling of mozAutoDocUpdate may require changes in that case.
111 nsresult
nsStyledElement::SetInlineStyleDeclaration(
112 DeclarationBlock
& aDeclaration
, MutationClosureData
& aData
) {
113 MOZ_ASSERT(OwnerDoc()->UpdateNestingLevel(),
114 "Should be inside document update!");
116 bool hasListeners
= !StaticPrefs::dom_mutation_events_cssom_disabled() &&
117 nsContentUtils::HasMutationListeners(
118 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED
, this);
120 nsAttrValue
attrValue(do_AddRef(&aDeclaration
), nullptr);
123 Document
* document
= GetComposedDoc();
124 mozAutoDocUpdate
updateBatch(document
, true);
125 return SetAttrAndNotify(kNameSpaceID_None
, nsGkAtoms::style
, nullptr,
126 aData
.mOldValue
.ptrOr(nullptr), attrValue
, nullptr,
127 aData
.mModType
, hasListeners
, true,
128 kDontCallAfterSetAttr
, document
, updateBatch
);
131 // ---------------------------------------------------------------
132 // Others and helpers
134 nsICSSDeclaration
* nsStyledElement::Style() {
135 Element::nsDOMSlots
* slots
= DOMSlots();
137 if (!slots
->mStyle
) {
139 ReparseStyleAttribute(/* aForceInDataDoc */ true);
141 slots
->mStyle
= new nsDOMCSSAttributeDeclaration(this, false);
145 return slots
->mStyle
;
148 nsresult
nsStyledElement::ReparseStyleAttribute(bool aForceInDataDoc
) {
149 if (!MayHaveStyle()) {
152 const nsAttrValue
* oldVal
= mAttrs
.GetAttr(nsGkAtoms::style
);
153 if (oldVal
&& oldVal
->Type() != nsAttrValue::eCSSDeclaration
) {
154 nsAttrValue attrValue
;
155 nsAutoString stringValue
;
156 oldVal
->ToString(stringValue
);
157 ParseStyleAttribute(stringValue
, nullptr, attrValue
, aForceInDataDoc
);
158 // Don't bother going through SetInlineStyleDeclaration; we don't
159 // want to fire off mutation events or document notifications anyway
162 mAttrs
.SetAndSwapAttr(nsGkAtoms::style
, attrValue
, &oldValueSet
);
163 NS_ENSURE_SUCCESS(rv
, rv
);
169 nsICSSDeclaration
* nsStyledElement::GetExistingStyle() {
170 Element::nsDOMSlots
* slots
= GetExistingDOMSlots();
175 return slots
->mStyle
;
178 void nsStyledElement::ParseStyleAttribute(const nsAString
& aValue
,
179 nsIPrincipal
* aMaybeScriptedPrincipal
,
180 nsAttrValue
& aResult
,
181 bool aForceInDataDoc
) {
182 Document
* doc
= OwnerDoc();
183 bool isNativeAnon
= IsInNativeAnonymousSubtree();
186 !nsStyleUtil::CSPAllowsInlineStyle(this, doc
, aMaybeScriptedPrincipal
, 0,
190 if (aForceInDataDoc
|| !doc
->IsLoadedAsData() || GetExistingStyle() ||
191 doc
->IsStaticDocument()) {
192 bool isCSS
= true; // assume CSS until proven otherwise
194 if (!isNativeAnon
) { // native anonymous content always assumes CSS
195 nsAutoString styleType
;
196 doc
->GetHeaderData(nsGkAtoms::headerContentStyleType
, styleType
);
197 if (!styleType
.IsEmpty()) {
198 static const char textCssStr
[] = "text/css";
200 (styleType
.EqualsIgnoreCase(textCssStr
, sizeof(textCssStr
) - 1));
205 aResult
.ParseStyleAttribute(aValue
, aMaybeScriptedPrincipal
, this)) {
210 aResult
.SetTo(aValue
);