Commit 9005af91 authored by Boris Chiou's avatar Boris Chiou
Browse files

Bug 1676791 - Part 9: Define Scroller to handle the source of ScrollTimeline. r=emilio

Per spec, "auto" represents the scrolling element of the document.
However, the scrolling element might be changed, based on the layout, in
quirks mode. Besides, the content of the root scroll frame is the root
element, instead of the scrolling element (e.g. body element) in both
standard and quirks modes. So now we define a special type, Scroller, to
represent the source of scroll-timeline, and use its |mType| to decide
which scroll frame we would like to use. In addition, hope this change let
us easier to implement nearest scroller.

Note: for auto scroller, we register this ScrollTimeline to the root
element, in both modes. Once we expose ScrollTimeline interface to the
script, we can rely on the |mType| of Scroller to return the correct
source element, whether it is scrolling element or not.

Differential Revision: https://phabricator.services.mozilla.com/D131578
parent 74f0bd8a
Loading
Loading
Loading
Loading
+35 −15
Original line number Diff line number Diff line
@@ -7,8 +7,8 @@
#include "ScrollTimeline.h"

#include "mozilla/dom/Animation.h"
#include "mozilla/dom/Document.h"
#include "mozilla/AnimationTarget.h"
#include "mozilla/PresShell.h"
#include "nsIFrame.h"
#include "nsIScrollableFrame.h"
#include "nsLayoutUtils.h"
@@ -26,12 +26,12 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ScrollTimeline,
                                                AnimationTimeline)
  tmp->Teardown();
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSource)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSource.mElement)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ScrollTimeline,
                                                  AnimationTimeline)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSource)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSource.mElement)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ScrollTimeline,
