Commit d17622aa authored by bokan's avatar bokan Committed by Commit bot

Viewport resize anchoring should use the current layout viewport.

The current viewport resize anchor assumes the FrameView is always the layout
viewport. This is not true in the setRootScroller experiment where an arbitrary
<div> may become the layout viewport in RootFrameViewport.

I've moved the anchoring logic into RootFrameViewport to keep this information
encapsulated and used the layout viewport as reported by the RootFrameViewport
class

BUG=505516

Review-Url: https://codereview.chromium.org/2248433002
Cr-Commit-Position: refs/heads/master@{#413376}
parent 7a6d81a8
...@@ -833,8 +833,13 @@ void FrameView::performPreLayoutTasks() ...@@ -833,8 +833,13 @@ void FrameView::performPreLayoutTasks()
m_viewportScrollableArea = RootFrameViewport::create(visualViewport, *layoutViewport); m_viewportScrollableArea = RootFrameViewport::create(visualViewport, *layoutViewport);
} }
if (!m_scrollAnchor.hasScroller()) if (!m_scrollAnchor.hasScroller()) {
m_scrollAnchor.setScroller(m_viewportScrollableArea ? m_viewportScrollableArea : this); ScrollableArea* scroller = m_viewportScrollableArea;
if (!scroller)
scroller = this;
m_scrollAnchor.setScroller(scroller);
}
if (shouldPerformScrollAnchoring()) if (shouldPerformScrollAnchoring())
m_scrollAnchor.save(); m_scrollAnchor.save();
...@@ -4149,6 +4154,11 @@ ScrollableArea* FrameView::layoutViewportScrollableArea() ...@@ -4149,6 +4154,11 @@ ScrollableArea* FrameView::layoutViewportScrollableArea()
return layoutViewItem.isNull() ? nullptr : layoutViewItem.getScrollableArea(); return layoutViewItem.isNull() ? nullptr : layoutViewItem.getScrollableArea();
} }
RootFrameViewport* FrameView::getRootFrameViewport()
{
return m_viewportScrollableArea.get();
}
LayoutObject* FrameView::viewportLayoutObject() const LayoutObject* FrameView::viewportLayoutObject() const
{ {
if (Document* document = frame().document()) { if (Document* document = frame().document()) {
......
...@@ -574,6 +574,12 @@ public: ...@@ -574,6 +574,12 @@ public:
// deltas from CC). For typical scrolling cases, use getScrollableArea(). // deltas from CC). For typical scrolling cases, use getScrollableArea().
ScrollableArea* layoutViewportScrollableArea(); ScrollableArea* layoutViewportScrollableArea();
// If this is the main frame, this will return the RootFrameViewport used
// to scroll the main frame. Otherwise returns nullptr. Unless you need a
// unique method on RootFrameViewport, you should probably use
// getScrollableArea.
RootFrameViewport* getRootFrameViewport();
int viewportWidth() const; int viewportWidth() const;
LayoutAnalyzer* layoutAnalyzer() { return m_analyzer.get(); } LayoutAnalyzer* layoutAnalyzer() { return m_analyzer.get(); }
...@@ -893,7 +899,7 @@ private: ...@@ -893,7 +899,7 @@ private:
// Exists only on root frame. // Exists only on root frame.
// TODO(bokan): crbug.com/484188. We should specialize FrameView for the // TODO(bokan): crbug.com/484188. We should specialize FrameView for the
// main frame. // main frame.
Member<ScrollableArea> m_viewportScrollableArea; Member<RootFrameViewport> m_viewportScrollableArea;
// This frame's bounds in the root frame's content coordinates, clipped // This frame's bounds in the root frame's content coordinates, clipped
// recursively through every ancestor view. // recursively through every ancestor view.
......
...@@ -23,6 +23,39 @@ void RootFrameViewport::setLayoutViewport(ScrollableArea& newLayoutViewport) ...@@ -23,6 +23,39 @@ void RootFrameViewport::setLayoutViewport(ScrollableArea& newLayoutViewport)
m_layoutViewport = &newLayoutViewport; m_layoutViewport = &newLayoutViewport;
} }
void RootFrameViewport::restoreToAnchor(const DoublePoint& targetPosition)
{
// Clamp the scroll offset of each viewport now so that we force any invalid
// offsets to become valid so we can compute the correct deltas.
visualViewport().setScrollPosition(
visualViewport().scrollPositionDouble(), ProgrammaticScroll);
layoutViewport().setScrollPosition(
layoutViewport().scrollPositionDouble(), ProgrammaticScroll);
DoubleSize delta = targetPosition - scrollPositionDouble();
visualViewport().setScrollPosition(
visualViewport().scrollPositionDouble() + delta, ProgrammaticScroll);
delta = targetPosition - scrollPositionDouble();
// Since the main thread FrameView has integer scroll offsets, scroll it to
// the next pixel and then we'll scroll the visual viewport again to
// compensate for the sub-pixel offset. We need this "overscroll" to ensure
// the pixel of which we want to be partially in appears fully inside the
// FrameView since the VisualViewport is bounded by the FrameView.
IntSize layoutDelta = IntSize(
delta.width() < 0 ? floor(delta.width()) : ceil(delta.width()),
delta.height() < 0 ? floor(delta.height()) : ceil(delta.height()));
layoutViewport().setScrollPosition(
layoutViewport().scrollPosition() + layoutDelta, ProgrammaticScroll);
delta = targetPosition - scrollPositionDouble();
visualViewport().setScrollPosition(
visualViewport().scrollPositionDouble() + delta, ProgrammaticScroll);
}
LayoutBox* RootFrameViewport::layoutBox() const LayoutBox* RootFrameViewport::layoutBox() const
{ {
return layoutViewport().layoutBox(); return layoutViewport().layoutBox();
......
...@@ -33,6 +33,8 @@ public: ...@@ -33,6 +33,8 @@ public:
void setLayoutViewport(ScrollableArea&); void setLayoutViewport(ScrollableArea&);
void restoreToAnchor(const DoublePoint&);
// ScrollableArea Implementation // ScrollableArea Implementation
bool isRootFrameViewport() const override { return true; } bool isRootFrameViewport() const override { return true; }
void setScrollPosition(const DoublePoint&, ScrollType, ScrollBehavior = ScrollBehaviorInstant) override; void setScrollPosition(const DoublePoint&, ScrollType, ScrollBehavior = ScrollBehaviorInstant) override;
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "core/frame/FrameHost.h" #include "core/frame/FrameHost.h"
#include "core/frame/FrameView.h" #include "core/frame/FrameView.h"
#include "core/frame/RootFrameViewport.h"
#include "core/frame/VisualViewport.h" #include "core/frame/VisualViewport.h"
#include "core/page/Page.h" #include "core/page/Page.h"
#include "platform/geometry/DoubleRect.h" #include "platform/geometry/DoubleRect.h"
...@@ -34,7 +35,6 @@ void ResizeViewportAnchor::endScope() ...@@ -34,7 +35,6 @@ void ResizeViewportAnchor::endScope()
if (!frameView) if (!frameView)
return; return;
VisualViewport& visualViewport = m_page->frameHost().visualViewport();
DoublePoint visualViewportInDocument = DoublePoint visualViewportInDocument =
frameView->getScrollableArea()->scrollPositionDouble() - m_drift; frameView->getScrollableArea()->scrollPositionDouble() - m_drift;
...@@ -45,41 +45,9 @@ void ResizeViewportAnchor::endScope() ...@@ -45,41 +45,9 @@ void ResizeViewportAnchor::endScope()
// which needs the two threads to match exactly pixel-for-pixel. We can // which needs the two threads to match exactly pixel-for-pixel. We can
// replace this with RFV::setScrollPosition once Blink is sub-pixel scroll // replace this with RFV::setScrollPosition once Blink is sub-pixel scroll
// offset aware. crbug.com/414283. // offset aware. crbug.com/414283.
DCHECK(frameView->getRootFrameViewport());
frameView->getRootFrameViewport()->restoreToAnchor(visualViewportInDocument);
ScrollableArea* rootViewport = frameView->getScrollableArea();
ScrollableArea* layoutViewport =
frameView->layoutViewportScrollableArea();
// Clamp the scroll offset of each viewport now so that we force any invalid
// offsets to become valid so we can compute the correct deltas.
visualViewport.clampToBoundaries();
layoutViewport->setScrollPosition(
layoutViewport->scrollPositionDouble(), ProgrammaticScroll);
DoubleSize delta = visualViewportInDocument
- rootViewport->scrollPositionDouble();
visualViewport.move(toFloatSize(delta));
delta = visualViewportInDocument
- rootViewport->scrollPositionDouble();
// Since the main thread FrameView has integer scroll offsets, scroll it to
// the next pixel and then we'll scroll the visual viewport again to
// compensate for the sub-pixel offset. We need this "overscroll" to ensure
// the pixel of which we want to be partially in appears fully inside the
// FrameView since the VisualViewport is bounded by the FrameView.
IntSize layoutDelta = IntSize(
delta.width() < 0 ? floor(delta.width()) : ceil(delta.width()),
delta.height() < 0 ? floor(delta.height()) : ceil(delta.height()));
layoutViewport->setScrollPosition(
layoutViewport->scrollPosition() + layoutDelta,
ProgrammaticScroll);
delta = visualViewportInDocument
- rootViewport->scrollPositionDouble();
visualViewport.move(toFloatSize(delta));
m_drift = DoubleSize(); m_drift = DoubleSize();
} }
......
...@@ -1861,4 +1861,41 @@ TEST_P(ParameterizedVisualViewportTest, ResizeWithScrollAnchoring) ...@@ -1861,4 +1861,41 @@ TEST_P(ParameterizedVisualViewportTest, ResizeWithScrollAnchoring)
RuntimeEnabledFeatures::setScrollAnchoringEnabled(wasScrollAnchoringEnabled); RuntimeEnabledFeatures::setScrollAnchoringEnabled(wasScrollAnchoringEnabled);
} }
// Ensure that resize anchoring as happens when top controls hide/show affects
// the scrollable area that's currently set as the root scroller.
TEST_P(ParameterizedVisualViewportTest, ResizeAnchoringWithRootScroller)
{
bool wasRootScrollerEnabled =
RuntimeEnabledFeatures::setRootScrollerEnabled();
RuntimeEnabledFeatures::setSetRootScrollerEnabled(true);
initializeWithAndroidSettings();
webViewImpl()->resize(IntSize(800, 600));
registerMockedHttpURLLoad("root-scroller-div.html");
navigateTo(m_baseURL + "root-scroller-div.html");
FrameView& frameView = *webViewImpl()->mainFrameImpl()->frameView();
Element* scroller = frame()->document()->getElementById("rootScroller");
NonThrowableExceptionState nonThrow;
frame()->document()->setRootScroller(scroller, nonThrow);
webViewImpl()->setPageScaleFactor(3.f);
frameView.getScrollableArea()->setScrollPosition(
DoublePoint(0, 400), ProgrammaticScroll);
VisualViewport& visualViewport =
webViewImpl()->page()->frameHost().visualViewport();
visualViewport.setScrollPosition(DoublePoint(0, 400), ProgrammaticScroll);
webViewImpl()->resize(IntSize(800, 500));
EXPECT_POINT_EQ(
DoublePoint(),
frameView.layoutViewportScrollableArea()->scrollPositionDouble());
RuntimeEnabledFeatures::setSetRootScrollerEnabled(wasRootScrollerEnabled);
}
} // namespace } // namespace
<!DOCTYPE html>
<meta name="viewport" content="width=device-width, minimum-scale=1">
<style>
body {
width: 1600px;
height: 1200px;
margin: 0px;
}
#rootScroller {
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: gray;
position: absolute;
overflow: auto;
}
#content {
height: 1000px;
width: 100px;
background-color: red;
}
</style>
<div id="rootScroller">
<div id="content"></div>
</div>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment