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 : #ifndef mozilla_EffectCompositor_h
8 : #define mozilla_EffectCompositor_h
9 :
10 : #include "mozilla/EnumeratedArray.h"
11 : #include "mozilla/Maybe.h"
12 : #include "mozilla/OwningNonNull.h"
13 : #include "mozilla/PseudoElementHashEntry.h"
14 : #include "mozilla/RefPtr.h"
15 : #include "nsCSSPropertyID.h"
16 : #include "nsCycleCollectionParticipant.h"
17 : #include "nsDataHashtable.h"
18 : #include "nsIStyleRuleProcessor.h"
19 : #include "nsTArray.h"
20 :
21 : class nsCSSPropertyIDSet;
22 : class nsIAtom;
23 : class nsIFrame;
24 : class nsIStyleRule;
25 : class nsPresContext;
26 : class nsStyleContext;
27 : struct RawServoAnimationValueMap;
28 : typedef RawServoAnimationValueMap* RawServoAnimationValueMapBorrowedMut;
29 :
30 : namespace mozilla {
31 :
32 : class EffectSet;
33 : class RestyleTracker;
34 : class StyleAnimationValue;
35 : struct AnimationPerformanceWarning;
36 : struct AnimationProperty;
37 : struct NonOwningAnimationTarget;
38 :
39 : namespace dom {
40 : class Animation;
41 : class Element;
42 : }
43 :
44 : class EffectCompositor
45 : {
46 : public:
47 28 : explicit EffectCompositor(nsPresContext* aPresContext)
48 28 : : mPresContext(aPresContext)
49 : {
50 84 : for (size_t i = 0; i < kCascadeLevelCount; i++) {
51 56 : CascadeLevel cascadeLevel = CascadeLevel(i);
52 56 : mRuleProcessors[cascadeLevel] =
53 112 : new AnimationStyleRuleProcessor(this, cascadeLevel);
54 : }
55 28 : }
56 :
57 32 : NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(EffectCompositor)
58 41 : NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(EffectCompositor)
59 :
60 4 : void Disconnect() {
61 4 : mPresContext = nullptr;
62 4 : }
63 :
64 : // Animations can be applied at two different levels in the CSS cascade:
65 : enum class CascadeLevel : uint32_t {
66 : // The animations sheet (CSS animations, script-generated animations,
67 : // and CSS transitions that are no longer tied to CSS markup)
68 : Animations = 0,
69 : // The transitions sheet (CSS transitions that are tied to CSS markup)
70 : Transitions = 1
71 : };
72 : // We don't define this as part of CascadeLevel as then we'd have to add
73 : // explicit checks for the Count enum value everywhere CascadeLevel is used.
74 : static const size_t kCascadeLevelCount =
75 : static_cast<size_t>(CascadeLevel::Transitions) + 1;
76 :
77 : // NOTE: This can return null after Disconnect().
78 : nsPresContext* PresContext() const { return mPresContext; }
79 :
80 : enum class RestyleType {
81 : // Animation style has changed but the compositor is applying the same
82 : // change so we might be able to defer updating the main thread until it
83 : // becomes necessary.
84 : Throttled,
85 : // Animation style has changed and needs to be updated on the main thread.
86 : Standard,
87 : // Animation style has changed and needs to be updated on the main thread
88 : // as well as forcing animations on layers to be updated.
89 : // This is needed in cases such as when an animation becomes paused or has
90 : // its playback rate changed. In such cases, although the computed style
91 : // and refresh driver time might not change, we still need to ensure the
92 : // corresponding animations on layers are updated to reflect the new
93 : // configuration of the animation.
94 : Layer
95 : };
96 :
97 : // Notifies the compositor that the animation rule for the specified
98 : // (pseudo-)element at the specified cascade level needs to be updated.
99 : // The specified steps taken to update the animation rule depend on
100 : // |aRestyleType| whose values are described above.
101 : void RequestRestyle(dom::Element* aElement,
102 : CSSPseudoElementType aPseudoType,
103 : RestyleType aRestyleType,
104 : CascadeLevel aCascadeLevel);
105 :
106 : // Schedule an animation restyle. This is called automatically by
107 : // RequestRestyle when necessary. However, it is exposed here since we also
108 : // need to perform this step when triggering transitions *without* also
109 : // invalidating the animation style rule (which RequestRestyle would do).
110 : void PostRestyleForAnimation(dom::Element* aElement,
111 : CSSPseudoElementType aPseudoType,
112 : CascadeLevel aCascadeLevel);
113 :
114 : // Posts an animation restyle for any elements whose animation style rule
115 : // is out of date but for which an animation restyle has not yet been
116 : // posted because updates on the main thread are throttled.
117 : void PostRestyleForThrottledAnimations();
118 :
119 : // Called when computed style on the specified (pseudo-) element might
120 : // have changed so that any context-sensitive values stored within
121 : // animation effects (e.g. em-based endpoints used in keyframe effects)
122 : // can be re-resolved to computed values.
123 : template<typename StyleType>
124 : void UpdateEffectProperties(StyleType* aStyleType,
125 : dom::Element* aElement,
126 : CSSPseudoElementType aPseudoType);
127 :
128 : // Updates the animation rule stored on the EffectSet for the
129 : // specified (pseudo-)element for cascade level |aLevel|.
130 : // If the animation rule is not marked as needing an update,
131 : // no work is done.
132 : // |aStyleContext| is used for UpdateCascadingResults.
133 : // |aStyleContext| can be nullptr if style context, which is associated with
134 : // the primary frame of the specified (pseudo-)element, is the current style
135 : // context.
136 : // If we are resolving a new style context, we shoud pass the newly created
137 : // style context, otherwise we may use an old style context, it will result
138 : // unexpected cascading results.
139 : void MaybeUpdateAnimationRule(dom::Element* aElement,
140 : CSSPseudoElementType aPseudoType,
141 : CascadeLevel aCascadeLevel,
142 : nsStyleContext *aStyleContext);
143 :
144 : // We need to pass the newly resolved style context as |aStyleContext| when
145 : // we call this function during resolving style context because this function
146 : // calls UpdateCascadingResults with a style context if necessary, at the
147 : // time, we end up using the previous style context if we don't pass the new
148 : // style context.
149 : // When we are not resolving style context, |aStyleContext| can be nullptr, we
150 : // will use a style context associated with the primary frame of the specified
151 : // (pseudo-)element.
152 : nsIStyleRule* GetAnimationRule(dom::Element* aElement,
153 : CSSPseudoElementType aPseudoType,
154 : CascadeLevel aCascadeLevel,
155 : nsStyleContext* aStyleContext);
156 :
157 : // Get animation rule for stylo. This is an equivalent of GetAnimationRule
158 : // and will be called from servo side.
159 : // The animation rule is stored in |RawServoAnimationValueMapBorrowed|.
160 : // We need to be careful while doing any modification because it may cause
161 : // some thread-safe issues.
162 : bool GetServoAnimationRule(
163 : const dom::Element* aElement,
164 : CSSPseudoElementType aPseudoType,
165 : CascadeLevel aCascadeLevel,
166 : RawServoAnimationValueMapBorrowedMut aAnimationValues);
167 :
168 : bool HasPendingStyleUpdates() const;
169 : bool HasThrottledStyleUpdates() const;
170 :
171 : // Tell the restyle tracker about all the animated styles that have
172 : // pending updates so that it can update the animation rule for these
173 : // elements.
174 : void AddStyleUpdatesTo(RestyleTracker& aTracker);
175 :
176 56 : nsIStyleRuleProcessor* RuleProcessor(CascadeLevel aCascadeLevel) const
177 : {
178 56 : return mRuleProcessors[aCascadeLevel];
179 : }
180 :
181 : static bool HasAnimationsForCompositor(const nsIFrame* aFrame,
182 : nsCSSPropertyID aProperty);
183 :
184 : static nsTArray<RefPtr<dom::Animation>>
185 : GetAnimationsForCompositor(const nsIFrame* aFrame,
186 : nsCSSPropertyID aProperty);
187 :
188 : static void ClearIsRunningOnCompositor(const nsIFrame* aFrame,
189 : nsCSSPropertyID aProperty);
190 :
191 : // Update animation cascade results for the specified (pseudo-)element
192 : // but only if we have marked the cascade as needing an update due a
193 : // the change in the set of effects or a change in one of the effects'
194 : // "in effect" state.
195 : //
196 : // When |aBackendType| is StyleBackendType::Gecko, |aStyleContext| is used to
197 : // find overridden properties. If it is nullptr, the nsStyleContext of the
198 : // primary frame of the specified (pseudo-)element, if available, is used.
199 : //
200 : // When |aBackendType| is StyleBackendType::Servo, we fetch the rule node
201 : // from the |aElement| (i.e. |aStyleContext| is ignored).
202 : //
203 : // This method does NOT detect if other styles that apply above the
204 : // animation level of the cascade have changed.
205 : static void
206 : MaybeUpdateCascadeResults(StyleBackendType aBackendType,
207 : dom::Element* aElement,
208 : CSSPseudoElementType aPseudoType,
209 : nsStyleContext* aStyleContext);
210 :
211 : // Helper to fetch the corresponding element and pseudo-type from a frame.
212 : //
213 : // For frames corresponding to pseudo-elements, the returned element is the
214 : // element on which we store the animations (i.e. the EffectSet and/or
215 : // AnimationCollection), *not* the generated content.
216 : //
217 : // Returns an empty result when a suitable element cannot be found including
218 : // when the frame represents a pseudo-element on which we do not support
219 : // animations.
220 : static Maybe<NonOwningAnimationTarget>
221 : GetAnimationElementAndPseudoForFrame(const nsIFrame* aFrame);
222 :
223 : // Associates a performance warning with effects on |aFrame| that animates
224 : // |aProperty|.
225 : static void SetPerformanceWarning(
226 : const nsIFrame* aFrame,
227 : nsCSSPropertyID aProperty,
228 : const AnimationPerformanceWarning& aWarning);
229 :
230 : // The type which represents what kind of animation restyle we want.
231 : enum class AnimationRestyleType {
232 : Throttled, // Restyle elements that have posted animation restyles.
233 : Full // Restyle all elements with animations (i.e. even if the
234 : // animations are throttled).
235 : };
236 :
237 : // Do a bunch of stuff that we should avoid doing during the parallel
238 : // traversal (e.g. changing member variables) for all elements that we expect
239 : // to restyle on the next traversal.
240 : //
241 : // Returns true if there are elements needing a restyle for animation.
242 : bool PreTraverse(AnimationRestyleType aRestyleType);
243 :
244 : // Similar to the above but only for the (pseudo-)element.
245 : bool PreTraverse(dom::Element* aElement, CSSPseudoElementType aPseudoType);
246 :
247 : // Similar to the above but for all elements in the subtree rooted
248 : // at aElement.
249 : bool PreTraverseInSubtree(dom::Element* aElement,
250 : AnimationRestyleType aRestyleType);
251 :
252 : // Returns the target element for restyling.
253 : //
254 : // If |aPseudoType| is ::after or ::before, returns the generated content
255 : // element of which |aElement| is the parent. If |aPseudoType| is any other
256 : // pseudo type (other thant CSSPseudoElementType::NotPseudo) returns nullptr.
257 : // Otherwise, returns |aElement|.
258 : static dom::Element* GetElementToRestyle(dom::Element* aElement,
259 : CSSPseudoElementType aPseudoType);
260 :
261 : private:
262 3 : ~EffectCompositor() = default;
263 :
264 : // Rebuilds the animation rule corresponding to |aCascadeLevel| on the
265 : // EffectSet associated with the specified (pseudo-)element.
266 : static void ComposeAnimationRule(dom::Element* aElement,
267 : CSSPseudoElementType aPseudoType,
268 : CascadeLevel aCascadeLevel);
269 :
270 : // Get the properties in |aEffectSet| that we are able to animate on the
271 : // compositor but which are also specified at a higher level in the cascade
272 : // than the animations level.
273 : //
274 : // When |aBackendType| is StyleBackendType::Gecko, we determine which
275 : // properties are specified using the provided |aStyleContext| and
276 : // |aElement| and |aPseudoType| are ignored. If |aStyleContext| is nullptr,
277 : // we automatically look up the style context of primary frame of the
278 : // (pseudo-)element.
279 : //
280 : // When |aBackendType| is StyleBackendType::Servo, we use the |StrongRuleNode|
281 : // stored on the (pseudo-)element indicated by |aElement| and |aPseudoType|.
282 : static nsCSSPropertyIDSet
283 : GetOverriddenProperties(StyleBackendType aBackendType,
284 : EffectSet& aEffectSet,
285 : dom::Element* aElement,
286 : CSSPseudoElementType aPseudoType,
287 : nsStyleContext* aStyleContext);
288 :
289 : // Update the mPropertiesWithImportantRules and
290 : // mPropertiesForAnimationsLevel members of the given EffectSet.
291 : //
292 : // This can be expensive so we should only call it if styles that apply
293 : // above the animation level of the cascade might have changed. For all
294 : // other cases we should call MaybeUpdateCascadeResults.
295 : //
296 : // As with MaybeUpdateCascadeResults, |aStyleContext| is only used
297 : // when |aBackendType| is StyleBackendType::Gecko. When |aBackendType| is
298 : // StyleBackendType::Servo, it is ignored.
299 : static void
300 : UpdateCascadeResults(StyleBackendType aBackendType,
301 : EffectSet& aEffectSet,
302 : dom::Element* aElement,
303 : CSSPseudoElementType aPseudoType,
304 : nsStyleContext* aStyleContext);
305 :
306 : static nsPresContext* GetPresContext(dom::Element* aElement);
307 :
308 : nsPresContext* mPresContext;
309 :
310 : // Elements with a pending animation restyle. The associated bool value is
311 : // true if a pending animation restyle has also been dispatched. For
312 : // animations that can be throttled, we will add an entry to the hashtable to
313 : // indicate that the style rule on the element is out of date but without
314 : // posting a restyle to update it.
315 : EnumeratedArray<CascadeLevel, CascadeLevel(kCascadeLevelCount),
316 : nsDataHashtable<PseudoElementHashEntry, bool>>
317 : mElementsToRestyle;
318 :
319 : bool mIsInPreTraverse = false;
320 :
321 : class AnimationStyleRuleProcessor final : public nsIStyleRuleProcessor
322 : {
323 : public:
324 56 : AnimationStyleRuleProcessor(EffectCompositor* aCompositor,
325 : CascadeLevel aCascadeLevel)
326 56 : : mCompositor(aCompositor)
327 56 : , mCascadeLevel(aCascadeLevel)
328 : {
329 56 : MOZ_ASSERT(aCompositor);
330 56 : }
331 :
332 : NS_DECL_ISUPPORTS
333 :
334 : // nsIStyleRuleProcessor (parts)
335 : nsRestyleHint HasStateDependentStyle(
336 : StateRuleProcessorData* aData) override;
337 : nsRestyleHint HasStateDependentStyle(
338 : PseudoElementStateRuleProcessorData* aData) override;
339 : bool HasDocumentStateDependentStyle(StateRuleProcessorData* aData) override;
340 : nsRestyleHint HasAttributeDependentStyle(
341 : AttributeRuleProcessorData* aData,
342 : RestyleHintData& aRestyleHintDataResult) override;
343 : bool MediumFeaturesChanged(nsPresContext* aPresContext) override;
344 : void RulesMatching(ElementRuleProcessorData* aData) override;
345 : void RulesMatching(PseudoElementRuleProcessorData* aData) override;
346 : void RulesMatching(AnonBoxRuleProcessorData* aData) override;
347 : #ifdef MOZ_XUL
348 : void RulesMatching(XULTreeRuleProcessorData* aData) override;
349 : #endif
350 : size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)
351 : const MOZ_MUST_OVERRIDE override;
352 : size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
353 : const MOZ_MUST_OVERRIDE override;
354 :
355 : private:
356 : ~AnimationStyleRuleProcessor() = default;
357 :
358 : EffectCompositor* mCompositor;
359 : CascadeLevel mCascadeLevel;
360 : };
361 :
362 : EnumeratedArray<CascadeLevel, CascadeLevel(kCascadeLevelCount),
363 : OwningNonNull<AnimationStyleRuleProcessor>>
364 : mRuleProcessors;
365 : };
366 :
367 : } // namespace mozilla
368 :
369 : #endif // mozilla_EffectCompositor_h
|