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