mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-03-02 14:30:43 +00:00
Bug 1777649 - Honor scroll snap properties on ScrollFrameRectIntoView call. r=emilio
Differential Revision: https://phabricator.services.mozilla.com/D151335
This commit is contained in:
parent
a313ef9201
commit
507bbaf6f5
@ -3493,6 +3493,21 @@ static nscoord ComputeWhereToScroll(WhereToScroll aWhereToScroll,
|
||||
return resultCoord;
|
||||
}
|
||||
|
||||
static WhereToScroll GetApplicableWhereToScroll(
|
||||
StyleScrollSnapAlignKeyword aAlign, WhereToScroll aOriginal) {
|
||||
switch (aAlign) {
|
||||
case StyleScrollSnapAlignKeyword::None:
|
||||
return aOriginal;
|
||||
case StyleScrollSnapAlignKeyword::Start:
|
||||
return kScrollToTop;
|
||||
case StyleScrollSnapAlignKeyword::Center:
|
||||
return kScrollToCenter;
|
||||
case StyleScrollSnapAlignKeyword::End:
|
||||
return kScrollToBottom;
|
||||
}
|
||||
return aOriginal;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function takes a scrollable frame, a rect in the coordinate system
|
||||
* of the scrolled frame, and a desired percentage-based scroll
|
||||
@ -3502,9 +3517,9 @@ static nscoord ComputeWhereToScroll(WhereToScroll aWhereToScroll,
|
||||
* This needs to work even if aRect has a width or height of zero.
|
||||
*/
|
||||
static void ScrollToShowRect(nsIScrollableFrame* aFrameAsScrollable,
|
||||
const nsRect& aRect, const nsMargin& aMargin,
|
||||
ScrollAxis aVertical, ScrollAxis aHorizontal,
|
||||
ScrollFlags aScrollFlags) {
|
||||
const nsIFrame* aTarget, const nsRect& aRect,
|
||||
const nsMargin& aMargin, ScrollAxis aVertical,
|
||||
ScrollAxis aHorizontal, ScrollFlags aScrollFlags) {
|
||||
nsPoint scrollPt = aFrameAsScrollable->GetVisualViewportOffset();
|
||||
const nsPoint originalScrollPt = scrollPt;
|
||||
const nsRect visibleRect(scrollPt,
|
||||
@ -3540,9 +3555,14 @@ static void ScrollToShowRect(nsIScrollableFrame* aFrameAsScrollable,
|
||||
if (ComputeNeedToScroll(aVertical.mWhenToScroll, lineSize.height, aRect.y,
|
||||
aRect.YMost(), visibleRect.y + padding.top,
|
||||
visibleRect.YMost() - padding.bottom)) {
|
||||
// If the scroll-snap-align on the frame is valid, we need to respect it.
|
||||
WhereToScroll whereToScroll = GetApplicableWhereToScroll(
|
||||
aFrameAsScrollable->GetScrollSnapAlignFor(aTarget).second,
|
||||
aVertical.mWhereToScroll);
|
||||
|
||||
nscoord maxHeight;
|
||||
scrollPt.y = ComputeWhereToScroll(
|
||||
aVertical.mWhereToScroll, scrollPt.y, rectToScrollIntoView.y,
|
||||
whereToScroll, scrollPt.y, rectToScrollIntoView.y,
|
||||
rectToScrollIntoView.YMost(), visibleRect.y, visibleRect.YMost(),
|
||||
&allowedRange.y, &maxHeight);
|
||||
allowedRange.height = maxHeight - allowedRange.y;
|
||||
@ -3556,9 +3576,14 @@ static void ScrollToShowRect(nsIScrollableFrame* aFrameAsScrollable,
|
||||
if (ComputeNeedToScroll(aHorizontal.mWhenToScroll, lineSize.width, aRect.x,
|
||||
aRect.XMost(), visibleRect.x + padding.left,
|
||||
visibleRect.XMost() - padding.right)) {
|
||||
// If the scroll-snap-align on the frame is valid, we need to respect it.
|
||||
WhereToScroll whereToScroll = GetApplicableWhereToScroll(
|
||||
aFrameAsScrollable->GetScrollSnapAlignFor(aTarget).first,
|
||||
aHorizontal.mWhereToScroll);
|
||||
|
||||
nscoord maxWidth;
|
||||
scrollPt.x = ComputeWhereToScroll(
|
||||
aHorizontal.mWhereToScroll, scrollPt.x, rectToScrollIntoView.x,
|
||||
whereToScroll, scrollPt.x, rectToScrollIntoView.x,
|
||||
rectToScrollIntoView.XMost(), visibleRect.x, visibleRect.XMost(),
|
||||
&allowedRange.x, &maxWidth);
|
||||
allowedRange.width = maxWidth - allowedRange.x;
|
||||
@ -3706,6 +3731,7 @@ void PresShell::DoScrollContentIntoView() {
|
||||
// over all continuation frames below.
|
||||
const nsMargin scrollMargin = GetScrollMargin(mContentToScrollTo, frame);
|
||||
|
||||
const nsIFrame* target = frame;
|
||||
// This is a two-step process.
|
||||
// Step 1: Find the bounds of the rect we want to scroll into view. For
|
||||
// example, for an inline frame we may want to scroll in the whole
|
||||
@ -3737,14 +3763,15 @@ void PresShell::DoScrollContentIntoView() {
|
||||
|
||||
ScrollFrameRectIntoView(container, frameBounds, scrollMargin,
|
||||
data->mContentScrollVAxis, data->mContentScrollHAxis,
|
||||
data->mContentToScrollToFlags);
|
||||
data->mContentToScrollToFlags, target);
|
||||
}
|
||||
|
||||
bool PresShell::ScrollFrameRectIntoView(nsIFrame* aFrame, const nsRect& aRect,
|
||||
const nsMargin& aMargin,
|
||||
ScrollAxis aVertical,
|
||||
ScrollAxis aHorizontal,
|
||||
ScrollFlags aScrollFlags) {
|
||||
ScrollFlags aScrollFlags,
|
||||
const nsIFrame* aTarget) {
|
||||
if (aFrame->AncestorHidesContent()) {
|
||||
return false;
|
||||
}
|
||||
@ -3753,6 +3780,7 @@ bool PresShell::ScrollFrameRectIntoView(nsIFrame* aFrame, const nsRect& aRect,
|
||||
// This function needs to work even if rect has a width or height of 0.
|
||||
nsRect rect = aRect;
|
||||
nsIFrame* container = aFrame;
|
||||
const nsIFrame* target = aTarget ? aTarget : aFrame;
|
||||
// Walk up the frame hierarchy scrolling the rect into view and
|
||||
// keeping rect relative to container
|
||||
do {
|
||||
@ -3786,8 +3814,8 @@ bool PresShell::ScrollFrameRectIntoView(nsIFrame* aFrame, const nsRect& aRect,
|
||||
|
||||
{
|
||||
AutoWeakFrame wf(container);
|
||||
ScrollToShowRect(sf, targetRect, aMargin, aVertical, aHorizontal,
|
||||
aScrollFlags);
|
||||
ScrollToShowRect(sf, target, targetRect, aMargin, aVertical,
|
||||
aHorizontal, aScrollFlags);
|
||||
if (!wf.IsAlive()) {
|
||||
return didScroll;
|
||||
}
|
||||
@ -3806,6 +3834,10 @@ bool PresShell::ScrollFrameRectIntoView(nsIFrame* aFrame, const nsRect& aRect,
|
||||
if (aScrollFlags & ScrollFlags::ScrollFirstAncestorOnly) {
|
||||
break;
|
||||
}
|
||||
|
||||
// This scroll container will be the next target element in the nearest
|
||||
// ancestor scroll container.
|
||||
target = container;
|
||||
}
|
||||
nsIFrame* parent;
|
||||
if (container->IsTransformed()) {
|
||||
|
@ -591,27 +591,25 @@ class PresShell final : public nsStubDocumentObserver,
|
||||
* the rect is empty.
|
||||
* @param aVertical see ScrollContentIntoView and ScrollAxis
|
||||
* @param aHorizontal see ScrollContentIntoView and ScrollAxis
|
||||
* @param aScrollFlags if SCROLL_FIRST_ANCESTOR_ONLY is set, only the
|
||||
* @param aScrollFlags if ScrollFirstAncestorOnly is set, only the
|
||||
* nearest scrollable ancestor is scrolled, otherwise all
|
||||
* scrollable ancestors may be scrolled if necessary
|
||||
* if SCROLL_OVERFLOW_HIDDEN is set then we may scroll in a direction
|
||||
* if ScrollOverflowHidden is set then we may scroll in a direction
|
||||
* even if overflow:hidden is specified in that direction; otherwise
|
||||
* we will not scroll in that direction when overflow:hidden is
|
||||
* set for that direction
|
||||
* If SCROLL_NO_PARENT_FRAMES is set then we only scroll
|
||||
* If ScrollNoParentFrames is set then we only scroll
|
||||
* nodes in this document, not in any parent documents which
|
||||
* contain this document in a iframe or the like.
|
||||
* If SCROLL_IGNORE_SCROLL_MARGIN_AND_PADDING is set we ignore scroll-margin
|
||||
* value specified for |aFrame| and scroll-padding value for the scroll
|
||||
* container. This option is typically used to locate poped-up frames into
|
||||
* view.
|
||||
* @param aTarget optional, specifying the targe frame where we want to scroll
|
||||
* if it's different from aFrame.
|
||||
* @return true if any scrolling happened, false if no scrolling happened
|
||||
*/
|
||||
MOZ_CAN_RUN_SCRIPT
|
||||
bool ScrollFrameRectIntoView(nsIFrame* aFrame, const nsRect& aRect,
|
||||
const nsMargin& aMargin, ScrollAxis aVertical,
|
||||
ScrollAxis aHorizontal,
|
||||
ScrollFlags aScrollFlags);
|
||||
ScrollAxis aHorizontal, ScrollFlags aScrollFlags,
|
||||
const nsIFrame* aTarget = nullptr);
|
||||
|
||||
/**
|
||||
* Suppress notification of the frame manager that frames are
|
||||
|
@ -8054,6 +8054,77 @@ void ScrollFrameHelper::PostPendingResnap() {
|
||||
mOuter->PresShell()->PostPendingScrollResnap(sf);
|
||||
}
|
||||
|
||||
nsIScrollableFrame::PhysicalScrollSnapAlign
|
||||
ScrollFrameHelper::GetScrollSnapAlignFor(const nsIFrame* aFrame) const {
|
||||
StyleScrollSnapAlignKeyword alignForY = StyleScrollSnapAlignKeyword::None;
|
||||
StyleScrollSnapAlignKeyword alignForX = StyleScrollSnapAlignKeyword::None;
|
||||
|
||||
nsIFrame* styleFrame = GetFrameForStyle();
|
||||
if (!styleFrame) {
|
||||
return {alignForX, alignForY};
|
||||
}
|
||||
|
||||
if (styleFrame->StyleDisplay()->mScrollSnapType.strictness ==
|
||||
StyleScrollSnapStrictness::None) {
|
||||
return {alignForX, alignForY};
|
||||
}
|
||||
|
||||
const nsStyleDisplay* styleDisplay = aFrame->StyleDisplay();
|
||||
if (styleDisplay->mScrollSnapAlign.inline_ ==
|
||||
StyleScrollSnapAlignKeyword::None &&
|
||||
styleDisplay->mScrollSnapAlign.block ==
|
||||
StyleScrollSnapAlignKeyword::None) {
|
||||
return {alignForX, alignForY};
|
||||
}
|
||||
|
||||
nsSize snapAreaSize =
|
||||
ScrollSnapUtils::GetSnapAreaFor(aFrame, mScrolledFrame, GetScrolledRect())
|
||||
.Size();
|
||||
const WritingMode writingMode =
|
||||
ScrollSnapUtils::NeedsToRespectTargetWritingMode(snapAreaSize,
|
||||
GetSnapportSize())
|
||||
? aFrame->GetWritingMode()
|
||||
: styleFrame->GetWritingMode();
|
||||
|
||||
switch (styleFrame->StyleDisplay()->mScrollSnapType.axis) {
|
||||
case StyleScrollSnapAxis::X:
|
||||
alignForX = writingMode.IsVertical()
|
||||
? styleDisplay->mScrollSnapAlign.block
|
||||
: styleDisplay->mScrollSnapAlign.inline_;
|
||||
break;
|
||||
case StyleScrollSnapAxis::Y:
|
||||
alignForY = writingMode.IsVertical()
|
||||
? styleDisplay->mScrollSnapAlign.inline_
|
||||
: styleDisplay->mScrollSnapAlign.block;
|
||||
break;
|
||||
case StyleScrollSnapAxis::Block:
|
||||
if (writingMode.IsVertical()) {
|
||||
alignForX = styleDisplay->mScrollSnapAlign.block;
|
||||
} else {
|
||||
alignForY = styleDisplay->mScrollSnapAlign.block;
|
||||
}
|
||||
break;
|
||||
case StyleScrollSnapAxis::Inline:
|
||||
if (writingMode.IsVertical()) {
|
||||
alignForY = styleDisplay->mScrollSnapAlign.inline_;
|
||||
} else {
|
||||
alignForX = styleDisplay->mScrollSnapAlign.inline_;
|
||||
}
|
||||
break;
|
||||
case StyleScrollSnapAxis::Both:
|
||||
if (writingMode.IsVertical()) {
|
||||
alignForX = styleDisplay->mScrollSnapAlign.block;
|
||||
alignForY = styleDisplay->mScrollSnapAlign.inline_;
|
||||
} else {
|
||||
alignForX = styleDisplay->mScrollSnapAlign.inline_;
|
||||
alignForY = styleDisplay->mScrollSnapAlign.block;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return {alignForX, alignForY};
|
||||
}
|
||||
|
||||
bool ScrollFrameHelper::UsesOverlayScrollbars() const {
|
||||
return mOuter->PresContext()->UseOverlayScrollbars();
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ class AutoContainsBlendModeCapturer;
|
||||
namespace mozilla {
|
||||
class PresShell;
|
||||
struct ScrollReflowInput;
|
||||
struct StyleScrollSnapAlign;
|
||||
namespace layers {
|
||||
class Layer;
|
||||
class WebRenderLayerManager;
|
||||
@ -494,6 +495,9 @@ class ScrollFrameHelper : public nsIReflowCallback {
|
||||
void PostPendingResnapIfNeeded(const nsIFrame* aFrame);
|
||||
void PostPendingResnap();
|
||||
|
||||
using PhysicalScrollSnapAlign = nsIScrollableFrame::PhysicalScrollSnapAlign;
|
||||
PhysicalScrollSnapAlign GetScrollSnapAlignFor(const nsIFrame* aFrame) const;
|
||||
|
||||
static bool ShouldActivateAllScrollFrames();
|
||||
nsRect RestrictToRootDisplayPort(const nsRect& aDisplayportBase);
|
||||
bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
|
||||
@ -1319,6 +1323,11 @@ class nsHTMLScrollFrame : public nsContainerFrame,
|
||||
mHelper.PostPendingResnapIfNeeded(aFrame);
|
||||
}
|
||||
void PostPendingResnap() final { mHelper.PostPendingResnap(); }
|
||||
using PhysicalScrollSnapAlign = nsIScrollableFrame::PhysicalScrollSnapAlign;
|
||||
PhysicalScrollSnapAlign GetScrollSnapAlignFor(
|
||||
const nsIFrame* aFrame) const final {
|
||||
return mHelper.GetScrollSnapAlignFor(aFrame);
|
||||
}
|
||||
|
||||
bool DragScroll(mozilla::WidgetEvent* aEvent) final {
|
||||
return mHelper.DragScroll(aEvent);
|
||||
@ -1806,6 +1815,11 @@ class nsXULScrollFrame final : public nsBoxFrame,
|
||||
mHelper.PostPendingResnapIfNeeded(aFrame);
|
||||
}
|
||||
void PostPendingResnap() final { mHelper.PostPendingResnap(); }
|
||||
using PhysicalScrollSnapAlign = nsIScrollableFrame::PhysicalScrollSnapAlign;
|
||||
PhysicalScrollSnapAlign GetScrollSnapAlignFor(
|
||||
const nsIFrame* aFrame) const final {
|
||||
return mHelper.GetScrollSnapAlignFor(aFrame);
|
||||
}
|
||||
|
||||
bool DragScroll(mozilla::WidgetEvent* aEvent) final {
|
||||
return mHelper.DragScroll(aEvent);
|
||||
|
@ -36,6 +36,7 @@ class nsIContent;
|
||||
namespace mozilla {
|
||||
class DisplayItemClip;
|
||||
class nsDisplayListBuilder;
|
||||
enum class StyleScrollSnapAlignKeyword : uint8_t;
|
||||
|
||||
namespace layers {
|
||||
struct ScrollMetadata;
|
||||
@ -54,11 +55,14 @@ class ScrollAnchorContainer;
|
||||
*/
|
||||
class nsIScrollableFrame : public nsIScrollbarMediator {
|
||||
public:
|
||||
typedef mozilla::CSSIntPoint CSSIntPoint;
|
||||
typedef mozilla::layers::ScrollSnapInfo ScrollSnapInfo;
|
||||
typedef mozilla::layout::ScrollAnchorContainer ScrollAnchorContainer;
|
||||
typedef mozilla::ScrollMode ScrollMode;
|
||||
typedef mozilla::ScrollOrigin ScrollOrigin;
|
||||
using CSSIntPoint = mozilla::CSSIntPoint;
|
||||
using ScrollSnapInfo = mozilla::layers::ScrollSnapInfo;
|
||||
using ScrollAnchorContainer = mozilla::layout::ScrollAnchorContainer;
|
||||
using ScrollMode = mozilla::ScrollMode;
|
||||
using ScrollOrigin = mozilla::ScrollOrigin;
|
||||
using PhysicalScrollSnapAlign =
|
||||
std::pair<mozilla::StyleScrollSnapAlignKeyword,
|
||||
mozilla::StyleScrollSnapAlignKeyword>;
|
||||
|
||||
NS_DECL_QUERYFRAME_TARGET(nsIScrollableFrame)
|
||||
|
||||
@ -586,6 +590,15 @@ class nsIScrollableFrame : public nsIScrollbarMediator {
|
||||
virtual void PostPendingResnapIfNeeded(const nsIFrame* aFrame) = 0;
|
||||
virtual void PostPendingResnap() = 0;
|
||||
|
||||
/**
|
||||
* Returns a pair of the scroll-snap-align property value both on X and Y axes
|
||||
* for the given |aFrame| considering the scroll-snap-type of this scroll
|
||||
* container. For example, if the scroll-snap-type is `none`, the pair of
|
||||
* scroll-snap-align is also `none none`.
|
||||
*/
|
||||
virtual PhysicalScrollSnapAlign GetScrollSnapAlignFor(
|
||||
const nsIFrame* aFrame) const = 0;
|
||||
|
||||
/**
|
||||
* Given the drag event aEvent, determine whether the mouse is near the edge
|
||||
* of the scrollable area, and scroll the view in the direction of that edge
|
||||
|
@ -1,5 +0,0 @@
|
||||
[scroll-target-snap-001.html]
|
||||
expected:
|
||||
if (os == "linux") and swgl and not debug: [FAIL, PASS]
|
||||
if (os == "android") and not debug: [FAIL, PASS]
|
||||
FAIL
|
@ -1,2 +0,0 @@
|
||||
[scroll-target-snap-003.html]
|
||||
expected: FAIL
|
Loading…
x
Reference in New Issue
Block a user