@@ -43,7 +43,7 @@ NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(ScrollTimeline,

TimingParams ScrollTimeline::sTiming;

ScrollTimeline::ScrollTimeline(Document* aDocument, Element* aScroller)
ScrollTimeline::ScrollTimeline(Document* aDocument, const Scroller& aScroller)
    : AnimationTimeline(aDocument->GetParentObject()),
      mDocument(aDocument),
      // FIXME: Bug 1737918: We may have to udpate the constructor arguments
@@ -67,7 +67,8 @@ already_AddRefed<ScrollTimeline> ScrollTimeline::FromRule(
    const NonOwningAnimationTarget& aTarget) {
  // FIXME: Use ScrollingElement in the next patch.
  RefPtr<ScrollTimeline> timeline = new ScrollTimeline(
      aDocument, aTarget.mElement->OwnerDoc()->GetDocumentElement());
      aDocument, Scroller::Auto(aTarget.mElement->OwnerDoc()));

  // FIXME: Bug 1737918: applying new spec update.
  // Note: If the rules changes after we build the scroll-timeline rule, we
  // rebuild the animtions, so does the timeline object (because now we create
@@ -79,10 +80,10 @@ already_AddRefed<ScrollTimeline> ScrollTimeline::FromRule(
}

Nullable<TimeDuration> ScrollTimeline::GetCurrentTimeAsDuration() const {
  const nsIFrame* frame = nsLayoutUtils::GetScrollFrameFromContent(mSource);
  const nsIScrollableFrame* scrollFrame =
      frame ? frame->GetScrollTargetFrame() : nullptr;
  if (!scrollFrame) {
  const nsIFrame* frame =
      mSource ? mSource.mElement->GetPrimaryFrame() : nullptr;
  const nsIScrollableFrame* scrollFrame = GetScrollFrame();
  if (!frame || !scrollFrame) {
    return nullptr;
  }

@@ -117,7 +118,7 @@ void ScrollTimeline::RegisterWithScrollSource() {
  }

  if (ScrollTimelineSet* scrollTimelineSet =
          ScrollTimelineSet::GetOrCreateScrollTimelineSet(mSource)) {
          ScrollTimelineSet::GetOrCreateScrollTimelineSet(mSource.mElement)) {
    scrollTimelineSet->AddScrollTimeline(*this);
  }
}
@@ -128,12 +129,31 @@ void ScrollTimeline::UnregisterFromScrollSource() {
  }

  if (ScrollTimelineSet* scrollTimelineSet =
          ScrollTimelineSet::GetScrollTimelineSet(mSource)) {
          ScrollTimelineSet::GetScrollTimelineSet(mSource.mElement)) {
    scrollTimelineSet->RemoveScrollTimeline(*this);
    if (scrollTimelineSet->IsEmpty()) {
      ScrollTimelineSet::DestroyScrollTimelineSet(mSource);
      ScrollTimelineSet::DestroyScrollTimelineSet(mSource.mElement);
    }
  }
}

const nsIScrollableFrame* ScrollTimeline::GetScrollFrame() const {
  if (!mSource) {
    return nullptr;
  }

  switch (mSource.mType) {
    case Scroller::Type::Auto:
      if (const PresShell* presShell =
              mSource.mElement->OwnerDoc()->GetPresShell()) {
        return presShell->GetRootScrollFrameAsScrollable();
      }
      break;
    case Scroller::Type::Other:
    default:
      return nsLayoutUtils::FindScrollableFrameFor(mSource.mElement);
  }
  return nullptr;
}

// ---------------------------------
@@ -142,9 +162,9 @@ void ScrollTimeline::UnregisterFromScrollSource() {

/* static */ ScrollTimelineSet* ScrollTimelineSet::GetScrollTimelineSet(
    Element* aElement) {
  MOZ_ASSERT(aElement);
  return static_cast<ScrollTimelineSet*>(
      aElement->GetProperty(nsGkAtoms::scrollTimelinesProperty));
  return aElement ? static_cast<ScrollTimelineSet*>(aElement->GetProperty(
                        nsGkAtoms::scrollTimelinesProperty))
                  : nullptr;
}

/* static */ ScrollTimelineSet* ScrollTimelineSet::GetOrCreateScrollTimelineSet(
+36 −4
Original line number Diff line number Diff line
@@ -8,18 +8,20 @@
#define mozilla_dom_ScrollTimeline_h

#include "mozilla/dom/AnimationTimeline.h"
#include "mozilla/dom/Document.h"
#include "mozilla/HashTable.h"
#include "mozilla/ServoStyleConsts.h"
#include "mozilla/TimingParams.h"
#include "mozilla/WritingModes.h"

class nsIScrollableFrame;

namespace mozilla {

struct NonOwningAnimationTarget;

namespace dom {

class Document;
class Element;

/**
@@ -62,8 +64,36 @@ class Element;
 */
class ScrollTimeline final : public AnimationTimeline {
 public:
  struct Scroller {
    // FIXME: Support nearest and replace auto with root in Bug 1737918.
    enum class Type : uint8_t {
      // For auto. Should be scrolling element of the owner doc.
      Auto,
      // For any other specific elements.
      Other,
    };
    Type mType = Type::Auto;
    RefPtr<Element> mElement;

    // We use the owner doc of the animation target. This may be different from
    // |mDocument| after we implement ScrollTimeline interface for script.
    static Scroller Auto(const Document* aOwnerDoc) {
      // For auto, we use scrolling element as the default scroller.
      // However, it's mutable, and we would like to keep things simple, so
      // we always register the ScrollTimeline to the document element (i.e.
      // root element) because the content of the root scroll frame is the root
      // element.
      return {Type::Auto, aOwnerDoc->GetDocumentElement()};
    }

    explicit operator bool() const { return mElement; }
    bool operator==(const Scroller& aOther) const {
      return mType == aOther.mType && mElement == aOther.mElement;
    }
  };

  ScrollTimeline() = delete;
  ScrollTimeline(Document* aDocument, Element* aScroller);
  ScrollTimeline(Document* aDocument, const Scroller& aScroller);

  // FIXME: Bug 1737918: Rewrite this because @scroll-timeline will be obsolete.
  static already_AddRefed<ScrollTimeline> FromRule(
@@ -127,6 +157,8 @@ class ScrollTimeline final : public AnimationTimeline {
  void RegisterWithScrollSource();
  void UnregisterFromScrollSource();

  const nsIScrollableFrame* GetScrollFrame() const;

  // A helper to get the physical orientation of this scroll-timeline.
  //
  // The spec defines auto, but there is a spec issue:
@@ -149,10 +181,10 @@ class ScrollTimeline final : public AnimationTimeline {
  // FIXME: Bug 1733260: new spec proposal uses a new way to define scroller,
  // and move the element-based offset into view-timeline, so here we only
  // implement the default behavior of scroll timeline:
  // 1. "source" is auto (use main viewport scroller), and
  // 1. "source" is auto (use scrolling element), and
  // 2. "scroll-offsets" is none (i.e. always 0% ~ 100%).
  // So now we will only use the scroll direction from @scroll-timeline rule.
  RefPtr<Element> mSource;
  Scroller mSource;
  StyleScrollDirection mDirection;

  // Note: it's unfortunate TimingParams cannot be a const variable because
+60 −0
Original line number Diff line number Diff line
<html class="reftest-wait">
<title>The default scroll-timeline at rule in quirks mode</title>
<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule">
<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-timeline">
<meta name="assert" content="CSS animation correctly updates values when using the default scroll-timeline at rule">
<link rel="match" href="at-scroll-timeline-default-descriptors-ref.html">

<style>
  @keyframes update {
    from { transform: translateY(0px); }
    to { transform: translateY(200px); }
  }

  @scroll-timeline test-timeline {
    source: auto;
    orientation: auto;
    scroll-offsets: none;
  }

  html {
    min-height: 100%;
    padding-bottom: 100px;
  }

  #box {
    width: 100px;
    height: 100px;
    background-color: green;
    animation: update 1s linear;
    animation-timeline: test-timeline;
  }

  #covered {
    width: 100px;
    height: 100px;
    background-color: red;
  }

  * {
    margin-top: 0px;
    margin-bottom: 0px;
  }
</style>

<div id="box"></div>
<div id="covered"></div>

<script>
  window.addEventListener('load', function() {
    const scroller = document.scrollingElement;

    // Move the scroller to the halfway point.
    const maxScroll = scroller.scrollHeight - scroller.clientHeight;
    scroller.scrollTop = 0.5 * maxScroll;

    window.requestAnimationFrame(() => {
      document.documentElement.classList.remove("reftest-wait");
    });
  });
</script>