Skip to content

Commit

Permalink
Give OOPIF FrameViews their own scroll animation timelines and hosts
Browse files Browse the repository at this point in the history
OOPIFs need independent scroll animation timelines and hosts, because
they have separate compositors. Ultimately we need to associate a
ScrollingCoordinator which each local frame root, but until that work
is done this CL resolves some known scrolling problems in
--site-per-process mode.

BUG=675695
CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:linux_layout_tests_slimming_paint_v2

Review-Url: https://codereview.chromium.org/2631853002
Cr-Commit-Position: refs/heads/master@{#444947}
(cherry picked from commit 65c7df6)

Review-Url: https://codereview.chromium.org/2654883002 .
Cr-Commit-Position: refs/branch-heads/2987@{#62}
Cr-Branched-From: ad51088-refs/heads/master@{#444943}
  • Loading branch information
kenrb committed Jan 24, 2017
1 parent 1b0254b commit 66ec94e
Show file tree
Hide file tree
Showing 14 changed files with 205 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<!DOCTYPE html>
<script src="/js-test-resources/js-test.js"></script>
<div style="height: 300px"></div>

<script>
setPrintTestResultsLazily();
self.jsTestIsAsync = true;

window.onload = (() => {
if (!window.eventSender || !window.internals) {
debug("This test requires window.eventSender.");
return;
}
internals.settings.setScrollAnimatorEnabled(true);
});

function handleMessage(event) {
if (event.data.hasOwnProperty('scrollBy')) {
eventSender.mouseMoveTo(event.data.left + 5, event.data.top + 5);
eventSender.mouseScrollBy(0, event.data.scrollBy);
requestAnimationFrame(() => {setTimeout(() => {event.source.postMessage("", "*")}, 500)});
} else {
event.source.postMessage({scrollTop: document.documentElement.scrollTop}, "*");
}
}

window.addEventListener("message", handleMessage);
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Verify that two sibling cross-origin iframes both correctly scroll on MouseWheel events, as per https://crbug.com/675695.

On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".


PASS event.data.scrollTop is 40
PASS event.data.scrollTop is 40
PASS successfullyParsed is true

TEST COMPLETE

Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<!DOCTYPE html>
<script src="/js-test-resources/js-test.js"></script>
<iframe id="target-iframe1" src="http://localhost:8080/misc/resources/cross-origin-subframe-for-scrolling.html" style="height: 100px; width: 100px; overflow-y: scroll; position: absolute; left: 50px; top: 50px"></iframe>
<iframe id="target-iframe2" src="http://localhost:8080/misc/resources/cross-origin-subframe-for-scrolling.html" style="height: 100px; width: 100px; overflow-y: scroll; position: absolute; left: 50px; top: 200px"></iframe>

<script>
description("Verify that two sibling cross-origin iframes both correctly scroll on MouseWheel events, as per https://crbug.com/675695.");

// States:
// 0 => Scroll sent to iframe1
// 1 => Fetching scroll offset from iframe1
// 2 => Scroll sent to iframe2
// 3 => Fetching scroll offset from iframe2
var state = 0;
var iframe1 = document.getElementById("target-iframe1");
var iframe2 = document.getElementById("target-iframe2");
setPrintTestResultsLazily();
self.jsTestIsAsync = true;

function handleMessage(event) {
if (state == 0) {
iframe1.contentWindow.postMessage("", "*");
state = 1;
} else if (state == 1) {
shouldBeEqualToNumber("event.data.scrollTop", 40);
state = 2;
iframe2.contentWindow.postMessage({scrollBy: -1, left: iframe2.offsetLeft, top: iframe2.offsetTop}, "*");
iframe2.contentWindow.postMessage("", "*");
} else if (state == 2) {
iframe1.contentWindow.postMessage("", "*");
state = 3;
} else if (state == 3) {
shouldBeEqualToNumber("event.data.scrollTop", 40);
finishJSTest();
}
}

window.addEventListener("message", handleMessage);

iframe1.onload = (() => {
iframe1.contentWindow.postMessage({scrollBy: -1, left: iframe1.offsetLeft, top: iframe1.offsetTop}, "*");
});

</script>
29 changes: 29 additions & 0 deletions third_party/WebKit/Source/core/frame/FrameView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -566,11 +566,30 @@ ScrollingCoordinator* FrameView::scrollingCoordinator() const {
}

CompositorAnimationHost* FrameView::compositorAnimationHost() const {
// When m_animationHost is not nullptr, this is the FrameView for an OOPIF.
if (m_animationHost)
return m_animationHost.get();

if (m_frame->localFrameRoot() != m_frame)
return m_frame->localFrameRoot()->view()->compositorAnimationHost();

if (!m_frame->isMainFrame())
return nullptr;

ScrollingCoordinator* c = scrollingCoordinator();
return c ? c->compositorAnimationHost() : nullptr;
}

CompositorAnimationTimeline* FrameView::compositorAnimationTimeline() const {
if (m_animationTimeline)
return m_animationTimeline.get();

if (m_frame->localFrameRoot() != m_frame)
return m_frame->localFrameRoot()->view()->compositorAnimationTimeline();

if (!m_frame->isMainFrame())
return nullptr;

ScrollingCoordinator* c = scrollingCoordinator();
return c ? c->compositorAnimationTimeline() : nullptr;
}
Expand Down Expand Up @@ -4999,4 +5018,14 @@ void FrameView::applyTransformForTopFrameSpace(TransformState& transformState) {
LayoutSize(-viewportIntersectionRect.x(), -viewportIntersectionRect.y()));
}

void FrameView::setAnimationTimeline(
std::unique_ptr<CompositorAnimationTimeline> timeline) {
m_animationTimeline = std::move(timeline);
}

void FrameView::setAnimationHost(
std::unique_ptr<CompositorAnimationHost> host) {
m_animationHost = std::move(host);
}

} // namespace blink
14 changes: 14 additions & 0 deletions third_party/WebKit/Source/core/frame/FrameView.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
#include "core/paint/ScrollbarManager.h"
#include "platform/RuntimeEnabledFeatures.h"
#include "platform/Widget.h"
#include "platform/animation/CompositorAnimationHost.h"
#include "platform/animation/CompositorAnimationTimeline.h"
#include "platform/geometry/IntRect.h"
#include "platform/geometry/LayoutRect.h"
#include "platform/graphics/Color.h"
Expand Down Expand Up @@ -819,6 +821,14 @@ class CORE_EXPORT FrameView final

void applyTransformForTopFrameSpace(TransformState&);

// TODO(kenrb): These are temporary methods pending resolution of
// https://crbug.com/680606. Animation timelines and hosts for scrolling
// are normally owned by ScrollingCoordinator, but there is only one
// of those objects per page. To get around this, we temporarily stash a
// unique timeline and host on each OOPIF FrameView.
void setAnimationTimeline(std::unique_ptr<CompositorAnimationTimeline>);
void setAnimationHost(std::unique_ptr<CompositorAnimationHost>);

protected:
// Scroll the content via the compositor.
bool scrollContentsFastPath(const IntSize& scrollDelta);
Expand Down Expand Up @@ -1190,6 +1200,10 @@ class CORE_EXPORT FrameView final
// The size of the vector depends on the number of
// main thread scrolling reasons.
Vector<int> m_mainThreadScrollingReasonsCounter;

// TODO(kenrb): Remove these when https://crbug.com/680606 is resolved.
std::unique_ptr<CompositorAnimationTimeline> m_animationTimeline;
std::unique_ptr<CompositorAnimationHost> m_animationHost;
};

