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 "ViewTimeline.h"
9 #include "mozilla/dom/Animation.h"
10 #include "mozilla/dom/ElementInlines.h"
11 #include "nsIScrollableFrame.h"
12 #include "nsLayoutUtils.h"
14 namespace mozilla::dom
{
16 NS_IMPL_CYCLE_COLLECTION_INHERITED(ViewTimeline
, ScrollTimeline
, mSubject
)
17 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(ViewTimeline
, ScrollTimeline
)
20 already_AddRefed
<ViewTimeline
> ViewTimeline::MakeNamed(
21 Document
* aDocument
, Element
* aSubject
, PseudoStyleType aPseudoType
,
22 const StyleViewTimeline
& aStyleTimeline
) {
23 MOZ_ASSERT(NS_IsMainThread());
25 // 1. Lookup scroller. We have to find the nearest scroller from |aSubject|
27 auto [element
, pseudo
] = FindNearestScroller(aSubject
, aPseudoType
);
28 auto scroller
= Scroller::Nearest(const_cast<Element
*>(element
), pseudo
);
30 // 2. Create timeline.
31 return MakeAndAddRef
<ViewTimeline
>(aDocument
, scroller
,
32 aStyleTimeline
.GetAxis(), aSubject
,
33 aPseudoType
, aStyleTimeline
.GetInset());
37 already_AddRefed
<ViewTimeline
> ViewTimeline::MakeAnonymous(
38 Document
* aDocument
, const NonOwningAnimationTarget
& aTarget
,
39 StyleScrollAxis aAxis
, const StyleViewTimelineInset
& aInset
) {
40 // view() finds the nearest scroll container from the animation target.
41 auto [element
, pseudo
] =
42 FindNearestScroller(aTarget
.mElement
, aTarget
.mPseudoType
);
43 Scroller scroller
= Scroller::Nearest(const_cast<Element
*>(element
), pseudo
);
44 return MakeAndAddRef
<ViewTimeline
>(aDocument
, scroller
, aAxis
,
45 aTarget
.mElement
, aTarget
.mPseudoType
,
49 void ViewTimeline::ReplacePropertiesWith(Element
* aSubjectElement
,
50 PseudoStyleType aPseudoType
,
51 const StyleViewTimeline
& aNew
) {
52 mSubject
= aSubjectElement
;
53 mSubjectPseudoType
= aPseudoType
;
54 mAxis
= aNew
.GetAxis();
55 // FIXME: Bug 1817073. We assume it is a non-animatable value for now.
56 mInset
= aNew
.GetInset();
58 for (auto* anim
= mAnimationOrder
.getFirst(); anim
;
59 anim
= static_cast<LinkedListElement
<Animation
>*>(anim
)->getNext()) {
60 MOZ_ASSERT(anim
->GetTimeline() == this);
61 // Set this so we just PostUpdate() for this animation.
62 anim
->SetTimeline(this);
66 Maybe
<ScrollTimeline::ScrollOffsets
> ViewTimeline::ComputeOffsets(
67 const nsIScrollableFrame
* aScrollFrame
,
68 layers::ScrollDirection aOrientation
) const {
70 MOZ_ASSERT(aScrollFrame
);
72 const Element
* subjectElement
=
73 AnimationUtils::GetElementForRestyle(mSubject
, mSubjectPseudoType
);
74 const nsIFrame
* subject
= subjectElement
->GetPrimaryFrame();
76 // No principal box of the subject, so we cannot compute the offset. This
77 // may happen when we clear all animation collections during unbinding from
82 // In order to get the distance between the subject and the scrollport
83 // properly, we use the position based on the domain of the scrolled frame,
84 // instead of the scrollable frame.
85 const nsIFrame
* scrolledFrame
= aScrollFrame
->GetScrolledFrame();
86 MOZ_ASSERT(scrolledFrame
);
87 const nsRect
subjectRect(subject
->GetOffsetTo(scrolledFrame
),
90 // Use scrollport size (i.e. padding box size - scrollbar size), which is used
91 // for calculating the view progress visibility range.
92 // https://drafts.csswg.org/scroll-animations/#view-progress-visibility-range
93 const nsRect scrollPort
= aScrollFrame
->GetScrollPortRect();
95 // Adjuct the positions and sizes based on the physical axis.
96 nscoord subjectPosition
= subjectRect
.y
;
97 nscoord subjectSize
= subjectRect
.height
;
98 nscoord scrollPortSize
= scrollPort
.height
;
99 if (aOrientation
== layers::ScrollDirection::eHorizontal
) {
100 // |subjectPosition| should be the position of the start border edge of the
101 // subject, so for R-L case, we have to use XMost() as the start border
102 // edge of the subject, and compute its position by using the x-most side of
103 // the scrolled frame as the origin on the horizontal axis.
104 subjectPosition
= scrolledFrame
->GetWritingMode().IsPhysicalRTL()
105 ? scrolledFrame
->GetSize().width
- subjectRect
.XMost()
107 subjectSize
= subjectRect
.width
;
108 scrollPortSize
= scrollPort
.width
;
111 // |sideInsets.mEnd| is used to adjust the start offset, and
112 // |sideInsets.mStart| is used to adjust the end offset. This is because
113 // |sideInsets.mStart| refers to logical start side [1] of the source box
114 // (i.e. the box of the scrollport), where as |startOffset| refers to the
115 // start of the timeline, and similarly for end side/offset. [1]
116 // https://drafts.csswg.org/css-writing-modes-4/#css-start
117 const auto sideInsets
= ComputeInsets(aScrollFrame
, aOrientation
);
119 // Basically, we are computing the "cover" timeline range name, which
120 // represents the full range of the view progress timeline.
121 // https://drafts.csswg.org/scroll-animations-1/#valdef-animation-timeline-range-cover
123 // Note: `subjectPosition - scrollPortSize` means the distance between the
124 // start border edge of the subject and the end edge of the scrollport.
125 nscoord startOffset
= subjectPosition
- scrollPortSize
+ sideInsets
.mEnd
;
126 // Note: `subjectPosition + subjectSize` means the position of the end border
127 // edge of the subject. When it touches the start edge of the scrollport, it
129 nscoord endOffset
= subjectPosition
+ subjectSize
- sideInsets
.mStart
;
130 return Some(ScrollOffsets
{startOffset
, endOffset
});
133 ScrollTimeline::ScrollOffsets
ViewTimeline::ComputeInsets(
134 const nsIScrollableFrame
* aScrollFrame
,
135 layers::ScrollDirection aOrientation
) const {
136 // If view-timeline-inset is auto, it indicates to use the value of
137 // scroll-padding. We use logical dimension to map that start/end offset to
138 // the corresponding scroll-padding-{inline|block}-{start|end} values.
139 const WritingMode wm
= aScrollFrame
->GetScrolledFrame()->GetWritingMode();
140 const auto& scrollPadding
=
141 LogicalMargin(wm
, aScrollFrame
->GetScrollPadding());
142 const bool isBlockAxis
=
143 mAxis
== StyleScrollAxis::Block
||
144 (mAxis
== StyleScrollAxis::Horizontal
&& wm
.IsVertical()) ||
145 (mAxis
== StyleScrollAxis::Vertical
&& !wm
.IsVertical());
147 // The percentages of view-timelne-inset is relative to the corresponding
148 // dimension of the relevant scrollport.
149 // https://drafts.csswg.org/scroll-animations-1/#view-timeline-inset
150 const nsRect scrollPort
= aScrollFrame
->GetScrollPortRect();
151 const nscoord percentageBasis
=
152 aOrientation
== layers::ScrollDirection::eHorizontal
? scrollPort
.width
156 mInset
.start
.IsAuto()
157 ? (isBlockAxis
? scrollPadding
.BStart(wm
) : scrollPadding
.IStart(wm
))
158 : mInset
.start
.AsLengthPercentage().Resolve(percentageBasis
);
161 ? (isBlockAxis
? scrollPadding
.BEnd(wm
) : scrollPadding
.IEnd(wm
))
162 : mInset
.end
.AsLengthPercentage().Resolve(percentageBasis
);
163 return {startInset
, endInset
};
166 } // namespace mozilla::dom