tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ViewTimeline.cpp (8113B)


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