ScrollTimeline.h (8615B)
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/. */ 6 7 #ifndef mozilla_dom_ScrollTimeline_h 8 #define mozilla_dom_ScrollTimeline_h 9 10 #include "mozilla/LinkedList.h" 11 #include "mozilla/ServoStyleConsts.h" 12 #include "mozilla/WritingModes.h" 13 #include "mozilla/dom/AnimationTimeline.h" 14 15 #define PROGRESS_TIMELINE_DURATION_MILLISEC 100000 16 17 namespace mozilla { 18 class ScrollContainerFrame; 19 class ElementAnimationData; 20 struct NonOwningAnimationTarget; 21 namespace dom { 22 class Document; 23 class Element; 24 25 /** 26 * Implementation notes 27 * -------------------- 28 * 29 * ScrollTimelines do not observe refreshes the way DocumentTimelines do. 30 * This is because the refresh driver keeps ticking while it has registered 31 * refresh observers. For a DocumentTimeline, it's appropriate to keep the 32 * refresh driver ticking as long as there are active animations, since the 33 * animations need to be sampled on every frame. Scroll-linked animations, 34 * however, only need to be sampled when scrolling has occurred, so keeping 35 * the refresh driver ticking is wasteful. 36 * 37 * As a result, we schedule an animation restyle when 38 * 1) there are any scroll offsets updated (from APZ or script), via 39 * ScrollContainerFrame, or 40 * 2) there are any possible scroll range updated during the frame reflow. 41 * 42 * ------------- 43 * | Animation | 44 * ------------- 45 * ^ 46 * | Call Animation::Tick() if there are any scroll updates. 47 * | 48 * ------------------ 49 * | ScrollTimeline | 50 * ------------------ 51 * ^ 52 * | Try schedule the scroll-driven animations, if there are any scroll 53 * | offsets changed or the scroll range changed [1]. 54 * | 55 * ------------------------ 56 * | ScrollContainerFrame | 57 * ------------------------ 58 * 59 * [1] ScrollContainerFrame uses its associated dom::Element to lookup the 60 * ScrollTimelineSet, and iterates the set to schedule the animations 61 * linked to the ScrollTimelines. 62 */ 63 class ScrollTimeline : public AnimationTimeline, 64 public LinkedListElement<ScrollTimeline> { 65 template <typename T, typename... Args> 66 friend already_AddRefed<T> mozilla::MakeAndAddRef(Args&&... aArgs); 67 68 public: 69 struct Scroller { 70 // FIXME: Bug 1765211. Perhaps we only need root and a specific element. 71 // This depends on how we fix this bug. 72 enum class Type : uint8_t { 73 Root, 74 Nearest, 75 Name, 76 Self, 77 }; 78 Type mType = Type::Root; 79 RefPtr<Element> mElement; 80 // FIXME: Bug 1928437. We have to update mPseudoType to use 81 // PseudoStyleRequest. 82 PseudoStyleType mPseudoType; 83 84 static Scroller Root(Element* aDocumentElement) { 85 return {Type::Root, aDocumentElement, PseudoStyleType::NotPseudo}; 86 } 87 88 static Scroller Nearest(Element* aElement, PseudoStyleType aPseudoType) { 89 return {Type::Nearest, aElement, aPseudoType}; 90 } 91 92 static Scroller Named(Element* aElement, PseudoStyleType aPseudoType) { 93 return {Type::Name, aElement, aPseudoType}; 94 } 95 96 static Scroller Self(Element* aElement, PseudoStyleType aPseudoType) { 97 return {Type::Self, aElement, aPseudoType}; 98 } 99 100 explicit operator bool() const { return mElement; } 101 bool operator==(const Scroller& aOther) const { 102 return mType == aOther.mType && mElement == aOther.mElement && 103 mPseudoType == aOther.mPseudoType; 104 } 105 }; 106 107 static already_AddRefed<ScrollTimeline> MakeAnonymous( 108 Document* aDocument, const NonOwningAnimationTarget& aTarget, 109 StyleScrollAxis aAxis, StyleScroller aScroller); 110 111 // Note: |aReferfenceElement| is used as the scroller which specifies 112 // scroll-timeline-name property. 113 static already_AddRefed<ScrollTimeline> MakeNamed( 114 Document* aDocument, Element* aReferenceElement, 115 const PseudoStyleRequest& aPseudoRequest, 116 const StyleScrollTimeline& aStyleTimeline); 117 118 bool operator==(const ScrollTimeline& aOther) const { 119 return mDocument == aOther.mDocument && mSource == aOther.mSource && 120 mAxis == aOther.mAxis; 121 } 122 123 NS_DECL_ISUPPORTS_INHERITED 124 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ScrollTimeline, AnimationTimeline) 125 126 JSObject* WrapObject(JSContext* aCx, 127 JS::Handle<JSObject*> aGivenProto) override { 128 // FIXME: Bug 1676794: Implement ScrollTimeline interface. 129 return nullptr; 130 } 131 132 // AnimationTimeline methods. 133 Nullable<TimeDuration> GetCurrentTimeAsDuration() const override; 134 bool TracksWallclockTime() const override { return false; } 135 Nullable<TimeDuration> ToTimelineTime( 136 const TimeStamp& aTimeStamp) const override { 137 // It's unclear to us what should we do for this function now, so return 138 // nullptr. 139 return nullptr; 140 } 141 TimeStamp ToTimeStamp(const TimeDuration& aTimelineTime) const override { 142 // It's unclear to us what should we do for this function now, so return 143 // zero time. 144 return {}; 145 } 146 Document* GetDocument() const override { return mDocument; } 147 bool IsMonotonicallyIncreasing() const override { return false; } 148 bool IsScrollTimeline() const override { return true; } 149 const ScrollTimeline* AsScrollTimeline() const override { return this; } 150 bool IsViewTimeline() const override { return false; } 151 152 Nullable<TimeDuration> TimelineDuration() const override { 153 // We are using this magic number for progress-based timeline duration 154 // because we don't support percentage for duration. 155 return TimeDuration::FromMilliseconds(PROGRESS_TIMELINE_DURATION_MILLISEC); 156 } 157 158 void WillRefresh(); 159 160 // If the source of a ScrollTimeline is an element whose principal box does 161 // not exist or is not a scroll container, then its phase is the timeline 162 // inactive phase. It is otherwise in the active phase. This returns true if 163 // the timeline is in active phase. 164 // https://drafts.csswg.org/web-animations-1/#inactive-timeline 165 // Note: This function is called only for compositor animations, so we must 166 // have the primary frame (principal box) for the source element if it exists. 167 bool IsActive() const { return GetScrollContainerFrame(); } 168 169 Element* SourceElement() const { 170 MOZ_ASSERT(mSource); 171 return mSource.mElement; 172 } 173 174 // A helper to get the physical orientation of this scroll-timeline. 175 layers::ScrollDirection Axis() const; 176 177 StyleOverflow SourceScrollStyle() const; 178 179 bool APZIsActiveForSource() const; 180 181 bool ScrollingDirectionIsAvailable() const; 182 183 void ReplacePropertiesWith(const Element* aReferenceElement, 184 const PseudoStyleRequest& aPseudoRequest, 185 const StyleScrollTimeline& aNew); 186 187 void NotifyAnimationUpdated(Animation& aAnimation) override; 188 189 void NotifyAnimationContentVisibilityChanged(Animation* aAnimation, 190 bool aIsVisible) override; 191 192 void UpdateCachedCurrentTime(); 193 194 protected: 195 virtual ~ScrollTimeline(); 196 ScrollTimeline() = delete; 197 ScrollTimeline(Document* aDocument, const Scroller& aScroller, 198 StyleScrollAxis aAxis); 199 200 struct ScrollOffsets { 201 nscoord mStart = 0; 202 nscoord mEnd = 0; 203 }; 204 virtual Maybe<ScrollOffsets> ComputeOffsets( 205 const ScrollContainerFrame* aScrollFrame, 206 layers::ScrollDirection aOrientation) const; 207 208 // Note: This function is required to be idempotent, as it can be called from 209 // both cycleCollection::Unlink() and ~ScrollTimeline(). When modifying this 210 // function, be sure to preserve this property. 211 void Teardown() { 212 if (isInList()) { 213 remove(); 214 } 215 } 216 217 const ScrollContainerFrame* GetScrollContainerFrame() const; 218 219 static std::pair<const Element*, PseudoStyleRequest> FindNearestScroller( 220 Element* aSubject, const PseudoStyleRequest& aPseudoRequest); 221 222 RefPtr<Document> mDocument; 223 224 // FIXME: Bug 1765211: We may have to update the source element once the 225 // overflow property of the scroll-container is updated when we are using 226 // nearest scroller. 227 Scroller mSource; 228 StyleScrollAxis mAxis; 229 230 struct CurrentTimeData { 231 // The position of the scroller, and this may be negative for RTL or 232 // sideways, e.g. the range of its value could be [0, -range]. The user 233 // needs to take care of that. 234 nscoord mPosition; 235 ScrollOffsets mOffsets; 236 }; 237 Maybe<CurrentTimeData> mCachedCurrentTime; 238 }; 239 240 } // namespace dom 241 } // namespace mozilla 242 243 #endif // mozilla_dom_ScrollTimeline_h