Bug 1719855 - Take over preventDefaulted infomation for long-tap events to the origin...
[gecko.git] / dom / smil / SMILCompositor.cpp
blob6b55267cae2f4c6ca76f78e13e16afe4e3866dfe
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 "mozilla/dom/SVGSVGElement.h"
10 #include "nsComputedDOMStyle.h"
11 #include "nsCSSProps.h"
12 #include "nsHashKeys.h"
13 #include "SMILCSSProperty.h"
15 namespace mozilla {
17 // PLDHashEntryHdr methods
18 bool SMILCompositor::KeyEquals(KeyTypePointer aKey) const {
19 return aKey && aKey->Equals(mKey);
22 /*static*/
23 PLDHashNumber SMILCompositor::HashKey(KeyTypePointer aKey) {
24 // Combine the 3 values into one numeric value, which will be hashed.
25 // NOTE: We right-shift one of the pointers by 2 to get some randomness in
26 // its 2 lowest-order bits. (Those shifted-off bits will always be 0 since
27 // our pointers will be word-aligned.)
28 return (NS_PTR_TO_UINT32(aKey->mElement.get()) >> 2) +
29 NS_PTR_TO_UINT32(aKey->mAttributeName.get());
32 // Cycle-collection support
33 void SMILCompositor::Traverse(nsCycleCollectionTraversalCallback* aCallback) {
34 if (!mKey.mElement) return;
36 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback, "Compositor mKey.mElement");
37 aCallback->NoteXPCOMChild(mKey.mElement);
40 // Other methods
41 void SMILCompositor::AddAnimationFunction(SMILAnimationFunction* aFunc) {
42 if (aFunc) {
43 mAnimationFunctions.AppendElement(aFunc);
47 void SMILCompositor::ComposeAttribute(bool& aMightHavePendingStyleUpdates) {
48 if (!mKey.mElement) return;
50 // If we might need to resolve base styles, grab a suitable ComputedStyle
51 // for initializing our SMILAttr with.
52 RefPtr<const ComputedStyle> baseComputedStyle;
53 if (MightNeedBaseStyle()) {
54 baseComputedStyle = nsComputedDOMStyle::GetUnanimatedComputedStyleNoFlush(
55 mKey.mElement, PseudoStyleType::NotPseudo);
58 // FIRST: Get the SMILAttr (to grab base value from, and to eventually
59 // give animated value to)
60 UniquePtr<SMILAttr> smilAttr = CreateSMILAttr(baseComputedStyle);
61 if (!smilAttr) {
62 // Target attribute not found (or, out of memory)
63 return;
65 if (mAnimationFunctions.IsEmpty()) {
66 // No active animation functions. (We can still have a SMILCompositor in
67 // that case if an animation function has *just* become inactive)
68 smilAttr->ClearAnimValue();
69 // Removing the animation effect may require a style update.
70 aMightHavePendingStyleUpdates = true;
71 return;
74 // SECOND: Sort the animationFunctions, to prepare for compositing.
75 SMILAnimationFunction::Comparator comparator;
76 mAnimationFunctions.Sort(comparator);
78 // THIRD: Step backwards through animation functions to find out
79 // which ones we actually care about.
80 uint32_t firstFuncToCompose = GetFirstFuncToAffectSandwich();
82 // FOURTH: Get & cache base value
83 SMILValue sandwichResultValue;
84 if (!mAnimationFunctions[firstFuncToCompose]->WillReplace()) {
85 sandwichResultValue = smilAttr->GetBaseValue();
87 UpdateCachedBaseValue(sandwichResultValue);
89 if (!mForceCompositing) {
90 return;
93 // FIFTH: Compose animation functions
94 aMightHavePendingStyleUpdates = true;
95 uint32_t length = mAnimationFunctions.Length();
96 for (uint32_t i = firstFuncToCompose; i < length; ++i) {
97 mAnimationFunctions[i]->ComposeResult(*smilAttr, sandwichResultValue);
99 if (sandwichResultValue.IsNull()) {
100 smilAttr->ClearAnimValue();
101 return;
104 // SIXTH: Set the animated value to the final composited result.
105 nsresult rv = smilAttr->SetAnimValue(sandwichResultValue);
106 if (NS_FAILED(rv)) {
107 NS_WARNING("SMILAttr::SetAnimValue failed");
111 void SMILCompositor::ClearAnimationEffects() {
112 if (!mKey.mElement || !mKey.mAttributeName) return;
114 UniquePtr<SMILAttr> smilAttr = CreateSMILAttr(nullptr);
115 if (!smilAttr) {
116 // Target attribute not found (or, out of memory)
117 return;
119 smilAttr->ClearAnimValue();
122 // Protected Helper Functions
123 // --------------------------
124 UniquePtr<SMILAttr> SMILCompositor::CreateSMILAttr(
125 const ComputedStyle* aBaseComputedStyle) {
126 nsCSSPropertyID propID = GetCSSPropertyToAnimate();
128 if (propID != eCSSProperty_UNKNOWN) {
129 return MakeUnique<SMILCSSProperty>(propID, mKey.mElement.get(),
130 aBaseComputedStyle);
133 return mKey.mElement->GetAnimatedAttr(mKey.mAttributeNamespaceID,
134 mKey.mAttributeName);
137 nsCSSPropertyID SMILCompositor::GetCSSPropertyToAnimate() const {
138 if (mKey.mAttributeNamespaceID != kNameSpaceID_None) {
139 return eCSSProperty_UNKNOWN;
142 nsCSSPropertyID propID =
143 nsCSSProps::LookupProperty(nsAtomCString(mKey.mAttributeName));
145 if (!SMILCSSProperty::IsPropertyAnimatable(propID)) {
146 return eCSSProperty_UNKNOWN;
149 // If we are animating the 'width' or 'height' of an outer SVG
150 // element we should animate it as a CSS property, but for other elements
151 // in SVG namespace (e.g. <rect>) we should animate it as a length attribute.
152 if ((mKey.mAttributeName == nsGkAtoms::width ||
153 mKey.mAttributeName == nsGkAtoms::height) &&
154 mKey.mElement->GetNameSpaceID() == kNameSpaceID_SVG) {
155 // Not an <svg> element.
156 if (!mKey.mElement->IsSVGElement(nsGkAtoms::svg)) {
157 return eCSSProperty_UNKNOWN;
160 // An inner <svg> element
161 if (static_cast<dom::SVGSVGElement const&>(*mKey.mElement).IsInner()) {
162 return eCSSProperty_UNKNOWN;
165 // Indeed an outer <svg> element, fall through.
168 return propID;
171 bool SMILCompositor::MightNeedBaseStyle() const {
172 if (GetCSSPropertyToAnimate() == eCSSProperty_UNKNOWN) {
173 return false;
176 // We should return true if at least one animation function might build on
177 // the base value.
178 for (const SMILAnimationFunction* func : mAnimationFunctions) {
179 if (!func->WillReplace()) {
180 return true;
184 return false;
187 uint32_t SMILCompositor::GetFirstFuncToAffectSandwich() {
188 // For performance reasons, we throttle most animations on elements in
189 // display:none subtrees. (We can't throttle animations that target the
190 // "display" property itself, though -- if we did, display:none elements
191 // could never be dynamically displayed via animations.)
192 // To determine whether we're in a display:none subtree, we will check the
193 // element's primary frame since element in display:none subtree doesn't have
194 // a primary frame. Before this process, we will construct frame when we
195 // append an element to subtree. So we will not need to worry about pending
196 // frame construction in this step.
197 bool canThrottle = mKey.mAttributeName != nsGkAtoms::display &&
198 !mKey.mElement->GetPrimaryFrame();
200 uint32_t i;
201 for (i = mAnimationFunctions.Length(); i > 0; --i) {
202 SMILAnimationFunction* curAnimFunc = mAnimationFunctions[i - 1];
203 // In the following, the lack of short-circuit behavior of |= means that we
204 // will ALWAYS run UpdateCachedTarget (even if mForceCompositing is true)
205 // but only call HasChanged and WasSkippedInPrevSample if necessary. This
206 // is important since we need UpdateCachedTarget to run in order to detect
207 // changes to the target in subsequent samples.
208 mForceCompositing |= curAnimFunc->UpdateCachedTarget(mKey) ||
209 (curAnimFunc->HasChanged() && !canThrottle) ||
210 curAnimFunc->WasSkippedInPrevSample();
212 if (curAnimFunc->WillReplace()) {
213 --i;
214 break;
218 // Mark remaining animation functions as having been skipped so if we later
219 // use them we'll know to force compositing.
220 // Note that we only really need to do this if something has changed
221 // (otherwise we would have set the flag on a previous sample) and if
222 // something has changed mForceCompositing will be true.
223 if (mForceCompositing) {
224 for (uint32_t j = i; j > 0; --j) {
225 mAnimationFunctions[j - 1]->SetWasSkipped();
228 return i;
231 void SMILCompositor::UpdateCachedBaseValue(const SMILValue& aBaseValue) {
232 if (mCachedBaseValue != aBaseValue) {
233 // Base value has changed since last sample.
234 mCachedBaseValue = aBaseValue;
235 mForceCompositing = true;
239 } // namespace mozilla