inline void FrameView::incrementVisuallyNonEmptyCharacterCount(unsigned count) {
Expand Down
2 changes: 2 additions & 0 deletions third_party/WebKit/Source/core/frame/VisualViewport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -801,12 +801,14 @@ bool VisualViewport::shouldDisableDesktopWorkarounds() const {
}

CompositorAnimationHost* VisualViewport::compositorAnimationHost() const {
DCHECK(frameHost().page().mainFrame()->isLocalFrame());
ScrollingCoordinator* c = frameHost().page().scrollingCoordinator();
return c ? c->compositorAnimationHost() : nullptr;
}

CompositorAnimationTimeline* VisualViewport::compositorAnimationTimeline()
const {
DCHECK(frameHost().page().mainFrame()->isLocalFrame());
ScrollingCoordinator* c = frameHost().page().scrollingCoordinator();
return c ? c->compositorAnimationTimeline() : nullptr;
}
Expand Down
10 changes: 6 additions & 4 deletions third_party/WebKit/Source/core/page/Page.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -507,14 +507,16 @@ DEFINE_TRACE(Page) {
PageVisibilityNotifier::trace(visitor);
}

void Page::layerTreeViewInitialized(WebLayerTreeView& layerTreeView) {
void Page::layerTreeViewInitialized(WebLayerTreeView& layerTreeView,
FrameView* view) {
if (scrollingCoordinator())
scrollingCoordinator()->layerTreeViewInitialized(layerTreeView);
scrollingCoordinator()->layerTreeViewInitialized(layerTreeView, view);
}

void Page::willCloseLayerTreeView(WebLayerTreeView& layerTreeView) {
void Page::willCloseLayerTreeView(WebLayerTreeView& layerTreeView,
FrameView* view) {
if (m_scrollingCoordinator)
m_scrollingCoordinator->willCloseLayerTreeView(layerTreeView);
m_scrollingCoordinator->willCloseLayerTreeView(layerTreeView, view);
}

void Page::willBeDestroyed() {
Expand Down
4 changes: 2 additions & 2 deletions third_party/WebKit/Source/core/page/Page.h
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,8 @@ class CORE_EXPORT Page final : public GarbageCollectedFinalized<Page>,

DECLARE_TRACE();

void layerTreeViewInitialized(WebLayerTreeView&);
void willCloseLayerTreeView(WebLayerTreeView&);
void layerTreeViewInitialized(WebLayerTreeView&, FrameView*);
void willCloseLayerTreeView(WebLayerTreeView&, FrameView*);

void willBeDestroyed();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -508,8 +508,19 @@ bool ScrollingCoordinator::scrollableAreaScrollLayerDidChange(
isForRootLayer(scrollableArea))
m_page->chromeClient().registerViewportLayers();

scrollableArea->layerForScrollingDidChange(
m_programmaticScrollAnimatorTimeline.get());
CompositorAnimationTimeline* timeline;
// FrameView::compositorAnimationTimeline() can indirectly return
// m_programmaticScrollAnimatorTimeline if it does not have its own
// timeline.
if (scrollableArea->isFrameView()) {
timeline = toFrameView(scrollableArea)->compositorAnimationTimeline();
} else if (scrollableArea->isPaintLayerScrollableArea()) {
timeline = toPaintLayerScrollableArea(scrollableArea)
->compositorAnimationTimeline();
} else {
timeline = m_programmaticScrollAnimatorTimeline.get();
}
scrollableArea->layerForScrollingDidChange(timeline);

return !!webLayer;
}
Expand Down Expand Up @@ -852,20 +863,37 @@ void ScrollingCoordinator::setShouldUpdateScrollLayerPositionOnMainThread(
}

void ScrollingCoordinator::layerTreeViewInitialized(
WebLayerTreeView& layerTreeView) {
WebLayerTreeView& layerTreeView,
FrameView* view) {
if (Platform::current()->isThreadedAnimationEnabled() &&
layerTreeView.compositorAnimationHost()) {
m_animationHost = WTF::makeUnique<CompositorAnimationHost>(
layerTreeView.compositorAnimationHost());
m_programmaticScrollAnimatorTimeline =
std::unique_ptr<CompositorAnimationTimeline> timeline =
CompositorAnimationTimeline::create();
m_animationHost->addTimeline(*m_programmaticScrollAnimatorTimeline.get());
std::unique_ptr<CompositorAnimationHost> host =
WTF::makeUnique<CompositorAnimationHost>(
layerTreeView.compositorAnimationHost());
if (view && view->frame().localFrameRoot() != m_page->mainFrame()) {
view->setAnimationHost(std::move(host));
view->setAnimationTimeline(std::move(timeline));
view->compositorAnimationHost()->addTimeline(
*view->compositorAnimationTimeline());
} else {
m_animationHost = std::move(host);
m_programmaticScrollAnimatorTimeline = std::move(timeline);
m_animationHost->addTimeline(*m_programmaticScrollAnimatorTimeline.get());
}
}
}

void ScrollingCoordinator::willCloseLayerTreeView(
WebLayerTreeView& layerTreeView) {
if (m_programmaticScrollAnimatorTimeline) {
WebLayerTreeView& layerTreeView,
FrameView* view) {
if (view && view->frame().localFrameRoot() != m_page->mainFrame()) {
view->compositorAnimationHost()->removeTimeline(
*view->compositorAnimationTimeline());
view->setAnimationTimeline(nullptr);
view->setAnimationHost(nullptr);
} else if (m_programmaticScrollAnimatorTimeline) {
m_animationHost->removeTimeline(
*m_programmaticScrollAnimatorTimeline.get());
m_programmaticScrollAnimatorTimeline = nullptr;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,12 @@ class CORE_EXPORT ScrollingCoordinator final
~ScrollingCoordinator();
DECLARE_TRACE();

void layerTreeViewInitialized(WebLayerTreeView&);
void willCloseLayerTreeView(WebLayerTreeView&);
// The FrameView argument is optional, nullptr causes the the scrolling
// animation host and timeline to be owned by the ScrollingCoordinator. When
// not null, the host and timeline are attached to the specified FrameView.
// A FrameView only needs to own them when it is the view for an OOPIF.
void layerTreeViewInitialized(WebLayerTreeView&, FrameView*);
void willCloseLayerTreeView(WebLayerTreeView&, FrameView*);

void willBeDestroyed();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1882,18 +1882,12 @@ void PaintLayerScrollableArea::resetRebuildScrollbarLayerFlags() {

CompositorAnimationHost* PaintLayerScrollableArea::compositorAnimationHost()
const {
if (ScrollingCoordinator* coordinator = getScrollingCoordinator())
return coordinator->compositorAnimationHost();

return nullptr;
return m_layer.layoutObject()->frameView()->compositorAnimationHost();
}

CompositorAnimationTimeline*
PaintLayerScrollableArea::compositorAnimationTimeline() const {
if (ScrollingCoordinator* coordinator = getScrollingCoordinator())
return coordinator->compositorAnimationTimeline();

return nullptr;
return m_layer.layoutObject()->frameView()->compositorAnimationTimeline();
}

PaintLayerScrollableArea*
Expand Down
12 changes: 8 additions & 4 deletions third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -651,8 +651,10 @@ bool WebFrameWidgetImpl::isAcceleratedCompositingActive() const {
}

void WebFrameWidgetImpl::willCloseLayerTreeView() {
if (m_layerTreeView)
page()->willCloseLayerTreeView(*m_layerTreeView);
if (m_layerTreeView) {
page()->willCloseLayerTreeView(*m_layerTreeView,
m_localRoot->frame()->view());
}

setIsAcceleratedCompositingActive(false);
m_mutator = nullptr;
Expand Down Expand Up @@ -996,8 +998,10 @@ void WebFrameWidgetImpl::initializeLayerTreeView() {
devTools->layerTreeViewChanged(m_layerTreeView);

page()->settings().setAcceleratedCompositingEnabled(m_layerTreeView);
if (m_layerTreeView)
page()->layerTreeViewInitialized(*m_layerTreeView);
if (m_layerTreeView) {
page()->layerTreeViewInitialized(*m_layerTreeView,
m_localRoot->frame()->view());
}

// FIXME: only unittests, click to play, Android priting, and printing (for
// headers and footers) make this assert necessary. We should make them not
Expand Down
17 changes: 14 additions & 3 deletions third_party/WebKit/Source/web/WebPagePopupImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,12 @@ void WebPagePopupImpl::setIsAcceleratedCompositingActive(bool enter) {
m_isAcceleratedCompositingActive = true;
m_animationHost = WTF::makeUnique<CompositorAnimationHost>(
m_layerTreeView->compositorAnimationHost());
m_page->layerTreeViewInitialized(*m_layerTreeView);
m_page->layerTreeViewInitialized(*m_layerTreeView,
m_popupClient->ownerElement()
.document()
.frame()
->localFrameRoot()
->view());
} else {
m_isAcceleratedCompositingActive = false;
m_animationHost = nullptr;
Expand All @@ -412,8 +417,14 @@ void WebPagePopupImpl::beginFrame(double lastFrameTimeMonotonic) {
}

void WebPagePopupImpl::willCloseLayerTreeView() {
if (m_page && m_layerTreeView)
m_page->willCloseLayerTreeView(*m_layerTreeView);
if (m_page && m_layerTreeView) {
m_page->willCloseLayerTreeView(*m_layerTreeView,
m_popupClient->ownerElement()
.document()
.frame()
->localFrameRoot()
->view());
}

setIsAcceleratedCompositingActive(false);
m_layerTreeView = nullptr;
Expand Down
4 changes: 2 additions & 2 deletions third_party/WebKit/Source/web/WebViewImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2515,7 +2515,7 @@ void WebViewImpl::willCloseLayerTreeView() {
}

if (m_layerTreeView)
page()->willCloseLayerTreeView(*m_layerTreeView);
page()->willCloseLayerTreeView(*m_layerTreeView, nullptr);

setRootLayer(nullptr);
m_animationHost = nullptr;
Expand Down Expand Up @@ -3956,7 +3956,7 @@ void WebViewImpl::initializeLayerTreeView() {

m_page->settings().setAcceleratedCompositingEnabled(m_layerTreeView);
if (m_layerTreeView)
m_page->layerTreeViewInitialized(*m_layerTreeView);
m_page->layerTreeViewInitialized(*m_layerTreeView, nullptr);

// FIXME: only unittests, click to play, Android printing, and printing (for
// headers and footers) make this assert necessary. We should make them not
Expand Down

0 comments on commit 66ec94e

Please sign in to comment.