Merge mozilla-central to autoland. a=merge CLOSED TREE
[gecko.git] / dom / animation / ViewTimeline.cpp
blob5727c5f08e71ba4730c7c57cb2b1af32ff018895
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)
19 /* static */
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|
26 // and |aPseudoType|.
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());
36 /* static */
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,
46 aInset);
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 {
69 MOZ_ASSERT(mSubject);
70 MOZ_ASSERT(aScrollFrame);
72 const Element* subjectElement =
73 AnimationUtils::GetElementForRestyle(mSubject, mSubjectPseudoType);
74 const nsIFrame* subject = subjectElement->GetPrimaryFrame();
75 if (!subject) {
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
78 // the tree.
79 return Nothing();
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),
88 subject->GetSize());
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()
106 : subjectRect.x;
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
128 // is 100%.
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
153 : scrollPort.height;
155 nscoord startInset =
156 mInset.start.IsAuto()
157 ? (isBlockAxis ? scrollPadding.BStart(wm) : scrollPadding.IStart(wm))
158 : mInset.start.AsLengthPercentage().Resolve(percentageBasis);
159 nscoord endInset =
160 mInset.end.IsAuto()
161 ? (isBlockAxis ? scrollPadding.BEnd(wm) : scrollPadding.IEnd(wm))
162 : mInset.end.AsLengthPercentage().Resolve(percentageBasis);
163 return {startInset, endInset};
166 } // namespace mozilla::dom