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/. */
6
7 /* DOM object for element.style */
8
9 #include "nsDOMCSSAttrDeclaration.h"
10
11 #include "mozilla/dom/Document.h"
12 #include "mozilla/dom/Element.h"
13 #include "mozilla/dom/SVGElement.h"
14 #include "mozilla/dom/MutationEventBinding.h"
15 #include "mozilla/DeclarationBlock.h"
16 #include "mozilla/InternalMutationEvent.h"
17 #include "mozilla/SMILCSSValueType.h"
18 #include "mozilla/SMILValue.h"
19 #include "mozAutoDocUpdate.h"
20 #include "nsWrapperCacheInlines.h"
21 #include "nsIFrame.h"
22 #include "ActiveLayerTracker.h"
23
24 using namespace mozilla;
25 using namespace mozilla::dom;
26
nsDOMCSSAttributeDeclaration(Element * aElement,bool aIsSMILOverride)27 nsDOMCSSAttributeDeclaration::nsDOMCSSAttributeDeclaration(Element* aElement,
28 bool aIsSMILOverride)
29 : mElement(aElement), mIsSMILOverride(aIsSMILOverride) {
30 NS_ASSERTION(aElement, "Inline style for a NULL element?");
31 }
32
33 nsDOMCSSAttributeDeclaration::~nsDOMCSSAttributeDeclaration() = default;
34
35 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsDOMCSSAttributeDeclaration, mElement)
36
37 // mElement holds a strong ref to us, so if it's going to be
38 // skipped, the attribute declaration can't be part of a garbage
39 // cycle.
40 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDOMCSSAttributeDeclaration)
41 if (tmp->mElement && Element::CanSkip(tmp->mElement, true)) {
42 if (tmp->PreservingWrapper()) {
43 tmp->MarkWrapperLive();
44 }
45 return true;
46 }
47 return tmp->HasKnownLiveWrapper();
48 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
49
50 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsDOMCSSAttributeDeclaration)
51 return tmp->HasKnownLiveWrapper() ||
52 (tmp->mElement && Element::CanSkipInCC(tmp->mElement));
53 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
54
55 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsDOMCSSAttributeDeclaration)
56 return tmp->HasKnownLiveWrapper() ||
57 (tmp->mElement && Element::CanSkipThis(tmp->mElement));
58 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
59
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMCSSAttributeDeclaration)60 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMCSSAttributeDeclaration)
61 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
62 NS_INTERFACE_MAP_END_INHERITING(nsDOMCSSDeclaration)
63
64 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMCSSAttributeDeclaration)
65 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMCSSAttributeDeclaration)
66
67 nsresult nsDOMCSSAttributeDeclaration::SetCSSDeclaration(
68 DeclarationBlock* aDecl, MutationClosureData* aClosureData) {
69 NS_ASSERTION(mElement, "Must have Element to set the declaration!");
70
71 // Whenever changing element.style values, aClosureData must be non-null.
72 // SMIL doesn't update Element's attribute values, so closure data isn't
73 // needed.
74 MOZ_ASSERT_IF(!mIsSMILOverride, aClosureData);
75
76 // The closure needs to have been called by now, otherwise we shouldn't be
77 // getting here when the attribute hasn't changed.
78 MOZ_ASSERT_IF(aClosureData && aClosureData->mShouldBeCalled,
79 aClosureData->mWasCalled);
80
81 aDecl->SetDirty();
82 if (mIsSMILOverride) {
83 mElement->SetSMILOverrideStyleDeclaration(*aDecl);
84 return NS_OK;
85 }
86 return mElement->SetInlineStyleDeclaration(*aDecl, *aClosureData);
87 }
88
DocToUpdate()89 Document* nsDOMCSSAttributeDeclaration::DocToUpdate() {
90 // We need OwnerDoc() rather than GetUncomposedDoc() because it might
91 // be the BeginUpdate call that inserts mElement into the document.
92 return mElement->OwnerDoc();
93 }
94
GetOrCreateCSSDeclaration(Operation aOperation,DeclarationBlock ** aCreated)95 DeclarationBlock* nsDOMCSSAttributeDeclaration::GetOrCreateCSSDeclaration(
96 Operation aOperation, DeclarationBlock** aCreated) {
97 MOZ_ASSERT(aOperation != Operation::Modify || aCreated);
98
99 if (!mElement) return nullptr;
100
101 DeclarationBlock* declaration;
102 if (mIsSMILOverride) {
103 declaration = mElement->GetSMILOverrideStyleDeclaration();
104 } else {
105 declaration = mElement->GetInlineStyleDeclaration();
106 }
107
108 if (declaration) {
109 return declaration;
110 }
111
112 if (aOperation != Operation::Modify) {
113 return nullptr;
114 }
115
116 // cannot fail
117 RefPtr<DeclarationBlock> decl = new DeclarationBlock();
118 // Mark the declaration dirty so that it can be reused by the caller.
119 // Normally SetDirty is called later in SetCSSDeclaration.
120 decl->SetDirty();
121 #ifdef DEBUG
122 RefPtr<DeclarationBlock> mutableDecl = decl->EnsureMutable();
123 MOZ_ASSERT(mutableDecl == decl);
124 #endif
125 decl.swap(*aCreated);
126 return *aCreated;
127 }
128
129 nsDOMCSSDeclaration::ParsingEnvironment
GetParsingEnvironment(nsIPrincipal * aSubjectPrincipal) const130 nsDOMCSSAttributeDeclaration::GetParsingEnvironment(
131 nsIPrincipal* aSubjectPrincipal) const {
132 return {
133 mElement->GetURLDataForStyleAttr(aSubjectPrincipal),
134 mElement->OwnerDoc()->GetCompatibilityMode(),
135 mElement->OwnerDoc()->CSSLoader(),
136 };
137 }
138
139 template <typename SetterFunc>
SetSMILValueHelper(SetterFunc aFunc)140 nsresult nsDOMCSSAttributeDeclaration::SetSMILValueHelper(SetterFunc aFunc) {
141 MOZ_ASSERT(mIsSMILOverride);
142
143 // No need to do the ActiveLayerTracker / ScrollLinkedEffectDetector bits,
144 // since we're in a SMIL animation anyway, no need to try to detect we're a
145 // scripted animation.
146 RefPtr<DeclarationBlock> created;
147 DeclarationBlock* olddecl =
148 GetOrCreateCSSDeclaration(Operation::Modify, getter_AddRefs(created));
149 if (!olddecl) {
150 return NS_ERROR_NOT_AVAILABLE;
151 }
152 mozAutoDocUpdate autoUpdate(DocToUpdate(), true);
153 RefPtr<DeclarationBlock> decl = olddecl->EnsureMutable();
154
155 bool changed = aFunc(*decl);
156
157 if (changed) {
158 // We can pass nullptr as the latter param, since this is
159 // mIsSMILOverride == true case.
160 SetCSSDeclaration(decl, nullptr);
161 }
162 return NS_OK;
163 }
164
SetSMILValue(const nsCSSPropertyID,const SMILValue & aValue)165 nsresult nsDOMCSSAttributeDeclaration::SetSMILValue(
166 const nsCSSPropertyID /*aPropID*/, const SMILValue& aValue) {
167 MOZ_ASSERT(aValue.mType == &SMILCSSValueType::sSingleton,
168 "We should only try setting a CSS value type");
169 return SetSMILValueHelper([&aValue](DeclarationBlock& aDecl) {
170 return SMILCSSValueType::SetPropertyValues(aValue, aDecl);
171 });
172 }
173
SetSMILValue(const nsCSSPropertyID aPropID,const SVGAnimatedLength & aLength)174 nsresult nsDOMCSSAttributeDeclaration::SetSMILValue(
175 const nsCSSPropertyID aPropID, const SVGAnimatedLength& aLength) {
176 return SetSMILValueHelper([aPropID, &aLength](DeclarationBlock& aDecl) {
177 return SVGElement::UpdateDeclarationBlockFromLength(
178 aDecl, aPropID, aLength, SVGElement::ValToUse::Anim);
179 });
180 }
181
SetSMILValue(const nsCSSPropertyID,const SVGAnimatedPathSegList & aPath)182 nsresult nsDOMCSSAttributeDeclaration::SetSMILValue(
183 const nsCSSPropertyID /*aPropID*/, const SVGAnimatedPathSegList& aPath) {
184 return SetSMILValueHelper([&aPath](DeclarationBlock& aDecl) {
185 return SVGElement::UpdateDeclarationBlockFromPath(
186 aDecl, aPath, SVGElement::ValToUse::Anim);
187 });
188 }
189
SetPropertyValue(const nsCSSPropertyID aPropID,const nsACString & aValue,nsIPrincipal * aSubjectPrincipal,ErrorResult & aRv)190 void nsDOMCSSAttributeDeclaration::SetPropertyValue(
191 const nsCSSPropertyID aPropID, const nsACString& aValue,
192 nsIPrincipal* aSubjectPrincipal, ErrorResult& aRv) {
193 // Scripted modifications to style.opacity or style.transform (or other
194 // transform-like properties, e.g. style.translate, style.rotate, style.scale)
195 // could immediately force us into the animated state if heuristics suggest
196 // this is scripted animation.
197 // FIXME: This is missing the margin shorthand and the logical versions of
198 // the margin properties, see bug 1266287.
199 if (aPropID == eCSSProperty_opacity || aPropID == eCSSProperty_transform ||
200 aPropID == eCSSProperty_translate || aPropID == eCSSProperty_rotate ||
201 aPropID == eCSSProperty_scale || aPropID == eCSSProperty_offset_path ||
202 aPropID == eCSSProperty_offset_distance ||
203 aPropID == eCSSProperty_offset_rotate ||
204 aPropID == eCSSProperty_offset_anchor) {
205 nsIFrame* frame = mElement->GetPrimaryFrame();
206 if (frame) {
207 ActiveLayerTracker::NotifyInlineStyleRuleModified(frame, aPropID, aValue,
208 this);
209 }
210 }
211 nsDOMCSSDeclaration::SetPropertyValue(aPropID, aValue, aSubjectPrincipal,
212 aRv);
213 }
214
MutationClosureFunction(void * aData)215 void nsDOMCSSAttributeDeclaration::MutationClosureFunction(void* aData) {
216 auto* data = static_cast<MutationClosureData*>(aData);
217 MOZ_ASSERT(
218 data->mShouldBeCalled,
219 "Did we pass a non-null closure to the style system unnecessarily?");
220 if (data->mWasCalled) {
221 return;
222 }
223 data->mWasCalled = true;
224 data->mElement->InlineStyleDeclarationWillChange(*data);
225 }
226