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"
23 #include "ActiveLayerTracker.h"
25 using namespace mozilla
;
26 using namespace mozilla::dom
;
28 nsDOMCSSAttributeDeclaration::nsDOMCSSAttributeDeclaration(Element
* aElement
,
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
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();
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
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
);
83 if (mIsSMILOverride
) {
84 mElement
->SetSMILOverrideStyleDeclaration(*aDecl
);
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();
106 declaration
= mElement
->GetInlineStyleDeclaration();
113 if (aOperation
!= Operation::Modify
) {
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.
123 RefPtr
<DeclarationBlock
> mutableDecl
= decl
->EnsureMutable();
124 MOZ_ASSERT(mutableDecl
== decl
);
126 decl
.swap(*aCreated
);
130 nsDOMCSSDeclaration::ParsingEnvironment
131 nsDOMCSSAttributeDeclaration::GetParsingEnvironment(
132 nsIPrincipal
* aSubjectPrincipal
) const {
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
));
151 return NS_ERROR_NOT_AVAILABLE
;
153 mozAutoDocUpdate
autoUpdate(DocToUpdate(), true);
154 RefPtr
<DeclarationBlock
> decl
= olddecl
->EnsureMutable();
156 bool changed
= aFunc(*decl
);
159 // We can pass nullptr as the latter param, since this is
160 // mIsSMILOverride == true case.
161 SetCSSDeclaration(decl
, nullptr);
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
) {
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
:
218 void nsDOMCSSAttributeDeclaration::SetPropertyValue(
219 const nsCSSPropertyID aPropID
, const nsACString
& aValue
,
220 nsIPrincipal
* aSubjectPrincipal
, ErrorResult
& aRv
) {
221 nsDOMCSSDeclaration::SetPropertyValue(aPropID
, aValue
, aSubjectPrincipal
,
225 static bool IsScrollLinkedEffectiveProperty(const nsCSSPropertyID 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
:
258 void nsDOMCSSAttributeDeclaration::MutationClosureFunction(
259 void* aData
, nsCSSPropertyID aPropID
) {
260 auto* data
= static_cast<MutationClosureData
*>(aData
);
262 data
->mShouldBeCalled
,
263 "Did we pass a non-null closure to the style system unnecessarily?");
264 if (data
->mWasCalled
) {
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
);