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