Bug 1305957 part 4 - Add basic ScrollAnchorContainer implementation. r=hiro
authorRyan Hunt <rhunt@eqrion.net>
Tue, 27 Nov 2018 15:18:03 -0600
changeset 510488 52a6a35a238d2ea87c60cc387d12e7348fcf821a
parent 510487 399c5fe7c934c1dd8a0235888d7cebbce3df23dd
child 510489 b6c42df7ce091d7f2417e13203a50c60cd144571
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershiro
bugs1305957
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1305957 part 4 - Add basic ScrollAnchorContainer implementation. r=hiro This commit adds a barebones class called 'ScrollAnchorContainer' that will contain most of the logic for scroll anchoring. It is owned as a member of ScrollFrameHelper, and has the same lifetime. Differential Revision: https://phabricator.services.mozilla.com/D13267
layout/generic/ScrollAnchorContainer.cpp
layout/generic/ScrollAnchorContainer.h
layout/generic/moz.build
layout/generic/nsGfxScrollFrame.cpp
layout/generic/nsGfxScrollFrame.h
layout/generic/nsIScrollableFrame.h
new file mode 100644
--- /dev/null
+++ b/layout/generic/ScrollAnchorContainer.cpp
@@ -0,0 +1,44 @@
+/* -*- 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 "ScrollAnchorContainer.h"
+
+#include "nsGfxScrollFrame.h"
+#include "nsLayoutUtils.h"
+
+#define ANCHOR_LOG(...)
+// #define ANCHOR_LOG(...) printf_stderr("ANCHOR: " __VA_ARGS__)
+
+namespace mozilla {
+namespace layout {
+
+ScrollAnchorContainer::ScrollAnchorContainer(ScrollFrameHelper* aScrollFrame)
+    : mScrollFrame(aScrollFrame) {}
+
+ScrollAnchorContainer::~ScrollAnchorContainer() {}
+
+ScrollAnchorContainer* ScrollAnchorContainer::FindFor(nsIFrame* aFrame) {
+  aFrame = aFrame->GetParent();
+  if (!aFrame) {
+    return nullptr;
+  }
+  nsIScrollableFrame* nearest = nsLayoutUtils::GetNearestScrollableFrame(
+      aFrame, nsLayoutUtils::SCROLLABLE_SAME_DOC |
+                  nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
+  if (nearest) {
+    return nearest->GetAnchor();
+  }
+  return nullptr;
+}
+
+nsIFrame* ScrollAnchorContainer::Frame() const { return mScrollFrame->mOuter; }
+
+nsIScrollableFrame* ScrollAnchorContainer::ScrollableFrame() const {
+  return Frame()->GetScrollTargetFrame();
+}
+
+}  // namespace layout
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/layout/generic/ScrollAnchorContainer.h
@@ -0,0 +1,55 @@
+/* -*- 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/. */
+
+#ifndef mozilla_layout_ScrollAnchorContainer_h_
+#define mozilla_layout_ScrollAnchorContainer_h_
+
+namespace mozilla {
+class ScrollFrameHelper;
+}  // namespace mozilla
+
+namespace mozilla {
+namespace layout {
+
+/**
+ * A scroll anchor container finds a descendent element of a scrollable frame
+ * to be an anchor node. After every reflow, the scroll anchor will apply
+ * scroll adjustments to keep the anchor node in the same relative position.
+ *
+ * See: https://drafts.csswg.org/css-scroll-anchoring/
+ */
+class ScrollAnchorContainer final {
+ public:
+  explicit ScrollAnchorContainer(ScrollFrameHelper* aScrollFrame);
+  ~ScrollAnchorContainer();
+
+  /**
+   * Returns the nearest scroll anchor container that could select aFrame as an
+   * anchor node.
+   */
+  static ScrollAnchorContainer* FindFor(nsIFrame* aFrame);
+
+  /**
+   * Returns the frame that owns this scroll anchor container. This is always
+   * non-null.
+   */
+  nsIFrame* Frame() const;
+
+  /**
+   * Returns the frame that owns this scroll anchor container as a scrollable
+   * frame. This is always non-null.
+   */
+  nsIScrollableFrame* ScrollableFrame() const;
+
+ private:
+  // The owner of this scroll anchor container
+  ScrollFrameHelper* mScrollFrame;
+};
+
+}  // namespace layout
+}  // namespace mozilla
+
+#endif  // mozilla_layout_ScrollAnchorContainer_h_
--- a/layout/generic/moz.build
+++ b/layout/generic/moz.build
@@ -140,16 +140,17 @@ EXPORTS.mozilla += [
     'ReflowInput.h',
     'ReflowOutput.h',
     'ViewportFrame.h',
     'WritingModes.h',
 ]
 
 EXPORTS.mozilla.layout += [
     'FrameChildList.h',
+    'ScrollAnchorContainer.h',
 ]
 
 UNIFIED_SOURCES += [
     'BlockReflowInput.cpp',
     'BRFrame.cpp',
     'ColumnSetWrapperFrame.cpp',
     'CSSAlignUtils.cpp',
     'CSSOrderAwareFrameIterator.cpp',
@@ -197,16 +198,17 @@ UNIFIED_SOURCES += [
     'nsSubDocumentFrame.cpp',
     'nsTextFrame.cpp',
     'nsTextFrameUtils.cpp',
     'nsTextRunTransformations.cpp',
     'nsVideoFrame.cpp',
     'ReflowInput.cpp',
     'ReflowOutput.cpp',
     'RubyUtils.cpp',
+    'ScrollAnchorContainer.cpp',
     'ScrollAnimationBezierPhysics.cpp',
     'ScrollAnimationMSDPhysics.cpp',
     'ScrollbarActivity.cpp',
     'ScrollSnap.cpp',
     'ScrollVelocityQueue.cpp',
     'StickyScrollContainer.cpp',
     'TextOverflow.cpp',
     'ViewportFrame.cpp',
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -1955,16 +1955,17 @@ ScrollFrameHelper::ScrollFrameHelper(nsC
       mDestination(0, 0),
       mRestorePos(-1, -1),
       mLastPos(-1, -1),
       mApzScrollPos(0, 0),
       mScrollPosForLayerPixelAlignment(-1, -1),
       mLastUpdateFramesPos(-1, -1),
       mDisplayPortAtLastFrameUpdate(),
       mScrollParentID(mozilla::layers::ScrollableLayerGuid::NULL_SCROLL_ID),
+      mAnchor(this),
       mAllowScrollOriginDowngrade(false),
       mHadDisplayPortAtLastFrameUpdate(false),
       mNeverHasVerticalScrollbar(false),
       mNeverHasHorizontalScrollbar(false),
       mHasVerticalScrollbar(false),
       mHasHorizontalScrollbar(false),
       mFrameIsUpdatingScrollbar(false),
       mDidHistoryRestore(false),
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -20,16 +20,17 @@
 #include "nsIReflowCallback.h"
 #include "nsBoxLayoutState.h"
 #include "nsQueryFrame.h"
 #include "nsRefreshDriver.h"
 #include "nsExpirationTracker.h"
 #include "TextOverflow.h"
 #include "ScrollVelocityQueue.h"
 #include "mozilla/PresState.h"
+#include "mozilla/layout/ScrollAnchorContainer.h"
 
 class nsPresContext;
 class nsIPresShell;
 class nsIContent;
 class nsAtom;
 class nsIScrollPositionListener;
 
 namespace mozilla {
@@ -47,16 +48,17 @@ class ScrollFrameHelper : public nsIRefl
   typedef nsIFrame::Sides Sides;
   typedef mozilla::CSSIntPoint CSSIntPoint;
   typedef mozilla::layout::ScrollbarActivity ScrollbarActivity;
   typedef mozilla::layers::FrameMetrics FrameMetrics;
   typedef mozilla::layers::ScrollableLayerGuid ScrollableLayerGuid;
   typedef mozilla::layers::ScrollSnapInfo ScrollSnapInfo;
   typedef mozilla::layers::Layer Layer;
   typedef mozilla::layers::LayerManager LayerManager;
+  typedef mozilla::layout::ScrollAnchorContainer ScrollAnchorContainer;
 
   class AsyncScroll;
   class AsyncSmoothMSDScroll;
 
   ScrollFrameHelper(nsContainerFrame* aOuter, bool aIsRoot);
   ~ScrollFrameHelper();
 
   mozilla::ScrollStyles GetScrollStylesFromFrame() const;
@@ -561,16 +563,18 @@ class ScrollFrameHelper : public nsIRefl
 
   nsRect mPrevScrolledRect;
 
   ScrollableLayerGuid::ViewID mScrollParentID;
 
   // Timer to remove the displayport some time after scrolling has stopped
   nsCOMPtr<nsITimer> mDisplayPortExpiryTimer;
 
+  ScrollAnchorContainer mAnchor;
+
   bool mAllowScrollOriginDowngrade : 1;
   bool mHadDisplayPortAtLastFrameUpdate : 1;
   bool mNeverHasVerticalScrollbar : 1;
   bool mNeverHasHorizontalScrollbar : 1;
   bool mHasVerticalScrollbar : 1;
   bool mHasHorizontalScrollbar : 1;
   bool mFrameIsUpdatingScrollbar : 1;
   bool mDidHistoryRestore : 1;
@@ -718,16 +722,17 @@ class ScrollFrameHelper : public nsIRefl
 class nsHTMLScrollFrame : public nsContainerFrame,
                           public nsIScrollableFrame,
                           public nsIAnonymousContentCreator,
                           public nsIStatefulFrame {
  public:
   typedef mozilla::ScrollFrameHelper ScrollFrameHelper;
   typedef mozilla::CSSIntPoint CSSIntPoint;
   typedef mozilla::ScrollReflowInput ScrollReflowInput;
+  typedef mozilla::layout::ScrollAnchorContainer ScrollAnchorContainer;
   friend nsHTMLScrollFrame* NS_NewHTMLScrollFrame(nsIPresShell* aPresShell,
                                                   ComputedStyle* aStyle,
                                                   bool aIsRoot);
 
   NS_DECL_QUERYFRAME
   NS_DECL_FRAMEARENA_HELPERS(nsHTMLScrollFrame)
 
   virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
@@ -1107,16 +1112,24 @@ class nsHTMLScrollFrame : public nsConta
   virtual void AsyncScrollbarDragRejected() override {
     return mHelper.AsyncScrollbarDragRejected();
   }
 
   virtual bool IsRootScrollFrameOfDocument() const override {
     return mHelper.IsRootScrollFrameOfDocument();
   }
 
+  virtual const ScrollAnchorContainer* GetAnchor() const override {
+    return &mHelper.mAnchor;
+  }
+
+  virtual ScrollAnchorContainer* GetAnchor() override {
+    return &mHelper.mAnchor;
+  }
+
   // Return the scrolled frame.
   void AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) override {
     aResult.AppendElement(OwnedAnonBox(mHelper.GetScrolledFrame()));
   }
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
@@ -1169,16 +1182,17 @@ class nsHTMLScrollFrame : public nsConta
  */
 class nsXULScrollFrame final : public nsBoxFrame,
                                public nsIScrollableFrame,
                                public nsIAnonymousContentCreator,
                                public nsIStatefulFrame {
  public:
   typedef mozilla::ScrollFrameHelper ScrollFrameHelper;
   typedef mozilla::CSSIntPoint CSSIntPoint;
+  typedef mozilla::layout::ScrollAnchorContainer ScrollAnchorContainer;
 
   NS_DECL_QUERYFRAME
   NS_DECL_FRAMEARENA_HELPERS(nsXULScrollFrame)
 
   friend nsXULScrollFrame* NS_NewXULScrollFrame(nsIPresShell* aPresShell,
                                                 ComputedStyle* aStyle,
                                                 bool aIsRoot,
                                                 bool aClipAllDescendants);
@@ -1572,16 +1586,24 @@ class nsXULScrollFrame final : public ns
   virtual void AsyncScrollbarDragRejected() override {
     return mHelper.AsyncScrollbarDragRejected();
   }
 
   virtual bool IsRootScrollFrameOfDocument() const override {
     return mHelper.IsRootScrollFrameOfDocument();
   }
 
+  virtual const ScrollAnchorContainer* GetAnchor() const override {
+    return &mHelper.mAnchor;
+  }
+
+  virtual ScrollAnchorContainer* GetAnchor() override {
+    return &mHelper.mAnchor;
+  }
+
   // Return the scrolled frame.
   void AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) override {
     aResult.AppendElement(OwnedAnonBox(mHelper.GetScrolledFrame()));
   }
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
--- a/layout/generic/nsIScrollableFrame.h
+++ b/layout/generic/nsIScrollableFrame.h
@@ -34,28 +34,32 @@ class nsDisplayListBuilder;
 
 namespace mozilla {
 struct ContainerLayerParameters;
 namespace layers {
 struct ScrollMetadata;
 class Layer;
 class LayerManager;
 }  // namespace layers
+namespace layout {
+class ScrollAnchorContainer;
+}  // namespace layout
 }  // namespace mozilla
 
 /**
  * Interface for frames that are scrollable. This interface exposes
  * APIs for examining scroll state, observing changes to scroll state,
  * and triggering scrolling.
  */
 class nsIScrollableFrame : public nsIScrollbarMediator {
  public:
   typedef mozilla::CSSIntPoint CSSIntPoint;
   typedef mozilla::ContainerLayerParameters ContainerLayerParameters;
   typedef mozilla::layers::ScrollSnapInfo ScrollSnapInfo;
+  typedef mozilla::layout::ScrollAnchorContainer ScrollAnchorContainer;
 
   NS_DECL_QUERYFRAME_TARGET(nsIScrollableFrame)
 
   /**
    * Get the frame for the content that we are scrolling within
    * this scrollable frame.
    */
   virtual nsIFrame* GetScrolledFrame() const = 0;
@@ -543,11 +547,17 @@ class nsIScrollableFrame : public nsIScr
   virtual void AsyncScrollbarDragRejected() = 0;
 
   /**
    * Returns whether this scroll frame is the root scroll frame of the document
    * that it is in. Note that some documents don't have root scroll frames at
    * all (ie XUL documents) even though they may contain other scroll frames.
    */
   virtual bool IsRootScrollFrameOfDocument() const = 0;
+
+  /**
+   * Returns the scroll anchor associated with this scrollable frame.
+   */
+  virtual const ScrollAnchorContainer* GetAnchor() const = 0;
+  virtual ScrollAnchorContainer* GetAnchor() = 0;
 };
 
 #endif