Bug 1883861 - Part 1: Move visitMemoryBarrier into the common CodeGenerator file...
[gecko.git] / layout / style / nsDOMCSSAttrDeclaration.cpp
blob1381b22d6c65355d8e33abd1f6932e78c4d659aa
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 /* DOM object for element.style */
9 #include "nsDOMCSSAttrDeclaration.h"
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/layers/ScrollLinkedEffectDetector.h"
16 #include "mozilla/DeclarationBlock.h"
17 #include "mozilla/InternalMutationEvent.h"
18 #include "mozilla/SMILCSSValueType.h"
19 #include "mozilla/SMILValue.h"
20 #include "mozAutoDocUpdate.h"
21 #include "nsWrapperCacheInlines.h"
22 #include "nsIFrame.h"
23 #include "ActiveLayerTracker.h"
25 using namespace mozilla;
26 using namespace mozilla::dom;
28 nsDOMCSSAttributeDeclaration::nsDOMCSSAttributeDeclaration(Element* aElement,
29 bool aIsSMILOverride)
30 : mElement(aElement), mIsSMILOverride(aIsSMILOverride) {
31 NS_ASSERTION(aElement, "Inline style for a NULL element?");
34 nsDOMCSSAttributeDeclaration::~nsDOMCSSAttributeDeclaration() = default;
36 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsDOMCSSAttributeDeclaration, mElement)
38 // mElement holds a strong ref to us, so if it's going to be
39 // skipped, the attribute declaration can't be part of a garbage
40 // cycle.
41 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDOMCSSAttributeDeclaration)
42 if (tmp->mElement && Element::CanSkip(tmp->mElement, true)) {
43 if (tmp->PreservingWrapper()) {
44 tmp->MarkWrapperLive();
46 return true;
48 return tmp->HasKnownLiveWrapper();
49 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
51 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsDOMCSSAttributeDeclaration)
52 return tmp->HasKnownLiveWrapper() ||
53 (tmp->mElement && Element::CanSkipInCC(tmp->mElement));
54 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
56 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsDOMCSSAttributeDeclaration)
57 return tmp->HasKnownLiveWrapper() ||
58 (tmp->mElement && Element::CanSkipThis(tmp->mElement));
59 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
61 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMCSSAttributeDeclaration)
62 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
63 NS_INTERFACE_MAP_END_INHERITING(nsDOMCSSDeclaration)
65 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMCSSAttributeDeclaration)
66 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMCSSAttributeDeclaration)
68 nsresult nsDOMCSSAttributeDeclaration::SetCSSDeclaration(
69 DeclarationBlock* aDecl, MutationClosureData* aClosureData) {
70 NS_ASSERTION(mElement, "Must have Element to set the declaration!");
72 // Whenever changing element.style values, aClosureData must be non-null.
73 // SMIL doesn't update Element's attribute values, so closure data isn't
74 // needed.
75 MOZ_ASSERT_IF(!mIsSMILOverride, aClosureData);
77 // The closure needs to have been called by now, otherwise we shouldn't be
78 // getting here when the attribute hasn't changed.
79 MOZ_ASSERT_IF(aClosureData && aClosureData->mShouldBeCalled,
80 aClosureData->mWasCalled);
82 aDecl->SetDirty();
83 if (mIsSMILOverride) {
84 mElement->SetSMILOverrideStyleDeclaration(*aDecl);
85 return NS_OK;
87 return mElement->SetInlineStyleDeclaration(*aDecl, *aClosureData);
90 Document* nsDOMCSSAttributeDeclaration::DocToUpdate() {
91 // We need OwnerDoc() rather than GetUncomposedDoc() because it might
92 // be the BeginUpdate call that inserts mElement into the document.
93 return mElement->OwnerDoc();
96 DeclarationBlock* nsDOMCSSAttributeDeclaration::GetOrCreateCSSDeclaration(
97 Operation aOperation, DeclarationBlock** aCreated) {
98 MOZ_ASSERT(aOperation != Operation::Modify || aCreated);
100 if (!mElement) return nullptr;
102 DeclarationBlock* declaration;
103 if (mIsSMILOverride) {
104 declaration = mElement->GetSMILOverrideStyleDeclaration();
105 } else {
106 declaration = mElement->GetInlineStyleDeclaration();
109 if (declaration) {
110 return declaration;
113 if (aOperation != Operation::Modify) {
114 return nullptr;
117 // cannot fail
118 RefPtr<DeclarationBlock> decl = new DeclarationBlock();
119 // Mark the declaration dirty so that it can be reused by the caller.
120 // Normally SetDirty is called later in SetCSSDeclaration.
121 decl->SetDirty();
122 #ifdef DEBUG
123 RefPtr<DeclarationBlock> mutableDecl = decl->EnsureMutable();
124 MOZ_ASSERT(mutableDecl == decl);
125 #endif
126 decl.swap(*aCreated);
127 return *aCreated;
130 nsDOMCSSDeclaration::ParsingEnvironment
131 nsDOMCSSAttributeDeclaration::GetParsingEnvironment(
132 nsIPrincipal* aSubjectPrincipal) const {
133 return {
134 mElement->GetURLDataForStyleAttr(aSubjectPrincipal),
135 mElement->OwnerDoc()->GetCompatibilityMode(),
136 mElement->OwnerDoc()->CSSLoader(),
140 template <typename SetterFunc>
141 nsresult nsDOMCSSAttributeDeclaration::SetSMILValueHelper(SetterFunc aFunc) {
142 MOZ_ASSERT(mIsSMILOverride);
144 // No need to do the ActiveLayerTracker / ScrollLinkedEffectDetector bits,
145 // since we're in a SMIL animation anyway, no need to try to detect we're a
146 // scripted animation.
147 RefPtr<DeclarationBlock> created;
148 DeclarationBlock* olddecl =
149 GetOrCreateCSSDeclaration(Operation::Modify, getter_AddRefs(created));
150 if (!olddecl) {
151 return NS_ERROR_NOT_AVAILABLE;
153 mozAutoDocUpdate autoUpdate(DocToUpdate(), true);
154 RefPtr<DeclarationBlock> decl = olddecl->EnsureMutable();
156 bool changed = aFunc(*decl);
158 if (changed) {
159 // We can pass nullptr as the latter param, since this is
160 // mIsSMILOverride == true case.
161 SetCSSDeclaration(decl, nullptr);
163 return NS_OK;
166 nsresult nsDOMCSSAttributeDeclaration::SetSMILValue(
167 const nsCSSPropertyID /*aPropID*/, const SMILValue& aValue) {
168 MOZ_ASSERT(aValue.mType == &SMILCSSValueType::sSingleton,
169 "We should only try setting a CSS value type");
170 return SetSMILValueHelper([&aValue](DeclarationBlock& aDecl) {
171 return SMILCSSValueType::SetPropertyValues(aValue, aDecl);
175 nsresult nsDOMCSSAttributeDeclaration::SetSMILValue(
176 const nsCSSPropertyID aPropID, const SVGAnimatedLength& aLength) {
177 return SetSMILValueHelper([aPropID, &aLength](DeclarationBlock& aDecl) {
178 MOZ_ASSERT(aDecl.IsMutable());
179 return SVGElement::UpdateDeclarationBlockFromLength(
180 *aDecl.Raw(), aPropID, aLength, SVGElement::ValToUse::Anim);
184 nsresult nsDOMCSSAttributeDeclaration::SetSMILValue(
185 const nsCSSPropertyID /*aPropID*/, const SVGAnimatedPathSegList& aPath) {
186 return SetSMILValueHelper([&aPath](DeclarationBlock& aDecl) {
187 MOZ_ASSERT(aDecl.IsMutable());
188 return SVGElement::UpdateDeclarationBlockFromPath(
189 *aDecl.Raw(), aPath, SVGElement::ValToUse::Anim);
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 a scripted animation.
198 // FIXME: This is missing the margin shorthand and the logical versions of
199 // the margin properties, see bug 1266287.
200 static bool IsActiveLayerProperty(nsCSSPropertyID aPropID) {
201 switch (aPropID) {
202 case eCSSProperty_opacity:
203 case eCSSProperty_transform:
204 case eCSSProperty_translate:
205 case eCSSProperty_rotate:
206 case eCSSProperty_scale:
207 case eCSSProperty_offset_path:
208 case eCSSProperty_offset_distance:
209 case eCSSProperty_offset_rotate:
210 case eCSSProperty_offset_anchor:
211 case eCSSProperty_offset_position:
212 return true;
213 default:
214 return false;
218 void nsDOMCSSAttributeDeclaration::SetPropertyValue(
219 const nsCSSPropertyID aPropID, const nsACString& aValue,
220 nsIPrincipal* aSubjectPrincipal, ErrorResult& aRv) {
221 nsDOMCSSDeclaration::SetPropertyValue(aPropID, aValue, aSubjectPrincipal,
222 aRv);
225 static bool IsScrollLinkedEffectiveProperty(const nsCSSPropertyID aPropID) {
226 switch (aPropID) {
227 case eCSSProperty_background_position:
228 case eCSSProperty_background_position_x:
229 case eCSSProperty_background_position_y:
230 case eCSSProperty_transform:
231 case eCSSProperty_translate:
232 case eCSSProperty_rotate:
233 case eCSSProperty_scale:
234 case eCSSProperty_offset_path:
235 case eCSSProperty_offset_distance:
236 case eCSSProperty_offset_rotate:
237 case eCSSProperty_offset_anchor:
238 case eCSSProperty_offset_position:
239 case eCSSProperty_top:
240 case eCSSProperty_left:
241 case eCSSProperty_bottom:
242 case eCSSProperty_right:
243 case eCSSProperty_margin:
244 case eCSSProperty_margin_top:
245 case eCSSProperty_margin_left:
246 case eCSSProperty_margin_bottom:
247 case eCSSProperty_margin_right:
248 case eCSSProperty_margin_inline_start:
249 case eCSSProperty_margin_inline_end:
250 case eCSSProperty_margin_block_start:
251 case eCSSProperty_margin_block_end:
252 return true;
253 default:
254 return false;
258 void nsDOMCSSAttributeDeclaration::MutationClosureFunction(
259 void* aData, nsCSSPropertyID aPropID) {
260 auto* data = static_cast<MutationClosureData*>(aData);
261 MOZ_ASSERT(
262 data->mShouldBeCalled,
263 "Did we pass a non-null closure to the style system unnecessarily?");
264 if (data->mWasCalled) {
265 return;
267 if (IsScrollLinkedEffectiveProperty(aPropID)) {
268 mozilla::layers::ScrollLinkedEffectDetector::PositioningPropertyMutated();
270 if (IsActiveLayerProperty(aPropID)) {
271 if (nsIFrame* frame = data->mElement->GetPrimaryFrame()) {
272 ActiveLayerTracker::NotifyInlineStyleRuleModified(frame, aPropID);
276 data->mWasCalled = true;
277 data->mElement->InlineStyleDeclarationWillChange(*data);