1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ViewTimeline.h"
#include "mozilla/ScrollContainerFrame.h"
#include "mozilla/dom/Animation.h"
#include "mozilla/dom/ElementInlines.h"
#include "nsLayoutUtils.h"
namespace mozilla::dom {
NS_IMPL_CYCLE_COLLECTION_INHERITED(ViewTimeline, ScrollTimeline, mSubject)
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(ViewTimeline, ScrollTimeline)
/* static */
already_AddRefed<ViewTimeline> ViewTimeline::MakeNamed(
Document* aDocument, Element* aSubject,
const PseudoStyleRequest& aPseudoRequest,
const StyleViewTimeline& aStyleTimeline) {
MOZ_ASSERT(NS_IsMainThread());
// 1. Lookup scroller. We have to find the nearest scroller from |aSubject|
// and |aPseudoType|.
auto [element, pseudo] = FindNearestScroller(aSubject, aPseudoRequest);
auto scroller =
Scroller::Nearest(const_cast<Element*>(element), pseudo.mType);
// 2. Create timeline.
return MakeAndAddRef<ViewTimeline>(
aDocument, scroller, aStyleTimeline.GetAxis(), aSubject,
aPseudoRequest.mType, aStyleTimeline.GetInset());
}
/* static */
already_AddRefed<ViewTimeline> ViewTimeline::MakeAnonymous(
Document* aDocument, const NonOwningAnimationTarget& aTarget,
StyleScrollAxis aAxis, const StyleViewTimelineInset& aInset) {
// view() finds the nearest scroll container from the animation target.
auto [element, pseudo] =
FindNearestScroller(aTarget.mElement, aTarget.mPseudoRequest);
Scroller scroller =
Scroller::Nearest(const_cast<Element*>(element), pseudo.mType);
return MakeAndAddRef<ViewTimeline>(aDocument, scroller, aAxis,
aTarget.mElement,
aTarget.mPseudoRequest.mType, aInset);
}
void ViewTimeline::ReplacePropertiesWith(
Element* aSubjectElement, const PseudoStyleRequest& aPseudoRequest,
const StyleViewTimeline& aNew) {
mSubject = aSubjectElement;
mSubjectPseudoType = aPseudoRequest.mType;
mAxis = aNew.GetAxis();
// FIXME: Bug 1817073. We assume it is a non-animatable value for now.
mInset = aNew.GetInset();
for (auto* anim = mAnimationOrder.getFirst(); anim;
anim = static_cast<LinkedListElement<Animation>*>(anim)->getNext()) {
MOZ_ASSERT(anim->GetTimeline() == this);
// Set this so we just PostUpdate() for this animation.
anim->SetTimeline(this);
}
}
Maybe<ScrollTimeline::ScrollOffsets> ViewTimeline::ComputeOffsets(
const ScrollContainerFrame* aScrollContainerFrame,
layers::ScrollDirection aOrientation) const {
MOZ_ASSERT(mSubject);
MOZ_ASSERT(aScrollContainerFrame);
const Element* subjectElement =
mSubject->GetPseudoElement(PseudoStyleRequest(mSubjectPseudoType));
const nsIFrame* subject = subjectElement->GetPrimaryFrame();
if (!subject) {
// No principal box of the subject, so we cannot compute the offset. This
// may happen when we clear all animation collections during unbinding from
// the tree.
return Nothing();
}
// In order to get the distance between the subject and the scrollport
// properly, we use the position based on the domain of the scrolled frame,
// instead of the scroll container frame.
const nsIFrame* scrolledFrame = aScrollContainerFrame->GetScrolledFrame();
MOZ_ASSERT(scrolledFrame);
const nsRect subjectRect(subject->GetOffsetTo(scrolledFrame),
subject->GetSize());
// Use scrollport size (i.e. padding box size - scrollbar size), which is used
// for calculating the view progress visibility range.
// https://drafts.csswg.org/scroll-animations/#view-progress-visibility-range
const nsRect scrollPort = aScrollContainerFrame->GetScrollPortRect();
// Adjuct the positions and sizes based on the physical axis.
nscoord subjectPosition = subjectRect.y;
nscoord subjectSize = subjectRect.height;
nscoord scrollPortSize = scrollPort.height;
if (aOrientation == layers::ScrollDirection::eHorizontal) {
// |subjectPosition| should be the position of the start border edge of the
// subject, so for R-L case, we have to use XMost() as the start border
// edge of the subject, and compute its position by using the x-most side of
// the scrolled frame as the origin on the horizontal axis.
subjectPosition = scrolledFrame->GetWritingMode().IsPhysicalRTL()
? scrolledFrame->GetSize().width - subjectRect.XMost()
: subjectRect.x;
subjectSize = subjectRect.width;
scrollPortSize = scrollPort.width;
}
// |sideInsets.mEnd| is used to adjust the start offset, and
// |sideInsets.mStart| is used to adjust the end offset. This is because
// |sideInsets.mStart| refers to logical start side [1] of the source box
// (i.e. the box of the scrollport), where as |startOffset| refers to the
// start of the timeline, and similarly for end side/offset. [1]
// https://drafts.csswg.org/css-writing-modes-4/#css-start
const auto sideInsets = ComputeInsets(aScrollContainerFrame, aOrientation);
// Basically, we are computing the "cover" timeline range name, which
// represents the full range of the view progress timeline.
// https://drafts.csswg.org/scroll-animations-1/#valdef-animation-timeline-range-cover
// Note: `subjectPosition - scrollPortSize` means the distance between the
// start border edge of the subject and the end edge of the scrollport.
nscoord startOffset = subjectPosition - scrollPortSize + sideInsets.mEnd;
// Note: `subjectPosition + subjectSize` means the position of the end border
// edge of the subject. When it touches the start edge of the scrollport, it
// is 100%.
nscoord endOffset = subjectPosition + subjectSize - sideInsets.mStart;
return Some(ScrollOffsets{startOffset, endOffset});
}
ScrollTimeline::ScrollOffsets ViewTimeline::ComputeInsets(
const ScrollContainerFrame* aScrollContainerFrame,
layers::ScrollDirection aOrientation) const {
// If view-timeline-inset is auto, it indicates to use the value of
// scroll-padding. We use logical dimension to map that start/end offset to
// the corresponding scroll-padding-{inline|block}-{start|end} values.
const WritingMode wm =
aScrollContainerFrame->GetScrolledFrame()->GetWritingMode();
const auto& scrollPadding =
LogicalMargin(wm, aScrollContainerFrame->GetScrollPadding());
const bool isBlockAxis = mAxis == StyleScrollAxis::Block ||
(mAxis == StyleScrollAxis::X && wm.IsVertical()) ||
(mAxis == StyleScrollAxis::Y && !wm.IsVertical());
// The percentages of view-timelne-inset is relative to the corresponding
// dimension of the relevant scrollport.
// https://drafts.csswg.org/scroll-animations-1/#view-timeline-inset
const nsRect scrollPort = aScrollContainerFrame->GetScrollPortRect();
const nscoord percentageBasis =
aOrientation == layers::ScrollDirection::eHorizontal ? scrollPort.width
: scrollPort.height;
nscoord startInset =
mInset.start.IsAuto()
? (isBlockAxis ? scrollPadding.BStart(wm) : scrollPadding.IStart(wm))
: mInset.start.AsLengthPercentage().Resolve(percentageBasis);
nscoord endInset =
mInset.end.IsAuto()
? (isBlockAxis ? scrollPadding.BEnd(wm) : scrollPadding.IEnd(wm))
: mInset.end.AsLengthPercentage().Resolve(percentageBasis);
return {startInset, endInset};
}
} // namespace mozilla::dom
|