From 91f41f3a0a406300e3b4fd7010894797cdf8528b Mon Sep 17 00:00:00 2001 From: Ryan Hunt Date: Tue, 27 Nov 2018 15:18:03 -0600 Subject: [PATCH] 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 --HG-- extra : rebase_source : 41f6f7691af60401f18f98a89f1878cfad2e74d3 extra : source : 3033401ef320414ef5f489c53526d015c38a89d7 --- layout/generic/ScrollAnchorContainer.cpp | 44 +++++++++++++++++++ layout/generic/ScrollAnchorContainer.h | 55 ++++++++++++++++++++++++ layout/generic/moz.build | 2 + layout/generic/nsGfxScrollFrame.cpp | 1 + layout/generic/nsGfxScrollFrame.h | 22 ++++++++++ layout/generic/nsIScrollableFrame.h | 10 +++++ 6 files changed, 134 insertions(+) create mode 100644 layout/generic/ScrollAnchorContainer.cpp create mode 100644 layout/generic/ScrollAnchorContainer.h diff --git a/layout/generic/ScrollAnchorContainer.cpp b/layout/generic/ScrollAnchorContainer.cpp new file mode 100644 index 000000000000..9e3be14465e8 --- /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 diff --git a/layout/generic/ScrollAnchorContainer.h b/layout/generic/ScrollAnchorContainer.h new file mode 100644 index 000000000000..e1805b8f44a4 --- /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_ diff --git a/layout/generic/moz.build b/layout/generic/moz.build index da9d5dd1569c..04e3b61b9a92 100644 --- a/layout/generic/moz.build +++ b/layout/generic/moz.build @@ -145,6 +145,7 @@ EXPORTS.mozilla += [ EXPORTS.mozilla.layout += [ 'FrameChildList.h', + 'ScrollAnchorContainer.h', ] UNIFIED_SOURCES += [ @@ -202,6 +203,7 @@ UNIFIED_SOURCES += [ 'ReflowInput.cpp', 'ReflowOutput.cpp', 'RubyUtils.cpp', + 'ScrollAnchorContainer.cpp', 'ScrollAnimationBezierPhysics.cpp', 'ScrollAnimationMSDPhysics.cpp', 'ScrollbarActivity.cpp', diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index 596a81ce5a28..f7e922a9638f 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -1960,6 +1960,7 @@ ScrollFrameHelper::ScrollFrameHelper(nsContainerFrame* aOuter, bool aIsRoot) mLastUpdateFramesPos(-1, -1), mDisplayPortAtLastFrameUpdate(), mScrollParentID(mozilla::layers::ScrollableLayerGuid::NULL_SCROLL_ID), + mAnchor(this), mAllowScrollOriginDowngrade(false), mHadDisplayPortAtLastFrameUpdate(false), mNeverHasVerticalScrollbar(false), diff --git a/layout/generic/nsGfxScrollFrame.h b/layout/generic/nsGfxScrollFrame.h index 85da98abb3a6..68bd7d9635bb 100644 --- a/layout/generic/nsGfxScrollFrame.h +++ b/layout/generic/nsGfxScrollFrame.h @@ -25,6 +25,7 @@ #include "TextOverflow.h" #include "ScrollVelocityQueue.h" #include "mozilla/PresState.h" +#include "mozilla/layout/ScrollAnchorContainer.h" class nsPresContext; class nsIPresShell; @@ -52,6 +53,7 @@ class ScrollFrameHelper : public nsIReflowCallback { typedef mozilla::layers::ScrollSnapInfo ScrollSnapInfo; typedef mozilla::layers::Layer Layer; typedef mozilla::layers::LayerManager LayerManager; + typedef mozilla::layout::ScrollAnchorContainer ScrollAnchorContainer; class AsyncScroll; class AsyncSmoothMSDScroll; @@ -566,6 +568,8 @@ class ScrollFrameHelper : public nsIReflowCallback { // Timer to remove the displayport some time after scrolling has stopped nsCOMPtr mDisplayPortExpiryTimer; + ScrollAnchorContainer mAnchor; + bool mAllowScrollOriginDowngrade : 1; bool mHadDisplayPortAtLastFrameUpdate : 1; bool mNeverHasVerticalScrollbar : 1; @@ -723,6 +727,7 @@ class nsHTMLScrollFrame : public nsContainerFrame, 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); @@ -1112,6 +1117,14 @@ class nsHTMLScrollFrame : public nsContainerFrame, 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& aResult) override { aResult.AppendElement(OwnedAnonBox(mHelper.GetScrolledFrame())); @@ -1174,6 +1187,7 @@ class nsXULScrollFrame final : public nsBoxFrame, public: typedef mozilla::ScrollFrameHelper ScrollFrameHelper; typedef mozilla::CSSIntPoint CSSIntPoint; + typedef mozilla::layout::ScrollAnchorContainer ScrollAnchorContainer; NS_DECL_QUERYFRAME NS_DECL_FRAMEARENA_HELPERS(nsXULScrollFrame) @@ -1577,6 +1591,14 @@ class nsXULScrollFrame final : public nsBoxFrame, 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& aResult) override { aResult.AppendElement(OwnedAnonBox(mHelper.GetScrolledFrame())); diff --git a/layout/generic/nsIScrollableFrame.h b/layout/generic/nsIScrollableFrame.h index 2b331d96ec7d..1008c3f5a65a 100644 --- a/layout/generic/nsIScrollableFrame.h +++ b/layout/generic/nsIScrollableFrame.h @@ -39,6 +39,9 @@ struct ScrollMetadata; class Layer; class LayerManager; } // namespace layers +namespace layout { +class ScrollAnchorContainer; +} // namespace layout } // namespace mozilla /** @@ -51,6 +54,7 @@ class nsIScrollableFrame : public nsIScrollbarMediator { typedef mozilla::CSSIntPoint CSSIntPoint; typedef mozilla::ContainerLayerParameters ContainerLayerParameters; typedef mozilla::layers::ScrollSnapInfo ScrollSnapInfo; + typedef mozilla::layout::ScrollAnchorContainer ScrollAnchorContainer; NS_DECL_QUERYFRAME_TARGET(nsIScrollableFrame) @@ -548,6 +552,12 @@ class nsIScrollableFrame : public nsIScrollbarMediator { * 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