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"
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
,
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
,
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
,
41 ScrollTimeline::ScrollTimeline(Document
* aDocument
, const Scroller
& aScroller
,
42 StyleScrollAxis aAxis
)
43 : AnimationTimeline(aDocument
->GetParentObject(),
44 aDocument
->GetScopeObject()->GetRTPCallerType()),
48 MOZ_ASSERT(aDocument
);
49 RegisterWithScrollSource();
52 /* static */ std::pair
<const Element
*, PseudoStyleType
>
53 ScrollTimeline::FindNearestScroller(Element
* aSubject
,
54 PseudoStyleType aPseudoType
) {
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()) {
67 curr
= curr
->GetFlattenedTreeParentElement();
69 // If there is no scroll container, we use root.
71 return {root
, PseudoStyleType::NotPseudo
};
73 return AnimationUtils::GetElementPseudoPair(curr
);
77 already_AddRefed
<ScrollTimeline
> ScrollTimeline::MakeAnonymous(
78 Document
* aDocument
, const NonOwningAnimationTarget
& aTarget
,
79 StyleScrollAxis aAxis
, StyleScroller aScroller
) {
83 case StyleScroller::Root
:
84 scroller
= Scroller::Root(aTarget
.mElement
->OwnerDoc());
87 case StyleScroller::Nearest
: {
88 auto [element
, pseudo
] =
89 FindNearestScroller(aTarget
.mElement
, aTarget
.mPseudoType
);
90 scroller
= Scroller::Nearest(const_cast<Element
*>(element
), pseudo
);
93 case StyleScroller::SelfElement
:
94 scroller
= Scroller::Self(aTarget
.mElement
, aTarget
.mPseudoType
);
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()) {
121 // if this is not a scroller container, this timeline is inactive.
122 const nsIScrollableFrame
* scrollFrame
= GetScrollFrame();
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
)) {
135 const bool isHorizontal
= orientation
== layers::ScrollDirection::eHorizontal
;
136 const nsPoint
& scrollPosition
= scrollFrame
->GetScrollPosition();
137 const Maybe
<ScrollOffsets
>& offsets
=
138 ComputeOffsets(scrollFrame
, orientation
);
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
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 {
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
212 : scrollRange
.height
;
213 MOZ_ASSERT(range
> 0);
214 return Some(ScrollOffsets
{0, range
});
217 void ScrollTimeline::RegisterWithScrollSource() {
223 ProgressTimelineScheduler::Ensure(mSource
.mElement
, mSource
.mPseudoType
);
224 scheduler
.AddTimeline(this);
227 void ScrollTimeline::UnregisterFromScrollSource() {
233 ProgressTimelineScheduler::Get(mSource
.mElement
, mSource
.mPseudoType
);
238 scheduler
->RemoveTimeline(this);
239 if (scheduler
->IsEmpty()) {
240 ProgressTimelineScheduler::Destroy(mSource
.mElement
, mSource
.mPseudoType
);
244 const nsIScrollableFrame
* ScrollTimeline::GetScrollFrame() const {
249 switch (mSource
.mType
) {
250 case Scroller::Type::Root
:
251 if (const PresShell
* presShell
=
252 mSource
.mElement
->OwnerDoc()->GetPresShell()) {
253 return presShell
->GetRootScrollFrameAsScrollable();
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");
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();
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
);
288 void ProgressTimelineScheduler::Destroy(const Element
* aElement
,
289 PseudoStyleType aPseudoType
) {
290 auto* data
= aElement
->GetAnimationData();
292 data
->ClearProgressTimelineScheduler(aPseudoType
);
295 } // namespace mozilla::dom