Bug 1523562 [wpt PR 15079] - Pass the full path to the flake8 config files, a=testonly
[gecko.git] / dom / smil / SMILCompositor.cpp
blobd3675dd8914a97a868edb79a7b35de2dcaac5d03
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 "SMILCompositor.h"
9 #include "nsComputedDOMStyle.h"
10 #include "nsCSSProps.h"
11 #include "nsHashKeys.h"
12 #include "SMILCSSProperty.h"
14 namespace mozilla {
16 // PLDHashEntryHdr methods
17 bool SMILCompositor::KeyEquals(KeyTypePointer aKey) const {
18 return aKey && aKey->Equals(mKey);
21 /*static*/ PLDHashNumber SMILCompositor::HashKey(KeyTypePointer aKey) {
22 // Combine the 3 values into one numeric value, which will be hashed.
23 // NOTE: We right-shift one of the pointers by 2 to get some randomness in
24 // its 2 lowest-order bits. (Those shifted-off bits will always be 0 since
25 // our pointers will be word-aligned.)
26 return (NS_PTR_TO_UINT32(aKey->mElement.get()) >> 2) +
27 NS_PTR_TO_UINT32(aKey->mAttributeName.get());
30 // Cycle-collection support
31 void SMILCompositor::Traverse(nsCycleCollectionTraversalCallback* aCallback) {
32 if (!mKey.mElement) return;
34 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback, "Compositor mKey.mElement");
35 aCallback->NoteXPCOMChild(mKey.mElement);
38 // Other methods
39 void SMILCompositor::AddAnimationFunction(SMILAnimationFunction* aFunc) {
40 if (aFunc) {
41 mAnimationFunctions.AppendElement(aFunc);
45 void SMILCompositor::ComposeAttribute(bool& aMightHavePendingStyleUpdates) {
46 if (!mKey.mElement) return;
48 // If we might need to resolve base styles, grab a suitable ComputedStyle
49 // for initializing our SMILAttr with.
50 RefPtr<ComputedStyle> baseComputedStyle;
51 if (MightNeedBaseStyle()) {
52 baseComputedStyle = nsComputedDOMStyle::GetUnanimatedComputedStyleNoFlush(
53 mKey.mElement, nullptr);
56 // FIRST: Get the SMILAttr (to grab base value from, and to eventually
57 // give animated value to)
58 UniquePtr<SMILAttr> smilAttr = CreateSMILAttr(baseComputedStyle);
59 if (!smilAttr) {
60 // Target attribute not found (or, out of memory)
61 return;
63 if (mAnimationFunctions.IsEmpty()) {
64 // No active animation functions. (We can still have a SMILCompositor in
65 // that case if an animation function has *just* become inactive)
66 smilAttr->ClearAnimValue();
67 // Removing the animation effect may require a style update.
68 aMightHavePendingStyleUpdates = true;
69 return;
72 // SECOND: Sort the animationFunctions, to prepare for compositing.
73 SMILAnimationFunction::Comparator comparator;
74 mAnimationFunctions.Sort(comparator);
76 // THIRD: Step backwards through animation functions to find out
77 // which ones we actually care about.
78 uint32_t firstFuncToCompose = GetFirstFuncToAffectSandwich();
80 // FOURTH: Get & cache base value
81 SMILValue sandwichResultValue;
82 if (!mAnimationFunctions[firstFuncToCompose]->WillReplace()) {
83 sandwichResultValue = smilAttr->GetBaseValue();
85 UpdateCachedBaseValue(sandwichResultValue);
87 if (!mForceCompositing) {
88 return;
91 // FIFTH: Compose animation functions
92 aMightHavePendingStyleUpdates = true;
93 uint32_t length = mAnimationFunctions.Length();
94 for (uint32_t i = firstFuncToCompose; i < length; ++i) {
95 mAnimationFunctions[i]->ComposeResult(*smilAttr, sandwichResultValue);
97 if (sandwichResultValue.IsNull()) {
98 smilAttr->ClearAnimValue();
99 return;
102 // SIXTH: Set the animated value to the final composited result.
103 nsresult rv = smilAttr->SetAnimValue(sandwichResultValue);
104 if (NS_FAILED(rv)) {
105 NS_WARNING("SMILAttr::SetAnimValue failed");
109 void SMILCompositor::ClearAnimationEffects() {
110 if (!mKey.mElement || !mKey.mAttributeName) return;
112 UniquePtr<SMILAttr> smilAttr = CreateSMILAttr(nullptr);
113 if (!smilAttr) {
114 // Target attribute not found (or, out of memory)
115 return;
117 smilAttr->ClearAnimValue();
120 // Protected Helper Functions
121 // --------------------------
122 UniquePtr<SMILAttr> SMILCompositor::CreateSMILAttr(
123 ComputedStyle* aBaseComputedStyle) {
124 nsCSSPropertyID propID = GetCSSPropertyToAnimate();
126 if (propID != eCSSProperty_UNKNOWN) {
127 return MakeUnique<SMILCSSProperty>(propID, mKey.mElement.get(),
128 aBaseComputedStyle);
131 return mKey.mElement->GetAnimatedAttr(mKey.mAttributeNamespaceID,
132 mKey.mAttributeName);
135 nsCSSPropertyID SMILCompositor::GetCSSPropertyToAnimate() const {
136 if (mKey.mAttributeNamespaceID != kNameSpaceID_None) {
137 return eCSSProperty_UNKNOWN;
140 nsCSSPropertyID propID =
141 nsCSSProps::LookupProperty(nsDependentAtomString(mKey.mAttributeName));
143 if (!SMILCSSProperty::IsPropertyAnimatable(propID)) {
144 return eCSSProperty_UNKNOWN;
147 // If we are animating the 'width' or 'height' of an outer SVG
148 // element we should animate it as a CSS property, but for other elements
149 // (e.g. <rect>) we should animate it as a length attribute.
150 // The easiest way to test for an outer SVG element, is to see if it is an
151 // SVG-namespace element mapping its width/height attribute to style.
153 // If we have animation of 'width' or 'height' on an SVG element that is
154 // NOT mapping that attributes to style then it must not be an outermost SVG
155 // element so we should return eCSSProperty_UNKNOWN to indicate that we
156 // should animate as an attribute instead.
157 if ((mKey.mAttributeName == nsGkAtoms::width ||
158 mKey.mAttributeName == nsGkAtoms::height) &&
159 mKey.mElement->GetNameSpaceID() == kNameSpaceID_SVG &&
160 !mKey.mElement->IsAttributeMapped(mKey.mAttributeName)) {
161 return eCSSProperty_UNKNOWN;
164 return propID;
167 bool SMILCompositor::MightNeedBaseStyle() const {
168 if (GetCSSPropertyToAnimate() == eCSSProperty_UNKNOWN) {
169 return false;
172 // We should return true if at least one animation function might build on
173 // the base value.
174 for (const SMILAnimationFunction* func : mAnimationFunctions) {
175 if (!func->WillReplace()) {
176 return true;
180 return false;
183 uint32_t SMILCompositor::GetFirstFuncToAffectSandwich() {
184 // For performance reasons, we throttle most animations on elements in
185 // display:none subtrees. (We can't throttle animations that target the
186 // "display" property itself, though -- if we did, display:none elements
187 // could never be dynamically displayed via animations.)
188 // To determine whether we're in a display:none subtree, we will check the
189 // element's primary frame since element in display:none subtree doesn't have
190 // a primary frame. Before this process, we will construct frame when we
191 // append an element to subtree. So we will not need to worry about pending
192 // frame construction in this step.
193 bool canThrottle = mKey.mAttributeName != nsGkAtoms::display &&
194 !mKey.mElement->GetPrimaryFrame();
196 uint32_t i;
197 for (i = mAnimationFunctions.Length(); i > 0; --i) {
198 SMILAnimationFunction* curAnimFunc = mAnimationFunctions[i - 1];
199 // In the following, the lack of short-circuit behavior of |= means that we
200 // will ALWAYS run UpdateCachedTarget (even if mForceCompositing is true)
201 // but only call HasChanged and WasSkippedInPrevSample if necessary. This
202 // is important since we need UpdateCachedTarget to run in order to detect
203 // changes to the target in subsequent samples.
204 mForceCompositing |= curAnimFunc->UpdateCachedTarget(mKey) ||
205 (curAnimFunc->HasChanged() && !canThrottle) ||
206 curAnimFunc->WasSkippedInPrevSample();
208 if (curAnimFunc->WillReplace()) {
209 --i;
210 break;
214 // Mark remaining animation functions as having been skipped so if we later
215 // use them we'll know to force compositing.
216 // Note that we only really need to do this if something has changed
217 // (otherwise we would have set the flag on a previous sample) and if
218 // something has changed mForceCompositing will be true.
219 if (mForceCompositing) {
220 for (uint32_t j = i; j > 0; --j) {
221 mAnimationFunctions[j - 1]->SetWasSkipped();
224 return i;
227 void SMILCompositor::UpdateCachedBaseValue(const SMILValue& aBaseValue) {
228 if (mCachedBaseValue != aBaseValue) {
229 // Base value has changed since last sample.
230 mCachedBaseValue = aBaseValue;
231 mForceCompositing = true;
235 } // namespace mozilla