Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(174)

Unified Diff: third_party/WebKit/Source/web/tests/RootScrollerTest.cpp

Issue 1913843004: Implementing document.setRootScroller API for main thread scrolling. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@overscrollController
Patch Set: Rebase and remove whitespace in Document.idl Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/web/tests/RootScrollerTest.cpp
diff --git a/third_party/WebKit/Source/web/tests/RootScrollerTest.cpp b/third_party/WebKit/Source/web/tests/RootScrollerTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..70f4f792caa064a522635e0a4ffb992ba30ee37b
--- /dev/null
+++ b/third_party/WebKit/Source/web/tests/RootScrollerTest.cpp
@@ -0,0 +1,489 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/page/scrolling/RootScroller.h"
+
+#include "core/frame/FrameHost.h"
+#include "core/frame/FrameView.h"
+#include "core/frame/TopControls.h"
+#include "core/html/HTMLFrameOwnerElement.h"
+#include "core/page/Page.h"
+#include "platform/testing/URLTestHelpers.h"
+#include "platform/testing/UnitTestHelpers.h"
+#include "public/platform/Platform.h"
+#include "public/platform/WebURLLoaderMockFactory.h"
+#include "public/web/WebCache.h"
+#include "public/web/WebConsoleMessage.h"
+#include "public/web/WebScriptSource.h"
+#include "public/web/WebSettings.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "web/WebLocalFrameImpl.h"
+#include "web/tests/FrameTestHelpers.h"
+#include "wtf/Vector.h"
+
+using blink::testing::runPendingTasks;
+using testing::Mock;
+
+namespace blink {
+
+namespace {
+
+class RootScrollerTestWebViewClient : public FrameTestHelpers::TestWebViewClient {
+public:
+ MOCK_METHOD4(didOverscroll, void(const WebFloatSize&, const WebFloatSize&, const WebFloatPoint&, const WebFloatSize&));
+};
+
+class RootScrollerTest : public ::testing::Test {
+public:
+ RootScrollerTest()
+ : m_baseURL("http://www.test.com/")
+ {
+ registerMockedHttpURLLoad("overflow-scrolling.html");
+ registerMockedHttpURLLoad("root-scroller.html");
+ registerMockedHttpURLLoad("root-scroller-iframe.html");
+ registerMockedHttpURLLoad("root-scroller-child.html");
+ }
+
+ ~RootScrollerTest() override
+ {
+ m_featuresBackup.restore();
+ Platform::current()->getURLLoaderMockFactory()->unregisterAllURLs();
+ WebCache::clear();
+ }
+
+ WebViewImpl* initialize(const std::string& pageName)
+ {
+ 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_baseURL + pageName, true, 0, &m_client, &configureSettings);
+
+ // Initialize top controls to be shown.
+ webViewImpl()->resizeWithTopControls(IntSize(400, 400), 50, true);
+ webViewImpl()->topControls().setShownRatio(1);
+
+ mainFrameView()->updateAllLifecyclePhases();
+
+ return webViewImpl();
+ }
+
+ static void configureSettings(WebSettings* settings)
+ {
+ settings->setJavaScriptEnabled(true);
+ settings->setAcceleratedCompositingEnabled(true);
+ settings->setPreferCompositingToLCDTextEnabled(true);
+ // Android settings.
+ settings->setViewportEnabled(true);
+ settings->setViewportMetaEnabled(true);
+ settings->setShrinksViewportContentToFit(true);
+ settings->setMainFrameResizesAreOrientationChanges(true);
+ }
+
+ void registerMockedHttpURLLoad(const std::string& fileName)
+ {
+ URLTestHelpers::registerMockedURLFromBaseURL(
+ WebString::fromUTF8(m_baseURL.c_str()),
+ WebString::fromUTF8(fileName.c_str()));
+ }
+
+ void executeScript(const WebString& code)
+ {
+ mainWebFrame()->executeScript(WebScriptSource(code));
+ mainWebFrame()->view()->updateAllLifecyclePhases();
+ 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
+ {
+ return m_helper.webViewImpl();
+ }
+
+ FrameHost& frameHost() const
+ {
+ return m_helper.webViewImpl()->page()->frameHost();
+ }
+
+ LocalFrame* mainFrame() const
+ {
+ return toWebLocalFrameImpl(webViewImpl()->mainFrame())->frame();
+ }
+
+ WebLocalFrame* mainWebFrame() const
+ {
+ return toWebLocalFrameImpl(webViewImpl()->mainFrame());
+ }
+
+ FrameView* mainFrameView() const
+ {
+ return webViewImpl()->mainFrameImpl()->frame()->view();
+ }
+
+ VisualViewport& visualViewport() const
+ {
+ return frameHost().visualViewport();
+ }
+
+ RootScroller& rootScroller() const
+ {
+ return *frameHost().rootScroller();
+ }
+
+ TopControls& topControls() const
+ {
+ return frameHost().topControls();
+ }
+
+protected:
+ std::string m_baseURL;
+ RootScrollerTestWebViewClient m_client;
+ FrameTestHelpers::WebViewHelper m_helper;
+ RuntimeEnabledFeatures::Backup m_featuresBackup;
+};
+
+// Test that a default root scroller element is set if setRootScroller isn't
+// called on any elements.
+TEST_F(RootScrollerTest, TestDefaultRootScroller)
+{
+ initialize("overflow-scrolling.html");
+
+ EXPECT_EQ(
+ mainFrame()->document()->documentElement(),
+ rootScroller().get());
+}
+
+// Tests that setting an element as the root scroller causes it to control url
+// bar hiding and overscroll.
+TEST_F(RootScrollerTest, TestSetRootScroller)
+{
+ initialize("root-scroller.html");
+
+ Element* container = mainFrame()->document()->getElementById("container");
+ TrackExceptionState exceptionState;
+ mainFrame()->document()->setRootScroller(container, exceptionState);
+ ASSERT_EQ(container, mainFrame()->document()->rootScroller());
+
+ // Content is 1000x1000, WebView size is 400x400 so max scroll is 600px.
+ double maximumScroll = 600;
+
+ webViewImpl()->handleInputEvent(
+ generateEvent(WebInputEvent::GestureScrollBegin));
+
+ {
+ // Scrolling over the #container DIV should cause the top controls to
+ // hide.
+ ASSERT_FLOAT_EQ(1, topControls().shownRatio());
+ webViewImpl()->handleInputEvent(generateEvent(
+ WebInputEvent::GestureScrollUpdate, 0, -topControls().height()));
+ ASSERT_FLOAT_EQ(0, topControls().shownRatio());
+ }
+
+ {
+ // Make sure we're actually scrolling the DIV and not the FrameView.
+ webViewImpl()->handleInputEvent(
+ generateEvent(WebInputEvent::GestureScrollUpdate, 0, -100));
+ ASSERT_FLOAT_EQ(100, container->scrollTop());
+ ASSERT_FLOAT_EQ(0, mainFrameView()->scrollPositionDouble().y());
+ }
+
+ {
+ // Scroll 50 pixels past the end. Ensure we report the 50 pixels as
+ // overscroll.
+ EXPECT_CALL(m_client,
+ didOverscroll(
+ WebFloatSize(0, 50),
+ WebFloatSize(0, 50),
+ WebFloatPoint(100, 100),
+ WebFloatSize()));
+ webViewImpl()->handleInputEvent(
+ generateEvent(WebInputEvent::GestureScrollUpdate, 0, -550));
+ ASSERT_FLOAT_EQ(maximumScroll, container->scrollTop());
+ ASSERT_FLOAT_EQ(0, mainFrameView()->scrollPositionDouble().y());
+ Mock::VerifyAndClearExpectations(&m_client);
+ }
+
+ {
+ // Continue the gesture overscroll.
+ EXPECT_CALL(m_client,
+ didOverscroll(
+ WebFloatSize(0, 20),
+ WebFloatSize(0, 70),
+ WebFloatPoint(100, 100),
+ WebFloatSize()));
+ webViewImpl()->handleInputEvent(
+ generateEvent(WebInputEvent::GestureScrollUpdate, 0, -20));
+ ASSERT_FLOAT_EQ(maximumScroll, container->scrollTop());
+ ASSERT_FLOAT_EQ(0, mainFrameView()->scrollPositionDouble().y());
+ Mock::VerifyAndClearExpectations(&m_client);
+ }
+
+ webViewImpl()->handleInputEvent(
+ generateEvent(WebInputEvent::GestureScrollEnd));
+
+ {
+ // Make sure a new gesture scroll still won't scroll the frameview and
+ // overscrolls.
+ webViewImpl()->handleInputEvent(
+ generateEvent(WebInputEvent::GestureScrollBegin));
+
+ EXPECT_CALL(m_client,
+ didOverscroll(
+ WebFloatSize(0, 30),
+ WebFloatSize(0, 30),
+ WebFloatPoint(100, 100),
+ WebFloatSize()));
+ webViewImpl()->handleInputEvent(
+ generateEvent(WebInputEvent::GestureScrollUpdate, 0, -30));
+ ASSERT_FLOAT_EQ(maximumScroll, container->scrollTop());
+ ASSERT_FLOAT_EQ(0, mainFrameView()->scrollPositionDouble().y());
+ Mock::VerifyAndClearExpectations(&m_client);
+
+ webViewImpl()->handleInputEvent(
+ generateEvent(WebInputEvent::GestureScrollEnd));
+ }
+
+ {
+ // Scrolling up should show the top controls.
+ webViewImpl()->handleInputEvent(
+ generateEvent(WebInputEvent::GestureScrollBegin));
+
+ ASSERT_FLOAT_EQ(0, topControls().shownRatio());
+ webViewImpl()->handleInputEvent(
+ generateEvent(WebInputEvent::GestureScrollUpdate, 0, 30));
+ ASSERT_FLOAT_EQ(0.6, topControls().shownRatio());
+
+ webViewImpl()->handleInputEvent(
+ generateEvent(WebInputEvent::GestureScrollEnd));
+ }
+}
+
+// Tests that removing the element that is the root scroller from the DOM tree
+// resets the default element to be the root scroller.
+TEST_F(RootScrollerTest, TestRemoveRootScrollerFromDom)
+{
+ initialize("root-scroller.html");
+
+ ASSERT_EQ(
+ mainFrame()->document()->documentElement(),
+ rootScroller().get());
+
+ Element* container = mainFrame()->document()->getElementById("container");
+ TrackExceptionState exceptionState;
+ mainFrame()->document()->setRootScroller(container, exceptionState);
+
+ ASSERT_EQ(container, mainFrame()->document()->rootScroller());
+
+ mainFrame()->document()->body()->removeChild(container);
+
+ ASSERT_EQ(
+ mainFrame()->document()->documentElement(),
+ mainFrame()->document()->rootScroller());
+}
+
+// Tests that setting an element that isn't a valid scroller as the root
+// scroller fails and doesn't change the current root scroller.
+TEST_F(RootScrollerTest, TestSetRootScrollerOnInvalidElement)
+{
+ initialize("root-scroller.html");
+
+ {
+ // Set to a non-block element. Should be rejected and a console message
+ // logged.
+ Element* element = mainFrame()->document()->getElementById("nonBlock");
+ TrackExceptionState exceptionState;
+ mainFrame()->document()->setRootScroller(element, exceptionState);
+ ASSERT_EQ(
+ mainFrame()->document()->documentElement(),
+ mainFrame()->document()->rootScroller());
+ EXPECT_TRUE(exceptionState.hadException());
+ }
+
+ {
+ // Set to an element with no size.
+ Element* element = mainFrame()->document()->getElementById("empty");
+ TrackExceptionState exceptionState;
+ mainFrame()->document()->setRootScroller(element, exceptionState);
+ ASSERT_EQ(
+ mainFrame()->document()->documentElement(),
+ mainFrame()->document()->rootScroller());
+ EXPECT_TRUE(exceptionState.hadException());
+ }
+}
+
+// Test that the root scroller resets to the default element when the current
+// root scroller element becomes invalid as a scroller.
+TEST_F(RootScrollerTest, TestRootScrollerBecomesInvalid)
+{
+ initialize("root-scroller.html");
+
+ ASSERT_EQ(
+ mainFrame()->document()->documentElement(),
+ rootScroller().get());
+
+ Element* container = mainFrame()->document()->getElementById("container");
+ TrackExceptionState exceptionState;
+ mainFrame()->document()->setRootScroller(container, exceptionState);
+
+ ASSERT_EQ(container, mainFrame()->document()->rootScroller());
+
+ executeScript(
+ "document.querySelector('#container').style.display = 'inline'");
+
+ ASSERT_EQ(
+ mainFrame()->document()->documentElement(),
+ mainFrame()->document()->rootScroller());
+}
+
+// Tests that setting the root scroller of the top codument to an element that
+// belongs to a nested document fails.
+TEST_F(RootScrollerTest, TestSetRootScrollerOnElementInIframe)
+{
+ initialize("root-scroller-iframe.html");
+
+ ASSERT_EQ(
+ mainFrame()->document()->documentElement(),
+ rootScroller().get());
+
+ {
+ // Trying to set an element from a nested document should fail.
+ HTMLFrameOwnerElement* iframe = toHTMLFrameOwnerElement(
+ mainFrame()->document()->getElementById("iframe"));
+ Element* innerContainer =
+ iframe->contentDocument()->getElementById("container");
+
+ TrackExceptionState exceptionState;
+ mainFrame()->document()->setRootScroller(
+ innerContainer,
+ exceptionState);
+ EXPECT_TRUE(exceptionState.hadException());
+
+ ASSERT_EQ(
+ mainFrame()->document()->documentElement(),
+ rootScroller().get());
+ }
+
+ {
+ // Setting the iframe itself, however, should work.
+ HTMLFrameOwnerElement* iframe = toHTMLFrameOwnerElement(
+ mainFrame()->document()->getElementById("iframe"));
+
+ TrackExceptionState exceptionState;
+ mainFrame()->document()->setRootScroller(iframe, exceptionState);
+ EXPECT_FALSE(exceptionState.hadException());
+
+ ASSERT_EQ(iframe, rootScroller().get());
+ }
+}
+
+// Tests that setting an otherwise valid element as the root scroller on a
+// document within an iframe fails and getting the root scroller in the nested
+// document returns the default element.
+TEST_F(RootScrollerTest, TestRootScrollerWithinIframe)
+{
+ initialize("root-scroller-iframe.html");
+
+ ASSERT_EQ(
+ mainFrame()->document()->documentElement(),
+ rootScroller().get());
+
+ {
+ // Trying to set an element within nested document should fail.
+ // rootScroller() should always return its documentElement.
+ HTMLFrameOwnerElement* iframe = toHTMLFrameOwnerElement(
+ mainFrame()->document()->getElementById("iframe"));
+
+ ASSERT_EQ(
+ iframe->contentDocument()->documentElement(),
+ iframe->contentDocument()->rootScroller());
+
+ Element* innerContainer =
+ iframe->contentDocument()->getElementById("container");
+ TrackExceptionState exceptionState;
+ iframe->contentDocument()->setRootScroller(
+ innerContainer,
+ exceptionState);
+ EXPECT_TRUE(exceptionState.hadException());
+
+ ASSERT_EQ(
+ iframe->contentDocument()->documentElement(),
+ iframe->contentDocument()->rootScroller());
+ }
+}
+
+// Tests that trying to set an element as the root scroller of a document inside
+// an iframe fails when that element belongs to the parent document.
+TEST_F(RootScrollerTest, TestSetRootScrollerOnElementFromOutsideIframe)
+{
+ initialize("root-scroller-iframe.html");
+
+ ASSERT_EQ(
+ mainFrame()->document()->documentElement(),
+ rootScroller().get());
+ {
+ // Try to set the the root scroller of the child document to be the
+ // <iframe> element in the parent document.
+ HTMLFrameOwnerElement* iframe = toHTMLFrameOwnerElement(
+ mainFrame()->document()->getElementById("iframe"));
+ NonThrowableExceptionState nonThrow;
+ Element* body =
+ mainFrame()->document()->querySelector("body", nonThrow);
+
+ ASSERT_EQ(
+ iframe->contentDocument()->documentElement(),
+ iframe->contentDocument()->rootScroller());
+
+ TrackExceptionState exceptionState;
+ iframe->contentDocument()->setRootScroller(
+ iframe,
+ exceptionState);
+ EXPECT_TRUE(exceptionState.hadException());
+
+ ASSERT_EQ(
+ iframe->contentDocument()->documentElement(),
+ iframe->contentDocument()->rootScroller());
+
+ exceptionState.clearException();
+
+ // Try to set the root scroller of the child document to be the
+ // <body> element of the parent document.
+ iframe->contentDocument()->setRootScroller(
+ body,
+ exceptionState);
+ EXPECT_TRUE(exceptionState.hadException());
+
+ ASSERT_EQ(
+ iframe->contentDocument()->documentElement(),
+ iframe->contentDocument()->rootScroller());
+ }
+}
+
+} // namespace
+
+} // namespace blink

Powered by Google App Engine
This is Rietveld 408576698