Bug 1834993 - Fix nursery allocatable flag for objects with foreground finalizers...
[gecko.git] / dom / animation / ScrollTimeline.cpp
blobeeb9571494a7c75ed0b8dc247e20140159649fc6
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 #include "ScrollTimeline.h"
9 #include "mozilla/dom/Animation.h"
10 #include "mozilla/dom/ElementInlines.h"
11 #include "mozilla/AnimationTarget.h"
12 #include "mozilla/DisplayPortUtils.h"
13 #include "mozilla/ElementAnimationData.h"
14 #include "mozilla/PresShell.h"
15 #include "nsIFrame.h"
16 #include "nsIScrollableFrame.h"
17 #include "nsLayoutUtils.h"
19 namespace mozilla::dom {
21 // ---------------------------------
22 // Methods of ScrollTimeline
23 // ---------------------------------
25 NS_IMPL_CYCLE_COLLECTION_CLASS(ScrollTimeline)
26 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ScrollTimeline,
27 AnimationTimeline)
28 tmp->Teardown();
29 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
30 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSource.mElement)
31 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
32 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ScrollTimeline,
33 AnimationTimeline)
34 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
35 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSource.mElement)
36 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
38 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(ScrollTimeline,
39 AnimationTimeline)
41 ScrollTimeline::ScrollTimeline(Document* aDocument, const Scroller& aScroller,
42 StyleScrollAxis aAxis)
43 : AnimationTimeline(aDocument->GetParentObject(),
44 aDocument->GetScopeObject()->GetRTPCallerType()),
45 mDocument(aDocument),
46 mSource(aScroller),
47 mAxis(aAxis) {
48 MOZ_ASSERT(aDocument);
49 RegisterWithScrollSource();
52 /* static */ std::pair<const Element*, PseudoStyleType>
53 ScrollTimeline::FindNearestScroller(Element* aSubject,
54 PseudoStyleType aPseudoType) {
55 MOZ_ASSERT(aSubject);
56 Element* subject =
57 AnimationUtils::GetElementForRestyle(aSubject, aPseudoType);
59 Element* curr = subject->GetFlattenedTreeParentElement();
60 Element* root = subject->OwnerDoc()->GetDocumentElement();
61 while (curr && curr != root) {
62 const ComputedStyle* style = Servo_Element_GetMaybeOutOfDateStyle(curr);
63 MOZ_ASSERT(style, "The ancestor should be styled.");
64 if (style->StyleDisplay()->IsScrollableOverflow()) {
65 break;
67 curr = curr->GetFlattenedTreeParentElement();
69 // If there is no scroll container, we use root.
70 if (!curr) {
71 return {root, PseudoStyleType::NotPseudo};
73 return AnimationUtils::GetElementPseudoPair(curr);
76 /* static */
77 already_AddRefed<ScrollTimeline> ScrollTimeline::MakeAnonymous(
78 Document* aDocument, const NonOwningAnimationTarget& aTarget,
79 StyleScrollAxis aAxis, StyleScroller aScroller) {
80 MOZ_ASSERT(aTarget);
81 Scroller scroller;
82 switch (aScroller) {
83 case StyleScroller::Root:
84 scroller = Scroller::Root(aTarget.mElement->OwnerDoc());
85 break;
87 case StyleScroller::Nearest: {
88 auto [element, pseudo] =
89 FindNearestScroller(aTarget.mElement, aTarget.mPseudoType);
90 scroller = Scroller::Nearest(const_cast<Element*>(element), pseudo);
91 break;
93 case StyleScroller::SelfElement:
94 scroller = Scroller::Self(aTarget.mElement, aTarget.mPseudoType);
95 break;
98 // Each use of scroll() corresponds to its own instance of ScrollTimeline in
99 // the Web Animations API, even if multiple elements use scroll() to refer to
100 // the same scroll container with the same arguments.
101 // https://drafts.csswg.org/scroll-animations-1/#scroll-notation
102 return MakeAndAddRef<ScrollTimeline>(aDocument, scroller, aAxis);
105 /* static*/ already_AddRefed<ScrollTimeline> ScrollTimeline::MakeNamed(
106 Document* aDocument, Element* aReferenceElement,
107 PseudoStyleType aPseudoType, const StyleScrollTimeline& aStyleTimeline) {
108 MOZ_ASSERT(NS_IsMainThread());
110 Scroller scroller = Scroller::Named(aReferenceElement, aPseudoType);
111 return MakeAndAddRef<ScrollTimeline>(aDocument, std::move(scroller),
112 aStyleTimeline.GetAxis());
115 Nullable<TimeDuration> ScrollTimeline::GetCurrentTimeAsDuration() const {
116 // If no layout box, this timeline is inactive.
117 if (!mSource || !mSource.mElement->GetPrimaryFrame()) {
118 return nullptr;
121 // if this is not a scroller container, this timeline is inactive.
122 const nsIScrollableFrame* scrollFrame = GetScrollFrame();
123 if (!scrollFrame) {
124 return nullptr;
127 const auto orientation = Axis();
129 // If there is no scrollable overflow, then the ScrollTimeline is inactive.
130 // https://drafts.csswg.org/scroll-animations-1/#scrolltimeline-interface
131 if (!scrollFrame->GetAvailableScrollingDirections().contains(orientation)) {
132 return nullptr;
135 const bool isHorizontal = orientation == layers::ScrollDirection::eHorizontal;
136 const nsPoint& scrollPosition = scrollFrame->GetScrollPosition();
137 const Maybe<ScrollOffsets>& offsets =
138 ComputeOffsets(scrollFrame, orientation);
139 if (!offsets) {
140 return nullptr;
143 // Note: For RTL, scrollPosition.x or scrollPosition.y may be negative,
144 // e.g. the range of its value is [0, -range], so we have to use the
145 // absolute value.
146 nscoord position =
147 std::abs(isHorizontal ? scrollPosition.x : scrollPosition.y);
148 double progress = static_cast<double>(position - offsets->mStart) /
149 static_cast<double>(offsets->mEnd - offsets->mStart);
150 return TimeDuration::FromMilliseconds(progress *
151 PROGRESS_TIMELINE_DURATION_MILLISEC);
154 layers::ScrollDirection ScrollTimeline::Axis() const {
155 MOZ_ASSERT(mSource && mSource.mElement->GetPrimaryFrame());
157 const WritingMode wm = mSource.mElement->GetPrimaryFrame()->GetWritingMode();
158 return mAxis == StyleScrollAxis::Horizontal ||
159 (!wm.IsVertical() && mAxis == StyleScrollAxis::Inline) ||
160 (wm.IsVertical() && mAxis == StyleScrollAxis::Block)
161 ? layers::ScrollDirection::eHorizontal
162 : layers::ScrollDirection::eVertical;
165 StyleOverflow ScrollTimeline::SourceScrollStyle() const {
166 MOZ_ASSERT(mSource && mSource.mElement->GetPrimaryFrame());
168 const nsIScrollableFrame* scrollFrame = GetScrollFrame();
169 MOZ_ASSERT(scrollFrame);
171 const ScrollStyles scrollStyles = scrollFrame->GetScrollStyles();
173 return Axis() == layers::ScrollDirection::eHorizontal
174 ? scrollStyles.mHorizontal
175 : scrollStyles.mVertical;
178 bool ScrollTimeline::APZIsActiveForSource() const {
179 MOZ_ASSERT(mSource);
180 return gfxPlatform::AsyncPanZoomEnabled() &&
181 !nsLayoutUtils::ShouldDisableApzForElement(mSource.mElement) &&
182 DisplayPortUtils::HasNonMinimalNonZeroDisplayPort(mSource.mElement);
185 bool ScrollTimeline::ScrollingDirectionIsAvailable() const {
186 const nsIScrollableFrame* scrollFrame = GetScrollFrame();
187 MOZ_ASSERT(scrollFrame);
188 return scrollFrame->GetAvailableScrollingDirections().contains(Axis());
191 void ScrollTimeline::ReplacePropertiesWith(const Element* aReferenceElement,
192 PseudoStyleType aPseudoType,
193 const StyleScrollTimeline& aNew) {
194 MOZ_ASSERT(aReferenceElement == mSource.mElement &&
195 aPseudoType == mSource.mPseudoType);
196 mAxis = aNew.GetAxis();
198 for (auto* anim = mAnimationOrder.getFirst(); anim;
199 anim = static_cast<LinkedListElement<Animation>*>(anim)->getNext()) {
200 MOZ_ASSERT(anim->GetTimeline() == this);
201 // Set this so we just PostUpdate() for this animation.
202 anim->SetTimeline(this);
206 Maybe<ScrollTimeline::ScrollOffsets> ScrollTimeline::ComputeOffsets(
207 const nsIScrollableFrame* aScrollFrame,
208 layers::ScrollDirection aOrientation) const {
209 const nsRect& scrollRange = aScrollFrame->GetScrollRange();
210 nscoord range = aOrientation == layers::ScrollDirection::eHorizontal
211 ? scrollRange.width
212 : scrollRange.height;
213 MOZ_ASSERT(range > 0);
214 return Some(ScrollOffsets{0, range});
217 void ScrollTimeline::RegisterWithScrollSource() {
218 if (!mSource) {
219 return;
222 auto& scheduler =
223 ProgressTimelineScheduler::Ensure(mSource.mElement, mSource.mPseudoType);
224 scheduler.AddTimeline(this);
227 void ScrollTimeline::UnregisterFromScrollSource() {
228 if (!mSource) {
229 return;
232 auto* scheduler =
233 ProgressTimelineScheduler::Get(mSource.mElement, mSource.mPseudoType);
234 if (!scheduler) {
235 return;
238 scheduler->RemoveTimeline(this);
239 if (scheduler->IsEmpty()) {
240 ProgressTimelineScheduler::Destroy(mSource.mElement, mSource.mPseudoType);
244 const nsIScrollableFrame* ScrollTimeline::GetScrollFrame() const {
245 if (!mSource) {
246 return nullptr;
249 switch (mSource.mType) {
250 case Scroller::Type::Root:
251 if (const PresShell* presShell =
252 mSource.mElement->OwnerDoc()->GetPresShell()) {
253 return presShell->GetRootScrollFrameAsScrollable();
255 return nullptr;
256 case Scroller::Type::Nearest:
257 case Scroller::Type::Name:
258 case Scroller::Type::Self:
259 return nsLayoutUtils::FindScrollableFrameFor(mSource.mElement);
262 MOZ_ASSERT_UNREACHABLE("Unsupported scroller type");
263 return nullptr;
266 // ------------------------------------
267 // Methods of ProgressTimelineScheduler
268 // ------------------------------------
269 /* static */ ProgressTimelineScheduler* ProgressTimelineScheduler::Get(
270 const Element* aElement, PseudoStyleType aPseudoType) {
271 MOZ_ASSERT(aElement);
272 auto* data = aElement->GetAnimationData();
273 if (!data) {
274 return nullptr;
277 return data->GetProgressTimelineScheduler(aPseudoType);
280 /* static */ ProgressTimelineScheduler& ProgressTimelineScheduler::Ensure(
281 Element* aElement, PseudoStyleType aPseudoType) {
282 MOZ_ASSERT(aElement);
283 return aElement->EnsureAnimationData().EnsureProgressTimelineScheduler(
284 *aElement, aPseudoType);
287 /* static */
288 void ProgressTimelineScheduler::Destroy(const Element* aElement,
289 PseudoStyleType aPseudoType) {
290 auto* data = aElement->GetAnimationData();
291 MOZ_ASSERT(data);
292 data->ClearProgressTimelineScheduler(aPseudoType);
295 } // namespace mozilla::dom