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 #include "EffectCompositor.h"
8 
9 #include <bitset>
10 #include <initializer_list>
11 
12 #include "mozilla/dom/Animation.h"
13 #include "mozilla/dom/Element.h"
14 #include "mozilla/dom/KeyframeEffectReadOnly.h"
15 #include "mozilla/AnimationComparator.h"
16 #include "mozilla/AnimationPerformanceWarning.h"
17 #include "mozilla/AnimationTarget.h"
18 #include "mozilla/AnimationUtils.h"
19 #include "mozilla/AutoRestore.h"
20 #include "mozilla/EffectSet.h"
21 #ifdef MOZ_OLD_STYLE
22 #include "mozilla/GeckoStyleContext.h"
23 #endif
24 #include "mozilla/LayerAnimationInfo.h"
25 #include "mozilla/RestyleManager.h"
26 #include "mozilla/RestyleManagerInlines.h"
27 #include "mozilla/ServoBindings.h"  // Servo_GetProperties_Overriding_Animation
28 #include "mozilla/ServoStyleSet.h"
29 #include "mozilla/StyleAnimationValue.h"
30 #include "mozilla/TypeTraits.h"  // For Forward<>
31 #include "nsContentUtils.h"
32 #include "nsCSSPseudoElements.h"
33 #include "nsCSSPropertyIDSet.h"
34 #include "nsCSSProps.h"
35 #include "nsAtom.h"
36 #include "nsIPresShell.h"
37 #include "nsIPresShellInlines.h"
38 #include "nsLayoutUtils.h"
39 #ifdef MOZ_OLD_STYLE
40 #include "nsRuleNode.h"  // For nsRuleNode::ComputePropertiesOverridingAnimation
41 #include "nsRuleProcessorData.h"  // For ElementRuleProcessorData etc.
42 #endif
43 #include "nsStyleContextInlines.h"
44 #include "nsTArray.h"
45 #include "PendingAnimationTracker.h"
46 
47 using mozilla::dom::Animation;
48 using mozilla::dom::Element;
49 using mozilla::dom::KeyframeEffectReadOnly;
50 
51 namespace mozilla {
52 
53 NS_IMPL_CYCLE_COLLECTION_CLASS(EffectCompositor)
54 
55 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(EffectCompositor)
56   for (auto& elementSet : tmp->mElementsToRestyle) {
57     elementSet.Clear();
58   }
59 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
60 
61 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(EffectCompositor)
62   for (auto& elementSet : tmp->mElementsToRestyle) {
63     for (auto iter = elementSet.Iter(); !iter.Done(); iter.Next()) {
64       CycleCollectionNoteChild(cb, iter.Key().mElement,
65                                "EffectCompositor::mElementsToRestyle[]",
66                                cb.Flags());
67     }
68   }
69 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
70 
71 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(EffectCompositor, AddRef)
72 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(EffectCompositor, Release)
73 
74 namespace {
75 enum class MatchForCompositor {
76   // This animation matches and should run on the compositor if possible.
77   Yes,
78   // This (not currently playing) animation matches and can be run on the
79   // compositor if there are other animations for this property that return
80   // 'Yes'.
81   IfNeeded,
82   // This animation does not match or can't be run on the compositor.
83   No,
84   // This animation does not match or can't be run on the compositor and,
85   // furthermore, its presence means we should not run any animations for this
86   // property on the compositor.
87   NoAndBlockThisProperty
88 };
89 }
90 
IsMatchForCompositor(const KeyframeEffectReadOnly & aEffect,nsCSSPropertyID aProperty,const nsIFrame * aFrame)91 static MatchForCompositor IsMatchForCompositor(
92     const KeyframeEffectReadOnly& aEffect, nsCSSPropertyID aProperty,
93     const nsIFrame* aFrame) {
94   const Animation* animation = aEffect.GetAnimation();
95   MOZ_ASSERT(animation);
96 
97   if (!animation->IsRelevant()) {
98     return MatchForCompositor::No;
99   }
100 
101   AnimationPerformanceWarning::Type warningType;
102   if (animation->ShouldBeSynchronizedWithMainThread(aProperty, aFrame,
103                                                     warningType)) {
104     EffectCompositor::SetPerformanceWarning(
105         aFrame, aProperty, AnimationPerformanceWarning(warningType));
106     // For a given |aFrame|, we don't want some animations of |aProperty| to
107     // run on the compositor and others to run on the main thread, so if any
108     // need to be synchronized with the main thread, run them all there.
109     return MatchForCompositor::NoAndBlockThisProperty;
110   }
111 
112   if (!aEffect.HasEffectiveAnimationOfProperty(aProperty)) {
113     return MatchForCompositor::No;
114   }
115 
116   return animation->IsPlaying() ? MatchForCompositor::Yes
117                                 : MatchForCompositor::IfNeeded;
118 }
119 
120 // Helper function to factor out the common logic from
121 // GetAnimationsForCompositor and HasAnimationsForCompositor.
122 //
123 // Takes an optional array to fill with eligible animations.
124 //
125 // Returns true if there are eligible animations, false otherwise.
FindAnimationsForCompositor(const nsIFrame * aFrame,nsCSSPropertyID aProperty,nsTArray<RefPtr<dom::Animation>> * aMatches)126 bool FindAnimationsForCompositor(
127     const nsIFrame* aFrame, nsCSSPropertyID aProperty,
128     nsTArray<RefPtr<dom::Animation>>* aMatches /*out*/) {
129   MOZ_ASSERT(!aMatches || aMatches->IsEmpty(),
130              "Matches array, if provided, should be empty");
131 
132   EffectSet* effects = EffectSet::GetEffectSet(aFrame);
133   if (!effects || effects->IsEmpty()) {
134     return false;
135   }
136 
137   // First check for newly-started transform animations that should be
138   // synchronized with geometric animations. We need to do this before any
139   // other early returns (the one above is ok) since we can only check this
140   // state when the animation is newly-started.
141   if (aProperty == eCSSProperty_transform) {
142     PendingAnimationTracker* tracker =
143         aFrame->PresContext()->Document()->GetPendingAnimationTracker();
144     if (tracker) {
145       tracker->MarkAnimationsThatMightNeedSynchronization();
146     }
147   }
148 
149   // If the property will be added to the animations level of the cascade but
150   // there is an !important rule for that property in the cascade then the
151   // animation will not be applied since the !important rule overrides it.
152   if (effects->PropertiesWithImportantRules().HasProperty(aProperty) &&
153       effects->PropertiesForAnimationsLevel().HasProperty(aProperty)) {
154     return false;
155   }
156 
157   if (aFrame->RefusedAsyncAnimation()) {
158     return false;
159   }
160 
161   // The animation cascade will almost always be up-to-date by this point
162   // but there are some cases such as when we are restoring the refresh driver
163   // from test control after seeking where it might not be the case.
164   //
165   // Those cases are probably not important but just to be safe, let's make
166   // sure the cascade is up to date since if it *is* up to date, this is
167   // basically a no-op.
168   Maybe<NonOwningAnimationTarget> pseudoElement =
169       EffectCompositor::GetAnimationElementAndPseudoForFrame(aFrame);
170   if (pseudoElement) {
171     StyleBackendType backend = aFrame->StyleContext()->IsServo()
172                                    ? StyleBackendType::Servo
173                                    : StyleBackendType::Gecko;
174     EffectCompositor::MaybeUpdateCascadeResults(
175         backend, pseudoElement->mElement, pseudoElement->mPseudoType,
176         aFrame->StyleContext());
177   }
178 
179   if (!nsLayoutUtils::AreAsyncAnimationsEnabled()) {
180     if (nsLayoutUtils::IsAnimationLoggingEnabled()) {
181       nsCString message;
182       message.AppendLiteral(
183           "Performance warning: Async animations are "
184           "disabled");
185       AnimationUtils::LogAsyncAnimationFailure(message);
186     }
187     return false;
188   }
189 
190   // Disable async animations if we have a rendering observer that
191   // depends on our content (svg masking, -moz-element etc) so that
192   // it gets updated correctly.
193   nsIContent* content = aFrame->GetContent();
194   while (content) {
195     if (content->HasRenderingObservers()) {
196       EffectCompositor::SetPerformanceWarning(
197           aFrame, aProperty,
198           AnimationPerformanceWarning(
199               AnimationPerformanceWarning::Type::HasRenderingObserver));
200       return false;
201     }
202     content = content->GetParent();
203   }
204 
205   bool foundRunningAnimations = false;
206   for (KeyframeEffectReadOnly* effect : *effects) {
207     MatchForCompositor matchResult =
208         IsMatchForCompositor(*effect, aProperty, aFrame);
209 
210     if (matchResult == MatchForCompositor::NoAndBlockThisProperty) {
211       // For a given |aFrame|, we don't want some animations of |aProperty| to
212       // run on the compositor and others to run on the main thread, so if any
213       // need to be synchronized with the main thread, run them all there.
214       if (aMatches) {
215         aMatches->Clear();
216       }
217       return false;
218     }
219 
220     if (matchResult == MatchForCompositor::No) {
221       continue;
222     }
223 
224     if (aMatches) {
225       aMatches->AppendElement(effect->GetAnimation());
226     }
227 
228     if (matchResult == MatchForCompositor::Yes) {
229       foundRunningAnimations = true;
230     }
231   }
232 
233   // If all animations we added were not currently playing animations, don't
234   // send them to the compositor.
235   if (aMatches && !foundRunningAnimations) {
236     aMatches->Clear();
237   }
238 
239   MOZ_ASSERT(!foundRunningAnimations || !aMatches || !aMatches->IsEmpty(),
240              "If return value is true, matches array should be non-empty");
241 
242   if (aMatches && foundRunningAnimations) {
243     aMatches->Sort(AnimationPtrComparator<RefPtr<dom::Animation>>());
244   }
245 
246   return foundRunningAnimations;
247 }
248 
RequestRestyle(dom::Element * aElement,CSSPseudoElementType aPseudoType,RestyleType aRestyleType,CascadeLevel aCascadeLevel)249 void EffectCompositor::RequestRestyle(dom::Element* aElement,
250                                       CSSPseudoElementType aPseudoType,
251                                       RestyleType aRestyleType,
252                                       CascadeLevel aCascadeLevel) {
253   if (!mPresContext) {
254     // Pres context will be null after the effect compositor is disconnected.
255     return;
256   }
257 
258   // Ignore animations on orphaned elements and elements in documents without
259   // a pres shell (e.g. XMLHttpRequest responseXML documents).
260   if (!nsContentUtils::GetPresShellForContent(aElement)) {
261     return;
262   }
263 
264   auto& elementsToRestyle = mElementsToRestyle[aCascadeLevel];
265   PseudoElementHashEntry::KeyType key = {aElement, aPseudoType};
266 
267   if (aRestyleType == RestyleType::Throttled) {
268     elementsToRestyle.LookupForAdd(key).OrInsert([]() { return false; });
269     mPresContext->PresShell()->SetNeedThrottledAnimationFlush();
270   } else {
271     bool skipRestyle;
272     // Update hashtable first in case PostRestyleForAnimation mutates it.
273     // (It shouldn't, but just to be sure.)
274     if (auto p = elementsToRestyle.LookupForAdd(key)) {
275       skipRestyle = p.Data();
276       p.Data() = true;
277     } else {
278       skipRestyle = false;
279       p.OrInsert([]() { return true; });
280     }
281 
282     if (!skipRestyle) {
283       PostRestyleForAnimation(aElement, aPseudoType, aCascadeLevel);
284     }
285   }
286 
287   if (aRestyleType == RestyleType::Layer) {
288     mPresContext->RestyleManager()->IncrementAnimationGeneration();
289     EffectSet* effectSet = EffectSet::GetEffectSet(aElement, aPseudoType);
290     if (effectSet) {
291       effectSet->UpdateAnimationGeneration(mPresContext);
292     }
293   }
294 }
295 
PostRestyleForAnimation(dom::Element * aElement,CSSPseudoElementType aPseudoType,CascadeLevel aCascadeLevel)296 void EffectCompositor::PostRestyleForAnimation(dom::Element* aElement,
297                                                CSSPseudoElementType aPseudoType,
298                                                CascadeLevel aCascadeLevel) {
299   if (!mPresContext) {
300     return;
301   }
302 
303   dom::Element* element = GetElementToRestyle(aElement, aPseudoType);
304   if (!element) {
305     return;
306   }
307 
308   nsRestyleHint hint = aCascadeLevel == CascadeLevel::Transitions
309                            ? eRestyle_CSSTransitions
310                            : eRestyle_CSSAnimations;
311 
312   if (mPresContext->StyleSet()->IsServo()) {
313     MOZ_ASSERT(NS_IsMainThread(),
314                "Restyle request during restyling should be requested only on "
315                "the main-thread. e.g. after the parallel traversal");
316     if (ServoStyleSet::IsInServoTraversal() || mIsInPreTraverse) {
317       MOZ_ASSERT(hint == eRestyle_CSSAnimations ||
318                  hint == eRestyle_CSSTransitions);
319 
320       // We can't call Servo_NoteExplicitHints here since AtomicRefCell does not
321       // allow us mutate ElementData of the |aElement| in SequentialTask.
322       // Instead we call Servo_NoteExplicitHints for the element in
323       // PreTraverse() which will be called right before the second traversal
324       // that we do for updating CSS animations. In that case PreTraverse() will
325       // return true so that we know to do the second traversal so we don't need
326       // to post any restyle requests to the PresShell.
327       return;
328     } else {
329       MOZ_ASSERT(!mPresContext->RestyleManager()->IsInStyleRefresh());
330     }
331   }
332   mPresContext->PresShell()->RestyleForAnimation(element, hint);
333 }
334 
PostRestyleForThrottledAnimations()335 void EffectCompositor::PostRestyleForThrottledAnimations() {
336   for (size_t i = 0; i < kCascadeLevelCount; i++) {
337     CascadeLevel cascadeLevel = CascadeLevel(i);
338     auto& elementSet = mElementsToRestyle[cascadeLevel];
339 
340     for (auto iter = elementSet.Iter(); !iter.Done(); iter.Next()) {
341       bool& postedRestyle = iter.Data();
342       if (postedRestyle) {
343         continue;
344       }
345 
346       PostRestyleForAnimation(iter.Key().mElement, iter.Key().mPseudoType,
347                               cascadeLevel);
348       postedRestyle = true;
349     }
350   }
351 }
352 
ClearRestyleRequestsFor(Element * aElement)353 void EffectCompositor::ClearRestyleRequestsFor(Element* aElement) {
354   MOZ_ASSERT(aElement);
355 
356   auto& elementsToRestyle = mElementsToRestyle[CascadeLevel::Animations];
357 
358   CSSPseudoElementType pseudoType = aElement->GetPseudoElementType();
359   if (pseudoType == CSSPseudoElementType::NotPseudo) {
360     PseudoElementHashEntry::KeyType notPseudoKey = {
361         aElement, CSSPseudoElementType::NotPseudo};
362     PseudoElementHashEntry::KeyType beforePseudoKey = {
363         aElement, CSSPseudoElementType::before};
364     PseudoElementHashEntry::KeyType afterPseudoKey = {
365         aElement, CSSPseudoElementType::after};
366 
367     elementsToRestyle.Remove(notPseudoKey);
368     elementsToRestyle.Remove(beforePseudoKey);
369     elementsToRestyle.Remove(afterPseudoKey);
370   } else if (pseudoType == CSSPseudoElementType::before ||
371              pseudoType == CSSPseudoElementType::after) {
372     Element* parentElement = aElement->GetParentElement();
373     MOZ_ASSERT(parentElement);
374     PseudoElementHashEntry::KeyType key = {parentElement, pseudoType};
375     elementsToRestyle.Remove(key);
376   }
377 }
378 
379 template <typename StyleType>
UpdateEffectProperties(StyleType * aStyleType,Element * aElement,CSSPseudoElementType aPseudoType)380 void EffectCompositor::UpdateEffectProperties(
381     StyleType* aStyleType, Element* aElement,
382     CSSPseudoElementType aPseudoType) {
383   EffectSet* effectSet = EffectSet::GetEffectSet(aElement, aPseudoType);
384   if (!effectSet) {
385     return;
386   }
387 
388   // Style context (Gecko) or computed values (Stylo) change might cause CSS
389   // cascade level, e.g removing !important, so we should update the cascading
390   // result.
391   effectSet->MarkCascadeNeedsUpdate();
392 
393   for (KeyframeEffectReadOnly* effect : *effectSet) {
394     effect->UpdateProperties(aStyleType);
395   }
396 }
397 
398 #ifdef MOZ_OLD_STYLE
MaybeUpdateAnimationRule(dom::Element * aElement,CSSPseudoElementType aPseudoType,CascadeLevel aCascadeLevel,nsStyleContext * aStyleContext)399 void EffectCompositor::MaybeUpdateAnimationRule(
400     dom::Element* aElement, CSSPseudoElementType aPseudoType,
401     CascadeLevel aCascadeLevel, nsStyleContext* aStyleContext) {
402   // First update cascade results since that may cause some elements to
403   // be marked as needing a restyle.
404   MaybeUpdateCascadeResults(StyleBackendType::Gecko, aElement, aPseudoType,
405                             aStyleContext);
406 
407   auto& elementsToRestyle = mElementsToRestyle[aCascadeLevel];
408   PseudoElementHashEntry::KeyType key = {aElement, aPseudoType};
409 
410   if (!elementsToRestyle.Contains(key)) {
411     return;
412   }
413 
414   ComposeAnimationRule(aElement, aPseudoType, aCascadeLevel);
415 
416   elementsToRestyle.Remove(key);
417 }
418 
GetAnimationRule(dom::Element * aElement,CSSPseudoElementType aPseudoType,CascadeLevel aCascadeLevel,nsStyleContext * aStyleContext)419 nsIStyleRule* EffectCompositor::GetAnimationRule(
420     dom::Element* aElement, CSSPseudoElementType aPseudoType,
421     CascadeLevel aCascadeLevel, nsStyleContext* aStyleContext) {
422   // NOTE: We need to be careful about early returns in this method where
423   // we *don't* update mElementsToRestyle. When we get a call to
424   // RequestRestyle that results in a call to PostRestyleForAnimation, we
425   // will set a bool flag in mElementsToRestyle indicating that we've
426   // called PostRestyleForAnimation so we don't need to call it again
427   // until that restyle happens. During that restyle, if we arrive here
428   // and *don't* update mElementsToRestyle we'll continue to skip calling
429   // PostRestyleForAnimation from RequestRestyle.
430 
431   if (!mPresContext || !mPresContext->IsDynamic()) {
432     // For print or print preview, ignore animations.
433     return nullptr;
434   }
435 
436   MOZ_ASSERT(mPresContext->RestyleManager()->IsGecko(),
437              "stylo: Servo-backed style system should not be using "
438              "EffectCompositor");
439   if (mPresContext->RestyleManager()->AsGecko()->SkipAnimationRules()) {
440     // We don't need to worry about updating mElementsToRestyle in this case
441     // since this is not the animation restyle we requested when we called
442     // PostRestyleForAnimation (see comment at start of this method).
443     return nullptr;
444   }
445 
446   MaybeUpdateAnimationRule(aElement, aPseudoType, aCascadeLevel, aStyleContext);
447 
448 #ifdef DEBUG
449   {
450     auto& elementsToRestyle = mElementsToRestyle[aCascadeLevel];
451     PseudoElementHashEntry::KeyType key = {aElement, aPseudoType};
452     MOZ_ASSERT(!elementsToRestyle.Contains(key),
453                "Element should no longer require a restyle after its "
454                "animation rule has been updated");
455   }
456 #endif
457 
458   EffectSet* effectSet = EffectSet::GetEffectSet(aElement, aPseudoType);
459   if (!effectSet) {
460     return nullptr;
461   }
462 
463   return effectSet->AnimationRule(aCascadeLevel);
464 }
465 #endif
466 
467 namespace {
468 class EffectCompositeOrderComparator {
469  public:
Equals(const KeyframeEffectReadOnly * a,const KeyframeEffectReadOnly * b) const470   bool Equals(const KeyframeEffectReadOnly* a,
471               const KeyframeEffectReadOnly* b) const {
472     return a == b;
473   }
474 
LessThan(const KeyframeEffectReadOnly * a,const KeyframeEffectReadOnly * b) const475   bool LessThan(const KeyframeEffectReadOnly* a,
476                 const KeyframeEffectReadOnly* b) const {
477     MOZ_ASSERT(a->GetAnimation() && b->GetAnimation());
478     MOZ_ASSERT(
479         Equals(a, b) ||
480         a->GetAnimation()->HasLowerCompositeOrderThan(*b->GetAnimation()) !=
481             b->GetAnimation()->HasLowerCompositeOrderThan(*a->GetAnimation()));
482     return a->GetAnimation()->HasLowerCompositeOrderThan(*b->GetAnimation());
483   }
484 };
485 }  // namespace
486 
GetServoAnimationRule(const dom::Element * aElement,CSSPseudoElementType aPseudoType,CascadeLevel aCascadeLevel,RawServoAnimationValueMapBorrowedMut aAnimationValues)487 bool EffectCompositor::GetServoAnimationRule(
488     const dom::Element* aElement, CSSPseudoElementType aPseudoType,
489     CascadeLevel aCascadeLevel,
490     RawServoAnimationValueMapBorrowedMut aAnimationValues) {
491   MOZ_ASSERT(aAnimationValues);
492   MOZ_ASSERT(mPresContext && mPresContext->IsDynamic(),
493              "Should not be in print preview");
494   // Gecko_GetAnimationRule should have already checked this
495   MOZ_ASSERT(nsContentUtils::GetPresShellForContent(aElement),
496              "Should not be trying to run animations on elements in documents"
497              " without a pres shell (e.g. XMLHttpRequest documents)");
498 
499   EffectSet* effectSet = EffectSet::GetEffectSet(aElement, aPseudoType);
500   if (!effectSet) {
501     return false;
502   }
503 
504   // Get a list of effects sorted by composite order.
505   nsTArray<KeyframeEffectReadOnly*> sortedEffectList(effectSet->Count());
506   for (KeyframeEffectReadOnly* effect : *effectSet) {
507     sortedEffectList.AppendElement(effect);
508   }
509   sortedEffectList.Sort(EffectCompositeOrderComparator());
510 
511   // If multiple animations affect the same property, animations with higher
512   // composite order (priority) override or add or animations with lower
513   // priority.
514   const nsCSSPropertyIDSet propertiesToSkip =
515       aCascadeLevel == CascadeLevel::Animations
516           ? effectSet->PropertiesForAnimationsLevel().Inverse()
517           : effectSet->PropertiesForAnimationsLevel();
518   for (KeyframeEffectReadOnly* effect : sortedEffectList) {
519     effect->GetAnimation()->ComposeStyle(*aAnimationValues, propertiesToSkip);
520   }
521 
522   MOZ_ASSERT(effectSet == EffectSet::GetEffectSet(aElement, aPseudoType),
523              "EffectSet should not change while composing style");
524 
525   return true;
526 }
527 
GetElementToRestyle(dom::Element * aElement,CSSPseudoElementType aPseudoType)528 /* static */ dom::Element* EffectCompositor::GetElementToRestyle(
529     dom::Element* aElement, CSSPseudoElementType aPseudoType) {
530   if (aPseudoType == CSSPseudoElementType::NotPseudo) {
531     return aElement;
532   }
533 
534   if (aPseudoType == CSSPseudoElementType::before) {
535     return nsLayoutUtils::GetBeforePseudo(aElement);
536   }
537 
538   if (aPseudoType == CSSPseudoElementType::after) {
539     return nsLayoutUtils::GetAfterPseudo(aElement);
540   }
541 
542   NS_NOTREACHED(
543       "Should not try to get the element to restyle for a pseudo "
544       "other that :before or :after");
545   return nullptr;
546 }
547 
HasPendingStyleUpdates() const548 bool EffectCompositor::HasPendingStyleUpdates() const {
549   for (auto& elementSet : mElementsToRestyle) {
550     if (elementSet.Count()) {
551       return true;
552     }
553   }
554 
555   return false;
556 }
557 
558 #ifdef MOZ_OLD_STYLE
HasThrottledStyleUpdates() const559 bool EffectCompositor::HasThrottledStyleUpdates() const {
560   for (auto& elementSet : mElementsToRestyle) {
561     for (auto iter = elementSet.ConstIter(); !iter.Done(); iter.Next()) {
562       if (!iter.Data()) {
563         return true;
564       }
565     }
566   }
567 
568   return false;
569 }
570 
AddStyleUpdatesTo(RestyleTracker & aTracker)571 void EffectCompositor::AddStyleUpdatesTo(RestyleTracker& aTracker) {
572   if (!mPresContext) {
573     return;
574   }
575 
576   for (size_t i = 0; i < kCascadeLevelCount; i++) {
577     CascadeLevel cascadeLevel = CascadeLevel(i);
578     auto& elementSet = mElementsToRestyle[cascadeLevel];
579 
580     // Copy the list of elements to restyle to a separate array that we can
581     // iterate over. This is because we need to call MaybeUpdateCascadeResults
582     // on each element, but doing that can mutate elementSet. In this case
583     // it will only mutate the bool value associated with each element in the
584     // set but even doing that will cause assertions in PLDHashTable to fail
585     // if we are iterating over the hashtable at the same time.
586     nsTArray<PseudoElementHashEntry::KeyType> elementsToRestyle(
587         elementSet.Count());
588     for (auto iter = elementSet.Iter(); !iter.Done(); iter.Next()) {
589       // Skip animations on elements that have been orphaned since they
590       // requested a restyle.
591       if (iter.Key().mElement->IsInComposedDoc()) {
592         elementsToRestyle.AppendElement(iter.Key());
593       }
594     }
595 
596     for (auto& pseudoElem : elementsToRestyle) {
597       MaybeUpdateCascadeResults(StyleBackendType::Gecko, pseudoElem.mElement,
598                                 pseudoElem.mPseudoType, nullptr);
599 
600       ComposeAnimationRule(pseudoElem.mElement, pseudoElem.mPseudoType,
601                            cascadeLevel);
602 
603       dom::Element* elementToRestyle =
604           GetElementToRestyle(pseudoElem.mElement, pseudoElem.mPseudoType);
605       if (elementToRestyle) {
606         nsRestyleHint rshint = cascadeLevel == CascadeLevel::Transitions
607                                    ? eRestyle_CSSTransitions
608                                    : eRestyle_CSSAnimations;
609         aTracker.AddPendingRestyle(elementToRestyle, rshint, nsChangeHint(0));
610       }
611     }
612 
613     elementSet.Clear();
614     // Note: mElement pointers in elementsToRestyle might now dangle
615   }
616 }
617 #endif
618 
HasAnimationsForCompositor(const nsIFrame * aFrame,nsCSSPropertyID aProperty)619 /* static */ bool EffectCompositor::HasAnimationsForCompositor(
620     const nsIFrame* aFrame, nsCSSPropertyID aProperty) {
621   return FindAnimationsForCompositor(aFrame, aProperty, nullptr);
622 }
623 
624 /* static */ nsTArray<RefPtr<dom::Animation>>
GetAnimationsForCompositor(const nsIFrame * aFrame,nsCSSPropertyID aProperty)625 EffectCompositor::GetAnimationsForCompositor(const nsIFrame* aFrame,
626                                              nsCSSPropertyID aProperty) {
627   nsTArray<RefPtr<dom::Animation>> result;
628 
629 #ifdef DEBUG
630   bool foundSome =
631 #endif
632       FindAnimationsForCompositor(aFrame, aProperty, &result);
633   MOZ_ASSERT(!foundSome || !result.IsEmpty(),
634              "If return value is true, matches array should be non-empty");
635 
636   return result;
637 }
638 
ClearIsRunningOnCompositor(const nsIFrame * aFrame,nsCSSPropertyID aProperty)639 /* static */ void EffectCompositor::ClearIsRunningOnCompositor(
640     const nsIFrame* aFrame, nsCSSPropertyID aProperty) {
641   EffectSet* effects = EffectSet::GetEffectSet(aFrame);
642   if (!effects) {
643     return;
644   }
645 
646   for (KeyframeEffectReadOnly* effect : *effects) {
647     effect->SetIsRunningOnCompositor(aProperty, false);
648   }
649 }
650 
MaybeUpdateCascadeResults(StyleBackendType aBackendType,Element * aElement,CSSPseudoElementType aPseudoType,nsStyleContext * aStyleContext)651 /* static */ void EffectCompositor::MaybeUpdateCascadeResults(
652     StyleBackendType aBackendType, Element* aElement,
653     CSSPseudoElementType aPseudoType, nsStyleContext* aStyleContext) {
654   EffectSet* effects = EffectSet::GetEffectSet(aElement, aPseudoType);
655   if (!effects || !effects->CascadeNeedsUpdate()) {
656     return;
657   }
658 
659   UpdateCascadeResults(aBackendType, *effects, aElement, aPseudoType,
660                        aStyleContext);
661 
662   MOZ_ASSERT(!effects->CascadeNeedsUpdate(), "Failed to update cascade state");
663 }
664 
665 /* static */ Maybe<NonOwningAnimationTarget>
GetAnimationElementAndPseudoForFrame(const nsIFrame * aFrame)666 EffectCompositor::GetAnimationElementAndPseudoForFrame(const nsIFrame* aFrame) {
667   // Always return the same object to benefit from return-value optimization.
668   Maybe<NonOwningAnimationTarget> result;
669 
670   CSSPseudoElementType pseudoType = aFrame->StyleContext()->GetPseudoType();
671 
672   if (pseudoType != CSSPseudoElementType::NotPseudo &&
673       pseudoType != CSSPseudoElementType::before &&
674       pseudoType != CSSPseudoElementType::after) {
675     return result;
676   }
677 
678   nsIContent* content = aFrame->GetContent();
679   if (!content) {
680     return result;
681   }
682 
683   if (pseudoType == CSSPseudoElementType::before ||
684       pseudoType == CSSPseudoElementType::after) {
685     content = content->GetParent();
686     if (!content) {
687       return result;
688     }
689   }
690 
691   if (!content->IsElement()) {
692     return result;
693   }
694 
695   result.emplace(content->AsElement(), pseudoType);
696 
697   return result;
698 }
699 
700 #ifdef MOZ_OLD_STYLE
ComposeAnimationRule(dom::Element * aElement,CSSPseudoElementType aPseudoType,CascadeLevel aCascadeLevel)701 /* static */ void EffectCompositor::ComposeAnimationRule(
702     dom::Element* aElement, CSSPseudoElementType aPseudoType,
703     CascadeLevel aCascadeLevel) {
704   EffectSet* effects = EffectSet::GetEffectSet(aElement, aPseudoType);
705   if (!effects) {
706     return;
707   }
708 
709   // The caller is responsible for calling MaybeUpdateCascadeResults first.
710   MOZ_ASSERT(!effects->CascadeNeedsUpdate(),
711              "Animation cascade out of date when composing animation rule");
712 
713   // Get a list of effects sorted by composite order.
714   nsTArray<KeyframeEffectReadOnly*> sortedEffectList(effects->Count());
715   for (KeyframeEffectReadOnly* effect : *effects) {
716     sortedEffectList.AppendElement(effect);
717   }
718   sortedEffectList.Sort(EffectCompositeOrderComparator());
719 
720   RefPtr<AnimValuesStyleRule>& animRule = effects->AnimationRule(aCascadeLevel);
721   animRule = nullptr;
722 
723   // If multiple animations affect the same property, animations with higher
724   // composite order (priority) override or add or animations with lower
725   // priority except properties in propertiesToSkip.
726   const nsCSSPropertyIDSet& propertiesToSkip =
727       aCascadeLevel == CascadeLevel::Animations
728           ? effects->PropertiesForAnimationsLevel().Inverse()
729           : effects->PropertiesForAnimationsLevel();
730   for (KeyframeEffectReadOnly* effect : sortedEffectList) {
731     effect->GetAnimation()->WillComposeStyle();
732     effect->GetAnimation()->ComposeStyle(animRule, propertiesToSkip);
733   }
734 
735   MOZ_ASSERT(effects == EffectSet::GetEffectSet(aElement, aPseudoType),
736              "EffectSet should not change while composing style");
737 }
738 #endif
739 
GetOverriddenProperties(StyleBackendType aBackendType,EffectSet & aEffectSet,Element * aElement,CSSPseudoElementType aPseudoType,nsStyleContext * aStyleContext)740 /* static */ nsCSSPropertyIDSet EffectCompositor::GetOverriddenProperties(
741     StyleBackendType aBackendType, EffectSet& aEffectSet, Element* aElement,
742     CSSPseudoElementType aPseudoType, nsStyleContext* aStyleContext) {
743   MOZ_ASSERT(aBackendType != StyleBackendType::Servo || aElement,
744              "Should have an element to get style data from if we are using"
745              " the Servo backend");
746 
747   nsCSSPropertyIDSet result;
748 
749   Element* elementToRestyle = GetElementToRestyle(aElement, aPseudoType);
750   if (aBackendType == StyleBackendType::Gecko && !aStyleContext) {
751 #ifdef MOZ_OLD_STYLE
752     if (elementToRestyle) {
753       nsIFrame* frame = elementToRestyle->GetPrimaryFrame();
754       if (frame) {
755         aStyleContext = frame->StyleContext();
756       }
757     }
758 
759     if (!aStyleContext) {
760       return result;
761     }
762 #else
763     MOZ_CRASH("old style system disabled");
764 #endif
765   } else if (aBackendType == StyleBackendType::Servo && !elementToRestyle) {
766     return result;
767   }
768 
769   AutoTArray<nsCSSPropertyID, LayerAnimationInfo::kRecords> propertiesToTrack;
770   {
771     nsCSSPropertyIDSet propertiesToTrackAsSet;
772     for (KeyframeEffectReadOnly* effect : aEffectSet) {
773       for (const AnimationProperty& property : effect->Properties()) {
774         if (nsCSSProps::PropHasFlags(property.mProperty,
775                                      CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR) &&
776             !propertiesToTrackAsSet.HasProperty(property.mProperty)) {
777           propertiesToTrackAsSet.AddProperty(property.mProperty);
778           propertiesToTrack.AppendElement(property.mProperty);
779         }
780       }
781       // Skip iterating over the rest of the effects if we've already
782       // found all the compositor-animatable properties.
783       if (propertiesToTrack.Length() == LayerAnimationInfo::kRecords) {
784         break;
785       }
786     }
787   }
788 
789   if (propertiesToTrack.IsEmpty()) {
790     return result;
791   }
792 
793   switch (aBackendType) {
794     case StyleBackendType::Servo:
795       Servo_GetProperties_Overriding_Animation(elementToRestyle,
796                                                &propertiesToTrack, &result);
797       break;
798     case StyleBackendType::Gecko:
799 #ifdef MOZ_OLD_STYLE
800       nsRuleNode::ComputePropertiesOverridingAnimation(
801           propertiesToTrack, aStyleContext->AsGecko(), result);
802 #else
803       MOZ_CRASH("old style system disabled");
804 #endif
805       break;
806 
807     default:
808       MOZ_ASSERT_UNREACHABLE("Unsupported style backend");
809   }
810 
811   return result;
812 }
813 
UpdateCascadeResults(StyleBackendType aBackendType,EffectSet & aEffectSet,Element * aElement,CSSPseudoElementType aPseudoType,nsStyleContext * aStyleContext)814 /* static */ void EffectCompositor::UpdateCascadeResults(
815     StyleBackendType aBackendType, EffectSet& aEffectSet, Element* aElement,
816     CSSPseudoElementType aPseudoType, nsStyleContext* aStyleContext) {
817   MOZ_ASSERT(EffectSet::GetEffectSet(aElement, aPseudoType) == &aEffectSet,
818              "Effect set should correspond to the specified (pseudo-)element");
819   if (aEffectSet.IsEmpty()) {
820     aEffectSet.MarkCascadeUpdated();
821     return;
822   }
823 
824   // Get a list of effects sorted by composite order.
825   nsTArray<KeyframeEffectReadOnly*> sortedEffectList(aEffectSet.Count());
826   for (KeyframeEffectReadOnly* effect : aEffectSet) {
827     sortedEffectList.AppendElement(effect);
828   }
829   sortedEffectList.Sort(EffectCompositeOrderComparator());
830 
831   // Get properties that override the *animations* level of the cascade.
832   //
833   // We only do this for properties that we can animate on the compositor
834   // since we will apply other properties on the main thread where the usual
835   // cascade applies.
836   nsCSSPropertyIDSet overriddenProperties = GetOverriddenProperties(
837       aBackendType, aEffectSet, aElement, aPseudoType, aStyleContext);
838 
839   // Returns a bitset the represents which properties from
840   // LayerAnimationInfo::sRecords are present in |aPropertySet|.
841   auto compositorPropertiesInSet = [](nsCSSPropertyIDSet& aPropertySet)
842       -> std::bitset<LayerAnimationInfo::kRecords> {
843     std::bitset<LayerAnimationInfo::kRecords> result;
844     for (size_t i = 0; i < LayerAnimationInfo::kRecords; i++) {
845       if (aPropertySet.HasProperty(LayerAnimationInfo::sRecords[i].mProperty)) {
846         result.set(i);
847       }
848     }
849     return result;
850   };
851 
852   nsCSSPropertyIDSet& propertiesWithImportantRules =
853       aEffectSet.PropertiesWithImportantRules();
854   nsCSSPropertyIDSet& propertiesForAnimationsLevel =
855       aEffectSet.PropertiesForAnimationsLevel();
856 
857   // Record which compositor-animatable properties were originally set so we can
858   // compare for changes later.
859   std::bitset<LayerAnimationInfo::kRecords>
860       prevCompositorPropertiesWithImportantRules =
861           compositorPropertiesInSet(propertiesWithImportantRules);
862 
863   nsCSSPropertyIDSet prevPropertiesForAnimationsLevel =
864       propertiesForAnimationsLevel;
865 
866   propertiesWithImportantRules.Empty();
867   propertiesForAnimationsLevel.Empty();
868 
869   nsCSSPropertyIDSet propertiesForTransitionsLevel;
870 
871   for (const KeyframeEffectReadOnly* effect : sortedEffectList) {
872     MOZ_ASSERT(effect->GetAnimation(),
873                "Effects on a target element should have an Animation");
874     CascadeLevel cascadeLevel = effect->GetAnimation()->CascadeLevel();
875 
876     for (const AnimationProperty& prop : effect->Properties()) {
877       if (overriddenProperties.HasProperty(prop.mProperty)) {
878         propertiesWithImportantRules.AddProperty(prop.mProperty);
879       }
880 
881       switch (cascadeLevel) {
882         case EffectCompositor::CascadeLevel::Animations:
883           propertiesForAnimationsLevel.AddProperty(prop.mProperty);
884           break;
885         case EffectCompositor::CascadeLevel::Transitions:
886           propertiesForTransitionsLevel.AddProperty(prop.mProperty);
887           break;
888       }
889     }
890   }
891 
892   aEffectSet.MarkCascadeUpdated();
893 
894   nsPresContext* presContext = nsContentUtils::GetContextForContent(aElement);
895   if (!presContext) {
896     return;
897   }
898 
899   // If properties for compositor are newly overridden by !important rules, or
900   // released from being overridden by !important rules, we need to update
901   // layers for animations level because it's a trigger to send animations to
902   // the compositor or pull animations back from the compositor.
903   if (prevCompositorPropertiesWithImportantRules !=
904       compositorPropertiesInSet(propertiesWithImportantRules)) {
905     presContext->EffectCompositor()->RequestRestyle(
906         aElement, aPseudoType, EffectCompositor::RestyleType::Layer,
907         EffectCompositor::CascadeLevel::Animations);
908   }
909 
910   // If we have transition properties and if the same propery for animations
911   // level is newly added or removed, we need to update the transition level
912   // rule since the it will be added/removed from the rule tree.
913   nsCSSPropertyIDSet changedPropertiesForAnimationLevel =
914       prevPropertiesForAnimationsLevel.Xor(propertiesForAnimationsLevel);
915   nsCSSPropertyIDSet commonProperties = propertiesForTransitionsLevel.Intersect(
916       changedPropertiesForAnimationLevel);
917   if (!commonProperties.IsEmpty()) {
918     EffectCompositor::RestyleType restyleType =
919         compositorPropertiesInSet(changedPropertiesForAnimationLevel).none()
920             ? EffectCompositor::RestyleType::Standard
921             : EffectCompositor::RestyleType::Layer;
922     presContext->EffectCompositor()->RequestRestyle(
923         aElement, aPseudoType, restyleType,
924         EffectCompositor::CascadeLevel::Transitions);
925   }
926 }
927 
SetPerformanceWarning(const nsIFrame * aFrame,nsCSSPropertyID aProperty,const AnimationPerformanceWarning & aWarning)928 /* static */ void EffectCompositor::SetPerformanceWarning(
929     const nsIFrame* aFrame, nsCSSPropertyID aProperty,
930     const AnimationPerformanceWarning& aWarning) {
931   EffectSet* effects = EffectSet::GetEffectSet(aFrame);
932   if (!effects) {
933     return;
934   }
935 
936   for (KeyframeEffectReadOnly* effect : *effects) {
937     effect->SetPerformanceWarning(aProperty, aWarning);
938   }
939 }
940 
PreTraverse(ServoTraversalFlags aFlags)941 bool EffectCompositor::PreTraverse(ServoTraversalFlags aFlags) {
942   return PreTraverseInSubtree(aFlags, nullptr);
943 }
944 
PreTraverseInSubtree(ServoTraversalFlags aFlags,Element * aRoot)945 bool EffectCompositor::PreTraverseInSubtree(ServoTraversalFlags aFlags,
946                                             Element* aRoot) {
947   MOZ_ASSERT(NS_IsMainThread());
948   MOZ_ASSERT(mPresContext->RestyleManager()->IsServo());
949   MOZ_ASSERT(!aRoot || nsContentUtils::GetPresShellForContent(aRoot),
950              "Traversal root, if provided, should be bound to a display "
951              "document");
952 
953   // Convert the root element to the parent element if the root element is
954   // pseudo since we check each element in mElementsToRestyle is in the subtree
955   // of the root element later in this function, but for pseudo elements the
956   // element in mElementsToRestyle is the parent of the pseudo.
957   if (aRoot && (aRoot->IsGeneratedContentContainerForBefore() ||
958                 aRoot->IsGeneratedContentContainerForAfter())) {
959     aRoot = aRoot->GetParentElement();
960   }
961 
962   AutoRestore<bool> guard(mIsInPreTraverse);
963   mIsInPreTraverse = true;
964 
965   // We need to force flush all throttled animations if we also have
966   // non-animation restyles (since we'll want the up-to-date animation style
967   // when we go to process them so we can trigger transitions correctly), and
968   // if we are currently flushing all throttled animation restyles.
969   bool flushThrottledRestyles =
970       (aRoot && aRoot->HasDirtyDescendantsForServo()) ||
971       (aFlags & ServoTraversalFlags::FlushThrottledAnimations);
972 
973   using ElementsToRestyleIterType =
974       nsDataHashtable<PseudoElementHashEntry, bool>::Iterator;
975   auto getNeededRestyleTarget =
976       [&](const ElementsToRestyleIterType& aIter) -> NonOwningAnimationTarget {
977     NonOwningAnimationTarget returnTarget;
978 
979     // If aIter.Data() is false, the element only requested a throttled
980     // (skippable) restyle, so we can skip it if flushThrottledRestyles is not
981     // true.
982     if (!flushThrottledRestyles && !aIter.Data()) {
983       return returnTarget;
984     }
985 
986     const NonOwningAnimationTarget& target = aIter.Key();
987 
988     // Skip elements in documents without a pres shell. Normally we filter out
989     // such elements in RequestRestyle but it can happen that, after adding
990     // them to mElementsToRestyle, they are transferred to a different document.
991     //
992     // We will drop them from mElementsToRestyle at the end of the next full
993     // document restyle (at the end of this function) but for consistency with
994     // how we treat such elements in RequestRestyle, we just ignore them here.
995     if (!nsContentUtils::GetPresShellForContent(target.mElement)) {
996       return returnTarget;
997     }
998 
999     // Ignore restyles that aren't in the flattened tree subtree rooted at
1000     // aRoot.
1001     if (aRoot && !nsContentUtils::ContentIsFlattenedTreeDescendantOfForStyle(
1002                      target.mElement, aRoot)) {
1003       return returnTarget;
1004     }
1005 
1006     returnTarget = target;
1007     return returnTarget;
1008   };
1009 
1010   bool foundElementsNeedingRestyle = false;
1011 
1012   nsTArray<NonOwningAnimationTarget> elementsWithCascadeUpdates;
1013   for (size_t i = 0; i < kCascadeLevelCount; ++i) {
1014     CascadeLevel cascadeLevel = CascadeLevel(i);
1015     auto& elementSet = mElementsToRestyle[cascadeLevel];
1016     for (auto iter = elementSet.Iter(); !iter.Done(); iter.Next()) {
1017       const NonOwningAnimationTarget& target = getNeededRestyleTarget(iter);
1018       if (!target.mElement) {
1019         continue;
1020       }
1021 
1022       EffectSet* effects =
1023           EffectSet::GetEffectSet(target.mElement, target.mPseudoType);
1024       if (!effects || !effects->CascadeNeedsUpdate()) {
1025         continue;
1026       }
1027 
1028       elementsWithCascadeUpdates.AppendElement(target);
1029     }
1030   }
1031 
1032   for (const NonOwningAnimationTarget& target : elementsWithCascadeUpdates) {
1033     MaybeUpdateCascadeResults(StyleBackendType::Servo, target.mElement,
1034                               target.mPseudoType, nullptr);
1035   }
1036   elementsWithCascadeUpdates.Clear();
1037 
1038   for (size_t i = 0; i < kCascadeLevelCount; ++i) {
1039     CascadeLevel cascadeLevel = CascadeLevel(i);
1040     auto& elementSet = mElementsToRestyle[cascadeLevel];
1041     for (auto iter = elementSet.Iter(); !iter.Done(); iter.Next()) {
1042       const NonOwningAnimationTarget& target = getNeededRestyleTarget(iter);
1043       if (!target.mElement) {
1044         continue;
1045       }
1046 
1047       // We need to post restyle hints even if the target is not in EffectSet to
1048       // ensure the final restyling for removed animations.
1049       // We can't call PostRestyleEvent directly here since we are still in the
1050       // middle of the servo traversal.
1051       mPresContext->RestyleManager()->AsServo()->PostRestyleEventForAnimations(
1052           target.mElement, target.mPseudoType,
1053           cascadeLevel == CascadeLevel::Transitions ? eRestyle_CSSTransitions
1054                                                     : eRestyle_CSSAnimations);
1055 
1056       foundElementsNeedingRestyle = true;
1057 
1058       EffectSet* effects =
1059           EffectSet::GetEffectSet(target.mElement, target.mPseudoType);
1060       if (!effects) {
1061         // Drop EffectSets that have been destroyed.
1062         iter.Remove();
1063         continue;
1064       }
1065 
1066       for (KeyframeEffectReadOnly* effect : *effects) {
1067         effect->GetAnimation()->WillComposeStyle();
1068       }
1069 
1070       // Remove the element from the list of elements to restyle since we are
1071       // about to restyle it.
1072       iter.Remove();
1073     }
1074 
1075     // If this is a full document restyle, then unconditionally clear
1076     // elementSet in case there are any elements that didn't match above
1077     // because they were moved to a document without a pres shell after
1078     // posting an animation restyle.
1079     if (!aRoot && flushThrottledRestyles) {
1080       elementSet.Clear();
1081     }
1082   }
1083 
1084   return foundElementsNeedingRestyle;
1085 }
1086 
PreTraverse(dom::Element * aElement,CSSPseudoElementType aPseudoType)1087 bool EffectCompositor::PreTraverse(dom::Element* aElement,
1088                                    CSSPseudoElementType aPseudoType) {
1089   MOZ_ASSERT(NS_IsMainThread());
1090   MOZ_ASSERT(mPresContext->RestyleManager()->IsServo());
1091 
1092   // If |aElement|'s document does not have a pres shell, e.g. it is document
1093   // without a browsing context such as we might get from an XMLHttpRequest, we
1094   // should not run animations on it.
1095   if (!nsContentUtils::GetPresShellForContent(aElement)) {
1096     return false;
1097   }
1098 
1099   bool found = false;
1100   if (aPseudoType != CSSPseudoElementType::NotPseudo &&
1101       aPseudoType != CSSPseudoElementType::before &&
1102       aPseudoType != CSSPseudoElementType::after) {
1103     return found;
1104   }
1105 
1106   AutoRestore<bool> guard(mIsInPreTraverse);
1107   mIsInPreTraverse = true;
1108 
1109   PseudoElementHashEntry::KeyType key = {aElement, aPseudoType};
1110 
1111   // We need to flush all throttled animation restyles too if we also have
1112   // non-animation restyles (since we'll want the up-to-date animation style
1113   // when we go to process them so we can trigger transitions correctly).
1114   Element* elementToRestyle = GetElementToRestyle(aElement, aPseudoType);
1115   bool flushThrottledRestyles =
1116       elementToRestyle && elementToRestyle->HasDirtyDescendantsForServo();
1117 
1118   for (size_t i = 0; i < kCascadeLevelCount; ++i) {
1119     CascadeLevel cascadeLevel = CascadeLevel(i);
1120     auto& elementSet = mElementsToRestyle[cascadeLevel];
1121 
1122     // Skip if we don't have a restyle, or if we only have a throttled
1123     // (skippable) restyle and we're not required to flush throttled restyles.
1124     bool hasUnthrottledRestyle = false;
1125     if (!elementSet.Get(key, &hasUnthrottledRestyle) ||
1126         (!flushThrottledRestyles && !hasUnthrottledRestyle)) {
1127       continue;
1128     }
1129 
1130     mPresContext->RestyleManager()->AsServo()->PostRestyleEventForAnimations(
1131         aElement, aPseudoType,
1132         cascadeLevel == CascadeLevel::Transitions ? eRestyle_CSSTransitions
1133                                                   : eRestyle_CSSAnimations);
1134 
1135     EffectSet* effects = EffectSet::GetEffectSet(aElement, aPseudoType);
1136     if (effects) {
1137       MaybeUpdateCascadeResults(StyleBackendType::Servo, aElement, aPseudoType,
1138                                 nullptr);
1139 
1140       for (KeyframeEffectReadOnly* effect : *effects) {
1141         effect->GetAnimation()->WillComposeStyle();
1142       }
1143     }
1144 
1145     elementSet.Remove(key);
1146     found = true;
1147   }
1148   return found;
1149 }
1150 
1151 #ifdef MOZ_OLD_STYLE
1152 // ---------------------------------------------------------
1153 //
1154 // Nested class: AnimationStyleRuleProcessor
1155 //
1156 // ---------------------------------------------------------
1157 
NS_IMPL_ISUPPORTS(EffectCompositor::AnimationStyleRuleProcessor,nsIStyleRuleProcessor)1158 NS_IMPL_ISUPPORTS(EffectCompositor::AnimationStyleRuleProcessor,
1159                   nsIStyleRuleProcessor)
1160 
1161 nsRestyleHint
1162 EffectCompositor::AnimationStyleRuleProcessor::HasStateDependentStyle(
1163     StateRuleProcessorData* aData) {
1164   return nsRestyleHint(0);
1165 }
1166 
1167 nsRestyleHint
HasStateDependentStyle(PseudoElementStateRuleProcessorData * aData)1168 EffectCompositor::AnimationStyleRuleProcessor::HasStateDependentStyle(
1169     PseudoElementStateRuleProcessorData* aData) {
1170   return nsRestyleHint(0);
1171 }
1172 
1173 bool EffectCompositor::AnimationStyleRuleProcessor::
HasDocumentStateDependentStyle(StateRuleProcessorData * aData)1174     HasDocumentStateDependentStyle(StateRuleProcessorData* aData) {
1175   return false;
1176 }
1177 
1178 nsRestyleHint
HasAttributeDependentStyle(AttributeRuleProcessorData * aData,RestyleHintData & aRestyleHintDataResult)1179 EffectCompositor::AnimationStyleRuleProcessor::HasAttributeDependentStyle(
1180     AttributeRuleProcessorData* aData,
1181     RestyleHintData& aRestyleHintDataResult) {
1182   return nsRestyleHint(0);
1183 }
1184 
MediumFeaturesChanged(nsPresContext * aPresContext)1185 bool EffectCompositor::AnimationStyleRuleProcessor::MediumFeaturesChanged(
1186     nsPresContext* aPresContext) {
1187   return false;
1188 }
1189 
RulesMatching(ElementRuleProcessorData * aData)1190 void EffectCompositor::AnimationStyleRuleProcessor::RulesMatching(
1191     ElementRuleProcessorData* aData) {
1192   nsIStyleRule* rule = mCompositor->GetAnimationRule(
1193       aData->mElement, CSSPseudoElementType::NotPseudo, mCascadeLevel, nullptr);
1194   if (rule) {
1195     aData->mRuleWalker->Forward(rule);
1196     aData->mRuleWalker->CurrentNode()->SetIsAnimationRule();
1197   }
1198 }
1199 
RulesMatching(PseudoElementRuleProcessorData * aData)1200 void EffectCompositor::AnimationStyleRuleProcessor::RulesMatching(
1201     PseudoElementRuleProcessorData* aData) {
1202   if (aData->mPseudoType != CSSPseudoElementType::before &&
1203       aData->mPseudoType != CSSPseudoElementType::after) {
1204     return;
1205   }
1206 
1207   nsIStyleRule* rule = mCompositor->GetAnimationRule(
1208       aData->mElement, aData->mPseudoType, mCascadeLevel, nullptr);
1209   if (rule) {
1210     aData->mRuleWalker->Forward(rule);
1211     aData->mRuleWalker->CurrentNode()->SetIsAnimationRule();
1212   }
1213 }
1214 
RulesMatching(AnonBoxRuleProcessorData * aData)1215 void EffectCompositor::AnimationStyleRuleProcessor::RulesMatching(
1216     AnonBoxRuleProcessorData* aData) {}
1217 
1218 #ifdef MOZ_XUL
RulesMatching(XULTreeRuleProcessorData * aData)1219 void EffectCompositor::AnimationStyleRuleProcessor::RulesMatching(
1220     XULTreeRuleProcessorData* aData) {}
1221 #endif
1222 
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const1223 size_t EffectCompositor::AnimationStyleRuleProcessor::SizeOfExcludingThis(
1224     MallocSizeOf aMallocSizeOf) const {
1225   return 0;
1226 }
1227 
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const1228 size_t EffectCompositor::AnimationStyleRuleProcessor::SizeOfIncludingThis(
1229     MallocSizeOf aMallocSizeOf) const {
1230   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
1231 }
1232 
1233 template void EffectCompositor::UpdateEffectProperties(
1234     GeckoStyleContext* aStyleContext, Element* aElement,
1235     CSSPseudoElementType aPseudoType);
1236 #endif
1237 
1238 template void EffectCompositor::UpdateEffectProperties(
1239     const ServoStyleContext* aStyleContext, Element* aElement,
1240     CSSPseudoElementType aPseudoType);
1241 
1242 }  // namespace mozilla
1243