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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_dom_ScrollTimeline_h
8 #define mozilla_dom_ScrollTimeline_h
10 #include "mozilla/dom/AnimationTimeline.h"
11 #include "mozilla/dom/Document.h"
12 #include "mozilla/HashTable.h"
13 #include "mozilla/PairHash.h"
14 #include "mozilla/ServoStyleConsts.h"
15 #include "mozilla/WritingModes.h"
17 #define PROGRESS_TIMELINE_DURATION_MILLISEC 100000
19 class nsIScrollableFrame
;
22 class ElementAnimationData
;
23 struct NonOwningAnimationTarget
;
28 * Implementation notes
29 * --------------------
31 * ScrollTimelines do not observe refreshes the way DocumentTimelines do.
32 * This is because the refresh driver keeps ticking while it has registered
33 * refresh observers. For a DocumentTimeline, it's appropriate to keep the
34 * refresh driver ticking as long as there are active animations, since the
35 * animations need to be sampled on every frame. Scroll-linked animations,
36 * however, only need to be sampled when scrolling has occurred, so keeping
37 * the refresh driver ticking is wasteful.
39 * As a result, we schedule an animation restyle when
40 * 1) there are any scroll offsets updated (from APZ or script), via
41 * nsIScrollableFrame, or
42 * 2) there are any possible scroll range updated during the frame reflow.
48 * | Call Animation::Tick() if there are any scroll updates.
54 * | Try schedule the scroll-driven animations, if there are any scroll
55 * | offsets changed or the scroll range changed [1].
57 * ----------------------
58 * | nsIScrollableFrame |
59 * ----------------------
61 * [1] nsIScrollableFrame uses its associated dom::Element to lookup the
62 * ScrollTimelineSet, and iterates the set to schedule the animations
63 * linked to the ScrollTimelines.
65 class ScrollTimeline
: public AnimationTimeline
{
66 template <typename T
, typename
... Args
>
67 friend already_AddRefed
<T
> mozilla::MakeAndAddRef(Args
&&... aArgs
);
71 // FIXME: Bug 1765211. Perhaps we only need root and a specific element.
72 // This depends on how we fix this bug.
73 enum class Type
: uint8_t {
79 Type mType
= Type::Root
;
80 RefPtr
<Element
> mElement
;
81 PseudoStyleType mPseudoType
;
83 // We use the owner doc of the animation target. This may be different from
84 // |mDocument| after we implement ScrollTimeline interface for script.
85 static Scroller
Root(const Document
* aOwnerDoc
) {
86 // For auto, we use scrolling element as the default scroller.
87 // However, it's mutable, and we would like to keep things simple, so
88 // we always register the ScrollTimeline to the document element (i.e.
89 // root element) because the content of the root scroll frame is the root
91 return {Type::Root
, aOwnerDoc
->GetDocumentElement(),
92 PseudoStyleType::NotPseudo
};
95 static Scroller
Nearest(Element
* aElement
, PseudoStyleType aPseudoType
) {
96 return {Type::Nearest
, aElement
, aPseudoType
};
99 static Scroller
Named(Element
* aElement
, PseudoStyleType aPseudoType
) {
100 return {Type::Name
, aElement
, aPseudoType
};
103 static Scroller
Self(Element
* aElement
, PseudoStyleType aPseudoType
) {
104 return {Type::Self
, aElement
, aPseudoType
};
107 explicit operator bool() const { return mElement
; }
108 bool operator==(const Scroller
& aOther
) const {
109 return mType
== aOther
.mType
&& mElement
== aOther
.mElement
&&
110 mPseudoType
== aOther
.mPseudoType
;
114 static already_AddRefed
<ScrollTimeline
> MakeAnonymous(
115 Document
* aDocument
, const NonOwningAnimationTarget
& aTarget
,
116 StyleScrollAxis aAxis
, StyleScroller aScroller
);
118 // Note: |aReferfenceElement| is used as the scroller which specifies
119 // scroll-timeline-name property.
120 static already_AddRefed
<ScrollTimeline
> MakeNamed(
121 Document
* aDocument
, Element
* aReferenceElement
,
122 PseudoStyleType aPseudoType
, const StyleScrollTimeline
& aStyleTimeline
);
124 bool operator==(const ScrollTimeline
& aOther
) const {
125 return mDocument
== aOther
.mDocument
&& mSource
== aOther
.mSource
&&
126 mAxis
== aOther
.mAxis
;
129 NS_DECL_ISUPPORTS_INHERITED
130 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ScrollTimeline
, AnimationTimeline
)
132 JSObject
* WrapObject(JSContext
* aCx
,
133 JS::Handle
<JSObject
*> aGivenProto
) override
{
134 // FIXME: Bug 1676794: Implement ScrollTimeline interface.
138 // AnimationTimeline methods.
139 Nullable
<TimeDuration
> GetCurrentTimeAsDuration() const override
;
140 bool TracksWallclockTime() const override
{ return false; }
141 Nullable
<TimeDuration
> ToTimelineTime(
142 const TimeStamp
& aTimeStamp
) const override
{
143 // It's unclear to us what should we do for this function now, so return
147 TimeStamp
ToTimeStamp(const TimeDuration
& aTimelineTime
) const override
{
148 // It's unclear to us what should we do for this function now, so return
152 Document
* GetDocument() const override
{ return mDocument
; }
153 bool IsMonotonicallyIncreasing() const override
{ return false; }
154 bool IsScrollTimeline() const override
{ return true; }
155 const ScrollTimeline
* AsScrollTimeline() const override
{ return this; }
156 bool IsViewTimeline() const override
{ return false; }
158 Nullable
<TimeDuration
> TimelineDuration() const override
{
159 // We are using this magic number for progress-based timeline duration
160 // because we don't support percentage for duration.
161 return TimeDuration::FromMilliseconds(PROGRESS_TIMELINE_DURATION_MILLISEC
);
164 void ScheduleAnimations() {
165 // FIXME: Bug 1737927: Need to check the animation mutation observers for
166 // animations with scroll timelines.
167 // nsAutoAnimationMutationBatch mb(mDocument);
170 // TODO: Do we need to synchronize scroll animations?
173 // If the source of a ScrollTimeline is an element whose principal box does
174 // not exist or is not a scroll container, then its phase is the timeline
175 // inactive phase. It is otherwise in the active phase. This returns true if
176 // the timeline is in active phase.
177 // https://drafts.csswg.org/web-animations-1/#inactive-timeline
178 // Note: This function is called only for compositor animations, so we must
179 // have the primary frame (principal box) for the source element if it exists.
180 bool IsActive() const { return GetScrollFrame(); }
182 Element
* SourceElement() const {
184 return mSource
.mElement
;
187 // A helper to get the physical orientation of this scroll-timeline.
188 layers::ScrollDirection
Axis() const;
190 StyleOverflow
SourceScrollStyle() const;
192 bool APZIsActiveForSource() const;
194 bool ScrollingDirectionIsAvailable() const;
196 void ReplacePropertiesWith(const Element
* aReferenceElement
,
197 PseudoStyleType aPseudoType
,
198 const StyleScrollTimeline
& aNew
);
201 virtual ~ScrollTimeline() { Teardown(); }
202 ScrollTimeline() = delete;
203 ScrollTimeline(Document
* aDocument
, const Scroller
& aScroller
,
204 StyleScrollAxis aAxis
);
206 struct ScrollOffsets
{
210 virtual Maybe
<ScrollOffsets
> ComputeOffsets(
211 const nsIScrollableFrame
* aScrollFrame
,
212 layers::ScrollDirection aOrientation
) const;
214 // Note: This function is required to be idempotent, as it can be called from
215 // both cycleCollection::Unlink() and ~ScrollTimeline(). When modifying this
216 // function, be sure to preserve this property.
217 void Teardown() { UnregisterFromScrollSource(); }
219 // Register this scroll timeline to the element property.
220 void RegisterWithScrollSource();
222 // Unregister this scroll timeline from the element property.
223 void UnregisterFromScrollSource();
225 const nsIScrollableFrame
* GetScrollFrame() const;
227 static std::pair
<const Element
*, PseudoStyleType
> FindNearestScroller(
228 Element
* aSubject
, PseudoStyleType aPseudoType
);
230 RefPtr
<Document
> mDocument
;
232 // FIXME: Bug 1765211: We may have to update the source element once the
233 // overflow property of the scroll-container is updated when we are using
236 StyleScrollAxis mAxis
;
240 * A wrapper around a hashset of ScrollTimeline objects to handle the scheduling
241 * of scroll driven animations. This is used for all kinds of progress
242 * timelines, i.e. anonymous/named scroll timelines and anonymous/named view
243 * timelines. And this object is owned by the scroll source (See
244 * ElementAnimationData and nsGfxScrollFrame for the usage).
246 class ProgressTimelineScheduler
{
248 ProgressTimelineScheduler() { MOZ_COUNT_CTOR(ProgressTimelineScheduler
); }
249 ~ProgressTimelineScheduler() { MOZ_COUNT_DTOR(ProgressTimelineScheduler
); }
251 static ProgressTimelineScheduler
* Get(const Element
* aElement
,
252 PseudoStyleType aPseudoType
);
253 static ProgressTimelineScheduler
& Ensure(Element
* aElement
,
254 PseudoStyleType aPseudoType
);
255 static void Destroy(const Element
* aElement
, PseudoStyleType aPseudoType
);
257 void AddTimeline(ScrollTimeline
* aScrollTimeline
) {
258 Unused
<< mTimelines
.put(aScrollTimeline
);
260 void RemoveTimeline(ScrollTimeline
* aScrollTimeline
) {
261 mTimelines
.remove(aScrollTimeline
);
264 bool IsEmpty() const { return mTimelines
.empty(); }
266 void ScheduleAnimations() const {
267 for (auto iter
= mTimelines
.iter(); !iter
.done(); iter
.next()) {
268 iter
.get()->ScheduleAnimations();
273 // We let Animations own its scroll timeline or view timeline if it is
274 // anonymous. For named progress timelines, they are created and destroyed by
275 // TimelineCollection.
276 HashSet
<ScrollTimeline
*> mTimelines
;
280 } // namespace mozilla
282 #endif // mozilla_dom_ScrollTimeline_h