tor-browser

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

ScrollTimeline.cpp (11524B)


      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 "ScrollTimeline.h"
      8 
      9 #include "mozilla/AnimationTarget.h"
     10 #include "mozilla/DisplayPortUtils.h"
     11 #include "mozilla/ElementAnimationData.h"
     12 #include "mozilla/PresShell.h"
     13 #include "mozilla/ScrollContainerFrame.h"
     14 #include "mozilla/dom/Animation.h"
     15 #include "mozilla/dom/AnimationTimelinesController.h"
     16 #include "mozilla/dom/Document.h"
     17 #include "mozilla/dom/DocumentInlines.h"
     18 #include "mozilla/dom/ElementInlines.h"
     19 #include "nsIFrame.h"
     20 #include "nsLayoutUtils.h"
     21 #include "nsRefreshDriver.h"
     22 
     23 namespace mozilla::dom {
     24 
     25 // ---------------------------------
     26 // Methods of ScrollTimeline
     27 // ---------------------------------
     28 
     29 NS_IMPL_CYCLE_COLLECTION_CLASS(ScrollTimeline)
     30 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ScrollTimeline,
     31                                                AnimationTimeline)
     32  tmp->Teardown();
     33  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
     34  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSource.mElement)
     35 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     36 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ScrollTimeline,
     37                                                  AnimationTimeline)
     38  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
     39  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSource.mElement)
     40 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     41 
     42 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(ScrollTimeline,
     43                                               AnimationTimeline)
     44 
     45 ScrollTimeline::ScrollTimeline(Document* aDocument, const Scroller& aScroller,
     46                               StyleScrollAxis aAxis)
     47    : AnimationTimeline(aDocument->GetParentObject(),
     48                        aDocument->GetScopeObject()->GetRTPCallerType()),
     49      mDocument(aDocument),
     50      mSource(aScroller),
     51      mAxis(aAxis) {
     52  MOZ_ASSERT(aDocument);
     53 
     54  mDocument->TimelinesController().AddScrollTimeline(*this);
     55 }
     56 
     57 /* static */
     58 std::pair<const Element*, PseudoStyleRequest>
     59 ScrollTimeline::FindNearestScroller(Element* aSubject,
     60                                    const PseudoStyleRequest& aPseudoRequest) {
     61  MOZ_ASSERT(aSubject);
     62  Element* subject = aSubject->GetPseudoElement(aPseudoRequest);
     63  Element* curr = subject->GetFlattenedTreeParentElement();
     64  Element* root = subject->OwnerDoc()->GetDocumentElement();
     65  while (curr && curr != root) {
     66    const ComputedStyle* style = Servo_Element_GetMaybeOutOfDateStyle(curr);
     67    MOZ_ASSERT(style, "The ancestor should be styled.");
     68    if (style->StyleDisplay()->IsScrollableOverflow()) {
     69      break;
     70    }
     71    curr = curr->GetFlattenedTreeParentElement();
     72  }
     73  // If there is no scroll container, we use root.
     74  if (!curr) {
     75    return {root, PseudoStyleRequest::NotPseudo()};
     76  }
     77  return AnimationUtils::GetElementPseudoPair(curr);
     78 }
     79 
     80 /* static */
     81 already_AddRefed<ScrollTimeline> ScrollTimeline::MakeAnonymous(
     82    Document* aDocument, const NonOwningAnimationTarget& aTarget,
     83    StyleScrollAxis aAxis, StyleScroller aScroller) {
     84  MOZ_ASSERT(aTarget);
     85  Scroller scroller;
     86  switch (aScroller) {
     87    case StyleScroller::Root:
     88      // Specifies to use the document viewport as the scroll container.
     89      //
     90      // We use the owner doc of the animation target. This may be different
     91      // from |mDocument| after we implement ScrollTimeline interface for
     92      // script.
     93      scroller =
     94          Scroller::Root(aTarget.mElement->OwnerDoc()->GetDocumentElement());
     95      break;
     96 
     97    case StyleScroller::Nearest: {
     98      auto [element, pseudo] =
     99          FindNearestScroller(aTarget.mElement, aTarget.mPseudoRequest);
    100      scroller = Scroller::Nearest(const_cast<Element*>(element), pseudo.mType);
    101      break;
    102    }
    103    case StyleScroller::SelfElement:
    104      scroller = Scroller::Self(aTarget.mElement, aTarget.mPseudoRequest.mType);
    105      break;
    106  }
    107 
    108  // Each use of scroll() corresponds to its own instance of ScrollTimeline in
    109  // the Web Animations API, even if multiple elements use scroll() to refer to
    110  // the same scroll container with the same arguments.
    111  // https://drafts.csswg.org/scroll-animations-1/#scroll-notation
    112  return MakeAndAddRef<ScrollTimeline>(aDocument, scroller, aAxis);
    113 }
    114 
    115 /* static*/
    116 already_AddRefed<ScrollTimeline> ScrollTimeline::MakeNamed(
    117    Document* aDocument, Element* aReferenceElement,
    118    const PseudoStyleRequest& aPseudoRequest,
    119    const StyleScrollTimeline& aStyleTimeline) {
    120  MOZ_ASSERT(NS_IsMainThread());
    121 
    122  Scroller scroller = Scroller::Named(aReferenceElement, aPseudoRequest.mType);
    123  return MakeAndAddRef<ScrollTimeline>(aDocument, std::move(scroller),
    124                                       aStyleTimeline.GetAxis());
    125 }
    126 
    127 Nullable<TimeDuration> ScrollTimeline::GetCurrentTimeAsDuration() const {
    128  if (!mCachedCurrentTime) {
    129    return nullptr;
    130  }
    131 
    132  const CurrentTimeData& data = mCachedCurrentTime.ref();
    133  // FIXME: Scroll offsets on the RTL container is complicated specifically on
    134  // mobile, see https://github.com/w3c/csswg-drafts/issues/12893. For now, we
    135  // use the absoluate value to make things simple.
    136  double progress =
    137      static_cast<double>(std::abs(data.mPosition) - data.mOffsets.mStart) /
    138      static_cast<double>(data.mOffsets.mEnd - data.mOffsets.mStart);
    139  return TimeDuration::FromMilliseconds(progress *
    140                                        PROGRESS_TIMELINE_DURATION_MILLISEC);
    141 }
    142 
    143 void ScrollTimeline::WillRefresh() {
    144  UpdateCachedCurrentTime();
    145 
    146  if (!mDocument->GetPresShell()) {
    147    // If we're not displayed, don't tick animations.
    148    return;
    149  }
    150 
    151  if (mAnimationOrder.isEmpty()) {
    152    return;
    153  }
    154 
    155  // FIXME: Bug 1737927: Need to check the animation mutation observers for
    156  // animations with scroll timelines.
    157  // nsAutoAnimationMutationBatch mb(mDocument);
    158 
    159  TickState dummyState;
    160  Tick(dummyState);
    161 }
    162 
    163 layers::ScrollDirection ScrollTimeline::Axis() const {
    164  MOZ_ASSERT(mSource && mSource.mElement->GetPrimaryFrame());
    165 
    166  const WritingMode wm = mSource.mElement->GetPrimaryFrame()->GetWritingMode();
    167  return mAxis == StyleScrollAxis::X ||
    168                 (!wm.IsVertical() && mAxis == StyleScrollAxis::Inline) ||
    169                 (wm.IsVertical() && mAxis == StyleScrollAxis::Block)
    170             ? layers::ScrollDirection::eHorizontal
    171             : layers::ScrollDirection::eVertical;
    172 }
    173 
    174 StyleOverflow ScrollTimeline::SourceScrollStyle() const {
    175  MOZ_ASSERT(mSource && mSource.mElement->GetPrimaryFrame());
    176 
    177  const ScrollContainerFrame* scrollContainerFrame = GetScrollContainerFrame();
    178  MOZ_ASSERT(scrollContainerFrame);
    179 
    180  const ScrollStyles scrollStyles = scrollContainerFrame->GetScrollStyles();
    181 
    182  return Axis() == layers::ScrollDirection::eHorizontal
    183             ? scrollStyles.mHorizontal
    184             : scrollStyles.mVertical;
    185 }
    186 
    187 bool ScrollTimeline::APZIsActiveForSource() const {
    188  MOZ_ASSERT(mSource);
    189  return gfxPlatform::AsyncPanZoomEnabled() &&
    190         !nsLayoutUtils::ShouldDisableApzForElement(mSource.mElement) &&
    191         DisplayPortUtils::HasNonMinimalNonZeroDisplayPort(mSource.mElement);
    192 }
    193 
    194 bool ScrollTimeline::ScrollingDirectionIsAvailable() const {
    195  const ScrollContainerFrame* scrollContainerFrame = GetScrollContainerFrame();
    196  MOZ_ASSERT(scrollContainerFrame);
    197  return scrollContainerFrame->GetAvailableScrollingDirections().contains(
    198      Axis());
    199 }
    200 
    201 void ScrollTimeline::ReplacePropertiesWith(
    202    const Element* aReferenceElement, const PseudoStyleRequest& aPseudoRequest,
    203    const StyleScrollTimeline& aNew) {
    204  MOZ_ASSERT(aReferenceElement == mSource.mElement &&
    205             aPseudoRequest.mType == mSource.mPseudoType);
    206  mAxis = aNew.GetAxis();
    207 
    208  for (auto* anim = mAnimationOrder.getFirst(); anim;
    209       anim = static_cast<LinkedListElement<Animation>*>(anim)->getNext()) {
    210    MOZ_ASSERT(anim->GetTimeline() == this);
    211    // Set this so we just PostUpdate() for this animation.
    212    anim->SetTimeline(this);
    213  }
    214 }
    215 
    216 ScrollTimeline::~ScrollTimeline() { Teardown(); }
    217 
    218 Maybe<ScrollTimeline::ScrollOffsets> ScrollTimeline::ComputeOffsets(
    219    const ScrollContainerFrame* aScrollContainerFrame,
    220    layers::ScrollDirection aOrientation) const {
    221  const nsRect& scrollRange = aScrollContainerFrame->GetScrollRange();
    222  nscoord range = aOrientation == layers::ScrollDirection::eHorizontal
    223                      ? scrollRange.width
    224                      : scrollRange.height;
    225  MOZ_ASSERT(range > 0);
    226  return Some(ScrollOffsets{0, range});
    227 }
    228 
    229 void ScrollTimeline::UpdateCachedCurrentTime() {
    230  mCachedCurrentTime.reset();
    231 
    232  // If no layout box, this timeline is inactive.
    233  if (!mSource || !mSource.mElement->GetPrimaryFrame()) {
    234    return;
    235  }
    236 
    237  // if this is not a scroller container, this timeline is inactive.
    238  const ScrollContainerFrame* scrollContainerFrame = GetScrollContainerFrame();
    239  if (!scrollContainerFrame) {
    240    return;
    241  }
    242 
    243  const auto orientation = Axis();
    244 
    245  // If there is no scrollable overflow, then the ScrollTimeline is inactive.
    246  // https://drafts.csswg.org/scroll-animations-1/#scrolltimeline-interface
    247  if (!scrollContainerFrame->GetAvailableScrollingDirections().contains(
    248          orientation)) {
    249    return;
    250  }
    251 
    252  const nsPoint& scrollPosition = scrollContainerFrame->GetScrollPosition();
    253  const Maybe<ScrollOffsets>& offsets =
    254      ComputeOffsets(scrollContainerFrame, orientation);
    255  if (!offsets) {
    256    return;
    257  }
    258 
    259  mCachedCurrentTime.emplace(CurrentTimeData{
    260      orientation == layers::ScrollDirection::eHorizontal ? scrollPosition.x
    261                                                          : scrollPosition.y,
    262      offsets.value()});
    263 }
    264 
    265 const ScrollContainerFrame* ScrollTimeline::GetScrollContainerFrame() const {
    266  if (!mSource) {
    267    return nullptr;
    268  }
    269 
    270  switch (mSource.mType) {
    271    case Scroller::Type::Root:
    272      if (const PresShell* presShell =
    273              mSource.mElement->OwnerDoc()->GetPresShell()) {
    274        return presShell->GetRootScrollContainerFrame();
    275      }
    276      return nullptr;
    277    case Scroller::Type::Nearest:
    278    case Scroller::Type::Name:
    279    case Scroller::Type::Self:
    280      return nsLayoutUtils::FindScrollContainerFrameFor(mSource.mElement);
    281  }
    282 
    283  MOZ_ASSERT_UNREACHABLE("Unsupported scroller type");
    284  return nullptr;
    285 }
    286 
    287 static nsRefreshDriver* GetRefreshDriver(Document* aDocument) {
    288  nsPresContext* presContext = aDocument->GetPresContext();
    289  if (MOZ_UNLIKELY(!presContext)) {
    290    return nullptr;
    291  }
    292  return presContext->RefreshDriver();
    293 }
    294 
    295 void ScrollTimeline::NotifyAnimationUpdated(Animation& aAnimation) {
    296  AnimationTimeline::NotifyAnimationUpdated(aAnimation);
    297 
    298  if (!mAnimationOrder.isEmpty()) {
    299    if (auto* rd = GetRefreshDriver(mDocument)) {
    300      MOZ_ASSERT(isInList(),
    301                 "We should not register with the refresh driver if we are not"
    302                 " in the document's list of timelines");
    303      rd->EnsureAnimationUpdate();
    304    }
    305  }
    306 }
    307 
    308 void ScrollTimeline::NotifyAnimationContentVisibilityChanged(
    309    Animation* aAnimation, bool aIsVisible) {
    310  AnimationTimeline::NotifyAnimationContentVisibilityChanged(aAnimation,
    311                                                             aIsVisible);
    312  if (auto* rd = GetRefreshDriver(mDocument)) {
    313    MOZ_ASSERT(isInList(),
    314               "We should not register with the refresh driver if we are not"
    315               " in the document's list of timelines");
    316    rd->EnsureAnimationUpdate();
    317  }
    318 }
    319 
    320 }  // namespace mozilla::dom