Backed out 9 changesets (bug 1846848) for causing multiple build bustages. CLOSED...
[gecko.git] / dom / animation / ScrollTimeline.h
blob0984845dc8f28279c1ceb47b154baaae882e973d
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;
21 namespace mozilla {
22 class ElementAnimationData;
23 struct NonOwningAnimationTarget;
24 namespace dom {
25 class Element;
27 /**
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.
44 * -------------
45 * | Animation |
46 * -------------
47 * ^
48 * | Call Animation::Tick() if there are any scroll updates.
49 * |
50 * ------------------
51 * | ScrollTimeline |
52 * ------------------
53 * ^
54 * | Try schedule the scroll-driven animations, if there are any scroll
55 * | offsets changed or the scroll range changed [1].
56 * |
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);
69 public:
70 struct Scroller {
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 {
74 Root,
75 Nearest,
76 Name,
77 Self,
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
90 // element.
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.
135 return nullptr;
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
144 // nullptr.
145 return nullptr;
147 TimeStamp ToTimeStamp(const TimeDuration& aTimelineTime) const override {
148 // It's unclear to us what should we do for this function now, so return
149 // zero time.
150 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);
168 TickState state;
169 Tick(state);
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 {
183 MOZ_ASSERT(mSource);
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);
200 protected:
201 virtual ~ScrollTimeline() { Teardown(); }
202 ScrollTimeline() = delete;
203 ScrollTimeline(Document* aDocument, const Scroller& aScroller,
204 StyleScrollAxis aAxis);
206 struct ScrollOffsets {
207 nscoord mStart = 0;
208 nscoord mEnd = 0;
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
234 // nearest scroller.
235 Scroller mSource;
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 {
247 public:
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();
272 private:
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;
279 } // namespace dom
280 } // namespace mozilla
282 #endif // mozilla_dom_ScrollTimeline_h