Commit 4e2f14c7 authored by bokan's avatar bokan Committed by Commit bot

Add some basic sanity tests to RootScroller for cases with remote frames.

Added one test that deals with a remote iframe and another with a remote main
frame. RootScroller doesn't quite work in OOPIF scenarios yet but it's still
behind a flag so that's ok. These are mostly testing that we don't crash in the
normal case and that scrolling works ok.

BUG=641775

Review-Url: https://codereview.chromium.org/2290173004
Cr-Commit-Position: refs/heads/master@{#416364}
parent dcb4915a
...@@ -15,10 +15,12 @@ ...@@ -15,10 +15,12 @@
#include "public/platform/WebURLLoaderMockFactory.h" #include "public/platform/WebURLLoaderMockFactory.h"
#include "public/web/WebCache.h" #include "public/web/WebCache.h"
#include "public/web/WebConsoleMessage.h" #include "public/web/WebConsoleMessage.h"
#include "public/web/WebRemoteFrame.h"
#include "public/web/WebScriptSource.h" #include "public/web/WebScriptSource.h"
#include "public/web/WebSettings.h" #include "public/web/WebSettings.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "web/WebLocalFrameImpl.h" #include "web/WebLocalFrameImpl.h"
#include "web/WebRemoteFrameImpl.h"
#include "web/tests/FrameTestHelpers.h" #include "web/tests/FrameTestHelpers.h"
#include "wtf/Vector.h" #include "wtf/Vector.h"
...@@ -29,11 +31,6 @@ namespace blink { ...@@ -29,11 +31,6 @@ namespace blink {
namespace { namespace {
class RootScrollerTestWebViewClient : public FrameTestHelpers::TestWebViewClient {
public:
MOCK_METHOD4(didOverscroll, void(const WebFloatSize&, const WebFloatSize&, const WebFloatPoint&, const WebFloatSize&));
};
class RootScrollerTest : public ::testing::Test { class RootScrollerTest : public ::testing::Test {
public: public:
RootScrollerTest() RootScrollerTest()
...@@ -52,14 +49,13 @@ public: ...@@ -52,14 +49,13 @@ public:
WebCache::clear(); WebCache::clear();
} }
WebViewImpl* initialize(const std::string& pageName) WebViewImpl* initialize(const std::string& pageName,
FrameTestHelpers::TestWebViewClient* client)
{ {
RuntimeEnabledFeatures::setSetRootScrollerEnabled(true); RuntimeEnabledFeatures::setSetRootScrollerEnabled(true);
// Load a page with large body and set viewport size to 400x400 to
// ensure main frame is scrollable.
m_helper.initializeAndLoad( m_helper.initializeAndLoad(
m_baseURL + pageName, true, nullptr, &m_client, nullptr, m_baseURL + pageName, true, nullptr, client, nullptr,
&configureSettings); &configureSettings);
// Initialize top controls to be shown. // Initialize top controls to be shown.
...@@ -71,6 +67,11 @@ public: ...@@ -71,6 +67,11 @@ public:
return webViewImpl(); return webViewImpl();
} }
WebViewImpl* initialize(const std::string& pageName)
{
return initialize(pageName, &m_client);
}
static void configureSettings(WebSettings* settings) static void configureSettings(WebSettings* settings)
{ {
settings->setJavaScriptEnabled(true); settings->setJavaScriptEnabled(true);
...@@ -97,31 +98,6 @@ public: ...@@ -97,31 +98,6 @@ public:
runPendingTasks(); runPendingTasks();
} }
WebGestureEvent generateEvent(
WebInputEvent::Type type, int deltaX = 0, int deltaY = 0)
{
WebGestureEvent event;
event.type = type;
event.sourceDevice = WebGestureDeviceTouchscreen;
event.x = 100;
event.y = 100;
if (type == WebInputEvent::GestureScrollUpdate) {
event.data.scrollUpdate.deltaX = deltaX;
event.data.scrollUpdate.deltaY = deltaY;
}
return event;
}
void verticalScroll(float deltaY)
{
webViewImpl()->handleInputEvent(
generateEvent(WebInputEvent::GestureScrollBegin));
webViewImpl()->handleInputEvent(
generateEvent(WebInputEvent::GestureScrollUpdate, 0, -deltaY));
webViewImpl()->handleInputEvent(
generateEvent(WebInputEvent::GestureScrollEnd));
}
WebViewImpl* webViewImpl() const WebViewImpl* webViewImpl() const
{ {
return m_helper.webView(); return m_helper.webView();
...@@ -162,9 +138,45 @@ public: ...@@ -162,9 +138,45 @@ public:
return doc->rootScrollerController()->effectiveRootScroller(); return doc->rootScrollerController()->effectiveRootScroller();
} }
WebGestureEvent generateTouchGestureEvent(
WebInputEvent::Type type,
int deltaX = 0,
int deltaY = 0)
{
return generateGestureEvent(
type, WebGestureDeviceTouchscreen, deltaX, deltaY);
}
WebGestureEvent generateWheelGestureEvent(
WebInputEvent::Type type,
int deltaX = 0,
int deltaY = 0)
{
return generateGestureEvent(
type, WebGestureDeviceTouchpad, deltaX, deltaY);
}
protected: protected:
WebGestureEvent generateGestureEvent(
WebInputEvent::Type type,
WebGestureDevice device,
int deltaX,
int deltaY)
{
WebGestureEvent event;
event.type = type;
event.sourceDevice = device;
event.x = 100;
event.y = 100;
if (type == WebInputEvent::GestureScrollUpdate) {
event.data.scrollUpdate.deltaX = deltaX;
event.data.scrollUpdate.deltaY = deltaY;
}
return event;
}
std::string m_baseURL; std::string m_baseURL;
RootScrollerTestWebViewClient m_client; FrameTestHelpers::TestWebViewClient m_client;
FrameTestHelpers::WebViewHelper m_helper; FrameTestHelpers::WebViewHelper m_helper;
RuntimeEnabledFeatures::Backup m_featuresBackup; RuntimeEnabledFeatures::Backup m_featuresBackup;
}; };
...@@ -182,11 +194,17 @@ TEST_F(RootScrollerTest, TestDefaultRootScroller) ...@@ -182,11 +194,17 @@ TEST_F(RootScrollerTest, TestDefaultRootScroller)
EXPECT_EQ(htmlElement, effectiveRootScroller(mainFrame()->document())); EXPECT_EQ(htmlElement, effectiveRootScroller(mainFrame()->document()));
} }
class OverscrollTestWebViewClient : public FrameTestHelpers::TestWebViewClient {
public:
MOCK_METHOD4(didOverscroll, void(const WebFloatSize&, const WebFloatSize&, const WebFloatPoint&, const WebFloatSize&));
};
// Tests that setting an element as the root scroller causes it to control url // Tests that setting an element as the root scroller causes it to control url
// bar hiding and overscroll. // bar hiding and overscroll.
TEST_F(RootScrollerTest, TestSetRootScroller) TEST_F(RootScrollerTest, TestSetRootScroller)
{ {
initialize("root-scroller.html"); OverscrollTestWebViewClient client;
initialize("root-scroller.html", &client);
Element* container = mainFrame()->document()->getElementById("container"); Element* container = mainFrame()->document()->getElementById("container");
TrackExceptionState exceptionState; TrackExceptionState exceptionState;
...@@ -197,21 +215,21 @@ TEST_F(RootScrollerTest, TestSetRootScroller) ...@@ -197,21 +215,21 @@ TEST_F(RootScrollerTest, TestSetRootScroller)
double maximumScroll = 600; double maximumScroll = 600;
webViewImpl()->handleInputEvent( webViewImpl()->handleInputEvent(
generateEvent(WebInputEvent::GestureScrollBegin)); generateTouchGestureEvent(WebInputEvent::GestureScrollBegin));
{ {
// Scrolling over the #container DIV should cause the top controls to // Scrolling over the #container DIV should cause the top controls to
// hide. // hide.
EXPECT_FLOAT_EQ(1, topControls().shownRatio()); EXPECT_FLOAT_EQ(1, topControls().shownRatio());
webViewImpl()->handleInputEvent(generateEvent( webViewImpl()->handleInputEvent(generateTouchGestureEvent(
WebInputEvent::GestureScrollUpdate, 0, -topControls().height())); WebInputEvent::GestureScrollUpdate, 0, -topControls().height()));
EXPECT_FLOAT_EQ(0, topControls().shownRatio()); EXPECT_FLOAT_EQ(0, topControls().shownRatio());
} }
{ {
// Make sure we're actually scrolling the DIV and not the FrameView. // Make sure we're actually scrolling the DIV and not the FrameView.
webViewImpl()->handleInputEvent( webViewImpl()->handleInputEvent(generateTouchGestureEvent(
generateEvent(WebInputEvent::GestureScrollUpdate, 0, -100)); WebInputEvent::GestureScrollUpdate, 0, -100));
EXPECT_FLOAT_EQ(100, container->scrollTop()); EXPECT_FLOAT_EQ(100, container->scrollTop());
EXPECT_FLOAT_EQ(0, mainFrameView()->scrollPositionDouble().y()); EXPECT_FLOAT_EQ(0, mainFrameView()->scrollPositionDouble().y());
} }
...@@ -219,72 +237,75 @@ TEST_F(RootScrollerTest, TestSetRootScroller) ...@@ -219,72 +237,75 @@ TEST_F(RootScrollerTest, TestSetRootScroller)
{ {
// Scroll 50 pixels past the end. Ensure we report the 50 pixels as // Scroll 50 pixels past the end. Ensure we report the 50 pixels as
// overscroll. // overscroll.
EXPECT_CALL(m_client, EXPECT_CALL(client,
didOverscroll( didOverscroll(
WebFloatSize(0, 50), WebFloatSize(0, 50),
WebFloatSize(0, 50), WebFloatSize(0, 50),
WebFloatPoint(100, 100), WebFloatPoint(100, 100),
WebFloatSize())); WebFloatSize()));
webViewImpl()->handleInputEvent( webViewImpl()->handleInputEvent(generateTouchGestureEvent(
generateEvent(WebInputEvent::GestureScrollUpdate, 0, -550)); WebInputEvent::GestureScrollUpdate, 0, -550));
EXPECT_FLOAT_EQ(maximumScroll, container->scrollTop()); EXPECT_FLOAT_EQ(maximumScroll, container->scrollTop());
EXPECT_FLOAT_EQ(0, mainFrameView()->scrollPositionDouble().y()); EXPECT_FLOAT_EQ(0, mainFrameView()->scrollPositionDouble().y());
Mock::VerifyAndClearExpectations(&m_client); Mock::VerifyAndClearExpectations(&client);
} }
{ {
// Continue the gesture overscroll. // Continue the gesture overscroll.
EXPECT_CALL(m_client, EXPECT_CALL(client,
didOverscroll( didOverscroll(
WebFloatSize(0, 20), WebFloatSize(0, 20),
WebFloatSize(0, 70), WebFloatSize(0, 70),
WebFloatPoint(100, 100), WebFloatPoint(100, 100),
WebFloatSize())); WebFloatSize()));
webViewImpl()->handleInputEvent( webViewImpl()->handleInputEvent(generateTouchGestureEvent(
generateEvent(WebInputEvent::GestureScrollUpdate, 0, -20)); WebInputEvent::GestureScrollUpdate, 0, -20));
EXPECT_FLOAT_EQ(maximumScroll, container->scrollTop()); EXPECT_FLOAT_EQ(maximumScroll, container->scrollTop());
EXPECT_FLOAT_EQ(0, mainFrameView()->scrollPositionDouble().y()); EXPECT_FLOAT_EQ(0, mainFrameView()->scrollPositionDouble().y());
Mock::VerifyAndClearExpectations(&m_client); Mock::VerifyAndClearExpectations(&client);
} }
webViewImpl()->handleInputEvent( webViewImpl()->handleInputEvent(generateTouchGestureEvent(
generateEvent(WebInputEvent::GestureScrollEnd)); WebInputEvent::GestureScrollEnd));
{ {
// Make sure a new gesture scroll still won't scroll the frameview and // Make sure a new gesture scroll still won't scroll the frameview and
// overscrolls. // overscrolls.
webViewImpl()->handleInputEvent( webViewImpl()->handleInputEvent(generateTouchGestureEvent(
generateEvent(WebInputEvent::GestureScrollBegin)); WebInputEvent::GestureScrollBegin));
EXPECT_CALL(m_client, EXPECT_CALL(client,
didOverscroll( didOverscroll(
WebFloatSize(0, 30), WebFloatSize(0, 30),
WebFloatSize(0, 30), WebFloatSize(0, 30),
WebFloatPoint(100, 100), WebFloatPoint(100, 100),
WebFloatSize())); WebFloatSize()));
webViewImpl()->handleInputEvent( webViewImpl()->handleInputEvent(generateTouchGestureEvent(
generateEvent(WebInputEvent::GestureScrollUpdate, 0, -30)); WebInputEvent::GestureScrollUpdate, 0, -30));
EXPECT_FLOAT_EQ(maximumScroll, container->scrollTop()); EXPECT_FLOAT_EQ(maximumScroll, container->scrollTop());
EXPECT_FLOAT_EQ(0, mainFrameView()->scrollPositionDouble().y()); EXPECT_FLOAT_EQ(0, mainFrameView()->scrollPositionDouble().y());
Mock::VerifyAndClearExpectations(&m_client); Mock::VerifyAndClearExpectations(&client);
webViewImpl()->handleInputEvent( webViewImpl()->handleInputEvent(generateTouchGestureEvent(
generateEvent(WebInputEvent::GestureScrollEnd)); WebInputEvent::GestureScrollEnd));
} }
{ {
// Scrolling up should show the top controls. // Scrolling up should show the top controls.
webViewImpl()->handleInputEvent( webViewImpl()->handleInputEvent(generateTouchGestureEvent(
generateEvent(WebInputEvent::GestureScrollBegin)); WebInputEvent::GestureScrollBegin));
EXPECT_FLOAT_EQ(0, topControls().shownRatio()); EXPECT_FLOAT_EQ(0, topControls().shownRatio());
webViewImpl()->handleInputEvent( webViewImpl()->handleInputEvent(generateTouchGestureEvent(
generateEvent(WebInputEvent::GestureScrollUpdate, 0, 30)); WebInputEvent::GestureScrollUpdate, 0, 30));
EXPECT_FLOAT_EQ(0.6, topControls().shownRatio()); EXPECT_FLOAT_EQ(0.6, topControls().shownRatio());
webViewImpl()->handleInputEvent( webViewImpl()->handleInputEvent(generateTouchGestureEvent(
generateEvent(WebInputEvent::GestureScrollEnd)); WebInputEvent::GestureScrollEnd));
} }
// Reset manually to avoid lifetime issues with custom WebViewClient.
m_helper.reset();
} }
// Tests that removing the element that is the root scroller from the DOM tree // Tests that removing the element that is the root scroller from the DOM tree
...@@ -502,6 +523,7 @@ TEST_F(RootScrollerTest, SetRootScrollerIframeBecomesEffective) ...@@ -502,6 +523,7 @@ TEST_F(RootScrollerTest, SetRootScrollerIframeBecomesEffective)
// root scroller layer and that the viewport apply scroll is set on it. // root scroller layer and that the viewport apply scroll is set on it.
TEST_F(RootScrollerTest, SetRootScrollerIframeUsesCorrectLayerAndCallback) TEST_F(RootScrollerTest, SetRootScrollerIframeUsesCorrectLayerAndCallback)
{ {
// TODO(bokan): The expectation and actual in the checks here are backwards.
initialize("root-scroller-iframe.html"); initialize("root-scroller-iframe.html");
ASSERT_EQ(nullptr, mainFrame()->document()->rootScroller()); ASSERT_EQ(nullptr, mainFrame()->document()->rootScroller());
...@@ -633,6 +655,107 @@ TEST_F(RootScrollerTest, DISABLED_TestSetRootScrollerOnElementFromOutsideIframe) ...@@ -633,6 +655,107 @@ TEST_F(RootScrollerTest, DISABLED_TestSetRootScrollerOnElementFromOutsideIframe)
} }
} }
// Do a basic sanity check that setting as root scroller an iframe that's remote
// doesn't crash or otherwise fail catastrophically.
TEST_F(RootScrollerTest, RemoteIFrame)
{
FrameTestHelpers::TestWebRemoteFrameClient remoteFrameClient;
initialize("root-scroller-iframe.html");
// Initialization: Replace the iframe with a remote frame.
{
WebRemoteFrame* remoteFrame = WebRemoteFrame::create(
WebTreeScopeType::Document, &remoteFrameClient);
WebFrame* childFrame = mainWebFrame()->firstChild();
childFrame->swap(remoteFrame);
}
// Set the root scroller in the local main frame to the iframe (which is
// remote).
{
Element* iframe = mainFrame()->document()->getElementById("iframe");
NonThrowableExceptionState nonThrow;
mainFrame()->document()->setRootScroller(iframe, nonThrow);
EXPECT_EQ(iframe, mainFrame()->document()->rootScroller());
}
// Reset explicitly to prevent lifetime issues with the RemoteFrameClient.
m_helper.reset();
}
// Do a basic sanity check that the scrolling and root scroller machinery
// doesn't fail catastrophically in site isolation when the main frame is
// remote. Setting a root scroller in OOPIF isn't implemented yet but we should
// still scroll as before and not crash.
TEST_F(RootScrollerTest, RemoteMainFrame)
{
FrameTestHelpers::TestWebRemoteFrameClient remoteClient;
FrameTestHelpers::TestWebWidgetClient webWidgetClient;
WebFrameWidget* widget;
WebLocalFrameImpl* localFrame;
initialize("root-scroller-iframe.html");
// Initialization: Set the main frame to be a RemoteFrame and add a local
// child.
{
webViewImpl()->setMainFrame(remoteClient.frame());
WebRemoteFrame* root = webViewImpl()->mainFrame()->toWebRemoteFrame();
root->setReplicatedOrigin(SecurityOrigin::createUnique());
WebFrameOwnerProperties properties;
localFrame = FrameTestHelpers::createLocalChild(
root, "frameName", nullptr, nullptr, nullptr, properties);
FrameTestHelpers::loadFrame(
localFrame, m_baseURL + "root-scroller-child.html");
widget = localFrame->frameWidget();
widget->resize(WebSize(400, 400));
}
Document* document = localFrame->frameView()->frame().document();
Element* container = document->getElementById("container");
// Try scrolling in the iframe.
{
widget->handleInputEvent(generateWheelGestureEvent(
WebInputEvent::GestureScrollBegin));
widget->handleInputEvent(generateWheelGestureEvent(
WebInputEvent::GestureScrollUpdate, 0, -100));
widget->handleInputEvent(generateWheelGestureEvent(
WebInputEvent::GestureScrollEnd));
EXPECT_EQ(100, container->scrollTop());
}
// Set the container Element as the root scroller.
{
NonThrowableExceptionState nonThrow;
document->setRootScroller(container, nonThrow);
EXPECT_EQ(container, document->rootScroller());
}
// Try scrolling in the iframe now that it has a root scroller set.
{
widget->handleInputEvent(generateWheelGestureEvent(
WebInputEvent::GestureScrollBegin));
widget->handleInputEvent(generateWheelGestureEvent(
WebInputEvent::GestureScrollUpdate, 0, -100));
widget->handleInputEvent(generateWheelGestureEvent(
WebInputEvent::GestureScrollEnd));
// TODO(bokan): This doesn't work right now because we notice in
// Element::nativeApplyScroll that the container is the
// effectiveRootScroller but the only way we expect to get to
// nativeApplyScroll is if the effective scroller had its applyScroll
// ViewportScrollCallback removed. Keep the scrolls to guard crashes
// but the expectations on when a ViewportScrollCallback have changed
// and should be updated.
// EXPECT_EQ(200, container->scrollTop());
}
// Reset explicitly to prevent lifetime issues with the RemoteFrameClient.
m_helper.reset();
}
} // namespace } // namespace
} // namespace blink } // namespace blink
...@@ -23,8 +23,8 @@ ...@@ -23,8 +23,8 @@
} }
#spacer { #spacer {
width: 400px; width: 800px;
height: 400px; height: 800px;
} }
</style> </style>
</head> </head>
......
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