| // Copyright 2014 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/frame/VisualViewport.h" |
| |
| #include "core/dom/Document.h" |
| #include "core/frame/FrameHost.h" |
| #include "core/frame/FrameView.h" |
| #include "core/frame/LocalFrame.h" |
| #include "core/html/HTMLBodyElement.h" |
| #include "core/html/HTMLElement.h" |
| #include "core/input/EventHandler.h" |
| #include "core/layout/LayoutObject.h" |
| #include "core/layout/LayoutView.h" |
| #include "core/layout/compositing/PaintLayerCompositor.h" |
| #include "core/page/Page.h" |
| #include "platform/PlatformGestureEvent.h" |
| #include "platform/geometry/DoublePoint.h" |
| #include "platform/geometry/DoubleRect.h" |
| #include "platform/testing/URLTestHelpers.h" |
| #include "public/platform/Platform.h" |
| #include "public/platform/WebLayerTreeView.h" |
| #include "public/platform/WebUnitTestSupport.h" |
| #include "public/web/WebContextMenuData.h" |
| #include "public/web/WebDocument.h" |
| #include "public/web/WebFrameClient.h" |
| #include "public/web/WebInputEvent.h" |
| #include "public/web/WebScriptSource.h" |
| #include "public/web/WebSettings.h" |
| #include "public/web/WebViewClient.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "web/WebLocalFrameImpl.h" |
| #include "web/tests/FrameTestHelpers.h" |
| |
| #include <string> |
| |
| #define ASSERT_POINT_EQ(expected, actual) \ |
| do { \ |
| ASSERT_EQ((expected).x(), (actual).x()); \ |
| ASSERT_EQ((expected).y(), (actual).y()); \ |
| } while (false) |
| |
| #define EXPECT_POINT_EQ(expected, actual) \ |
| do { \ |
| EXPECT_EQ((expected).x(), (actual).x()); \ |
| EXPECT_EQ((expected).y(), (actual).y()); \ |
| } while (false) |
| |
| #define EXPECT_FLOAT_POINT_EQ(expected, actual) \ |
| do { \ |
| EXPECT_FLOAT_EQ((expected).x(), (actual).x()); \ |
| EXPECT_FLOAT_EQ((expected).y(), (actual).y()); \ |
| } while (false) |
| |
| #define EXPECT_POINT_EQ(expected, actual) \ |
| do { \ |
| EXPECT_EQ((expected).x(), (actual).x()); \ |
| EXPECT_EQ((expected).y(), (actual).y()); \ |
| } while (false) |
| |
| #define EXPECT_SIZE_EQ(expected, actual) \ |
| do { \ |
| EXPECT_EQ((expected).width(), (actual).width()); \ |
| EXPECT_EQ((expected).height(), (actual).height()); \ |
| } while (false) |
| |
| #define EXPECT_FLOAT_SIZE_EQ(expected, actual) \ |
| do { \ |
| EXPECT_FLOAT_EQ((expected).width(), (actual).width()); \ |
| EXPECT_FLOAT_EQ((expected).height(), (actual).height()); \ |
| } while (false) |
| |
| #define EXPECT_FLOAT_RECT_EQ(expected, actual) \ |
| do { \ |
| EXPECT_FLOAT_EQ((expected).x(), (actual).x()); \ |
| EXPECT_FLOAT_EQ((expected).y(), (actual).y()); \ |
| EXPECT_FLOAT_EQ((expected).width(), (actual).width()); \ |
| EXPECT_FLOAT_EQ((expected).height(), (actual).height()); \ |
| } while (false) |
| |
| |
| using namespace blink; |
| |
| using ::testing::_; |
| using ::testing::PrintToString; |
| using ::testing::Mock; |
| |
| namespace blink { |
| ::std::ostream& operator<<(::std::ostream& os, const WebContextMenuData& data) |
| { |
| return os << "Context menu location: [" |
| << data.mousePosition.x << ", " << data.mousePosition.y << "]"; |
| } |
| } |
| |
| |
| namespace { |
| |
| class VisualViewportTest |
| : public testing::Test |
| , public FrameTestHelpers::SettingOverrider { |
| public: |
| VisualViewportTest() |
| : m_baseURL("http://www.test.com/") |
| , m_helper(this) |
| { |
| } |
| |
| void overrideSettings(WebSettings *settings) override |
| { |
| } |
| |
| void initializeWithDesktopSettings(void (*overrideSettingsFunc)(WebSettings*) = 0) |
| { |
| if (!overrideSettingsFunc) |
| overrideSettingsFunc = &configureSettings; |
| m_helper.initialize(true, 0, &m_mockWebViewClient, overrideSettingsFunc); |
| webViewImpl()->setDefaultPageScaleLimits(1, 4); |
| } |
| |
| void initializeWithAndroidSettings(void (*overrideSettingsFunc)(WebSettings*) = 0) |
| { |
| if (!overrideSettingsFunc) |
| overrideSettingsFunc = &configureAndroidSettings; |
| m_helper.initialize(true, 0, &m_mockWebViewClient, overrideSettingsFunc); |
| webViewImpl()->setDefaultPageScaleLimits(0.25f, 5); |
| } |
| |
| ~VisualViewportTest() override |
| { |
| Platform::current()->unitTestSupport()->unregisterAllMockedURLs(); |
| } |
| |
| void navigateTo(const std::string& url) |
| { |
| FrameTestHelpers::loadFrame(webViewImpl()->mainFrame(), url); |
| } |
| |
| void forceFullCompositingUpdate() |
| { |
| webViewImpl()->updateAllLifecyclePhases(); |
| } |
| |
| void registerMockedHttpURLLoad(const std::string& fileName) |
| { |
| URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8(fileName.c_str())); |
| } |
| |
| WebLayer* getRootScrollLayer() |
| { |
| PaintLayerCompositor* compositor = frame()->contentLayoutObject()->compositor(); |
| ASSERT(compositor); |
| ASSERT(compositor->scrollLayer()); |
| |
| WebLayer* webScrollLayer = compositor->scrollLayer()->platformLayer(); |
| return webScrollLayer; |
| } |
| |
| WebViewImpl* webViewImpl() const { return m_helper.webViewImpl(); } |
| LocalFrame* frame() const { return m_helper.webViewImpl()->mainFrameImpl()->frame(); } |
| |
| static void configureSettings(WebSettings* settings) |
| { |
| settings->setJavaScriptEnabled(true); |
| settings->setAcceleratedCompositingEnabled(true); |
| settings->setPreferCompositingToLCDTextEnabled(true); |
| } |
| |
| static void configureAndroidSettings(WebSettings* settings) |
| { |
| configureSettings(settings); |
| settings->setViewportEnabled(true); |
| settings->setViewportMetaEnabled(true); |
| settings->setShrinksViewportContentToFit(true); |
| settings->setMainFrameResizesAreOrientationChanges(true); |
| } |
| |
| protected: |
| std::string m_baseURL; |
| FrameTestHelpers::TestWebViewClient m_mockWebViewClient; |
| |
| private: |
| FrameTestHelpers::WebViewHelper m_helper; |
| }; |
| |
| typedef void (*SettingOverrideFunction)(WebSettings*); |
| |
| static void DefaultSettingOverride(WebSettings *) |
| { |
| } |
| |
| class ParameterizedVisualViewportTest |
| : public VisualViewportTest |
| , public testing::WithParamInterface<SettingOverrideFunction> { |
| public: |
| void overrideSettings(WebSettings *settings) override |
| { |
| GetParam()(settings); |
| } |
| }; |
| |
| static void RootLayerScrollsSettingOverride(WebSettings *settings) |
| { |
| settings->setRootLayerScrolls(true); |
| } |
| INSTANTIATE_TEST_CASE_P(All, ParameterizedVisualViewportTest, ::testing::Values( |
| DefaultSettingOverride, |
| RootLayerScrollsSettingOverride)); |
| |
| |
| // Disable these tests on Mac OSX ASAN due to all these tests failing because of |
| // a user-after-free. crbug.com/582755. |
| #if OS(MACOSX) && defined(ADDRESS_SANITIZER) |
| #define MAYBE_TEST_P(test) TEST_P(ParameterizedVisualViewportTest, DISABLED_##test) |
| #define MAYBE_TEST_F(test) TEST_F(VisualViewportTest, DISABLED_##test) |
| #else |
| #define MAYBE_TEST_P(test) TEST_P(ParameterizedVisualViewportTest, test) |
| #define MAYBE_TEST_F(test) TEST_F(VisualViewportTest, test) |
| #endif |
| |
| // Test that resizing the VisualViewport works as expected and that resizing the |
| // WebView resizes the VisualViewport. |
| MAYBE_TEST_P(TestResize) |
| { |
| initializeWithDesktopSettings(); |
| webViewImpl()->resize(IntSize(320, 240)); |
| |
| navigateTo("about:blank"); |
| forceFullCompositingUpdate(); |
| |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| |
| IntSize webViewSize = webViewImpl()->size(); |
| |
| // Make sure the visual viewport was initialized. |
| EXPECT_SIZE_EQ(webViewSize, visualViewport.size()); |
| |
| // Resizing the WebView should change the VisualViewport. |
| webViewSize = IntSize(640, 480); |
| webViewImpl()->resize(webViewSize); |
| EXPECT_SIZE_EQ(webViewSize, IntSize(webViewImpl()->size())); |
| EXPECT_SIZE_EQ(webViewSize, visualViewport.size()); |
| |
| // Resizing the visual viewport shouldn't affect the WebView. |
| IntSize newViewportSize = IntSize(320, 200); |
| visualViewport.setSize(newViewportSize); |
| EXPECT_SIZE_EQ(webViewSize, IntSize(webViewImpl()->size())); |
| EXPECT_SIZE_EQ(newViewportSize, visualViewport.size()); |
| } |
| |
| // This tests that shrinking the WebView while the page is fully scrolled |
| // doesn't move the viewport up/left, it should keep the visible viewport |
| // unchanged from the user's perspective (shrinking the FrameView will clamp |
| // the VisualViewport so we need to counter scroll the FrameView to make it |
| // appear to stay still). This caused bugs like crbug.com/453859. |
| MAYBE_TEST_P(TestResizeAtFullyScrolledPreservesViewportLocation) |
| { |
| initializeWithDesktopSettings(); |
| webViewImpl()->resize(IntSize(800, 600)); |
| |
| registerMockedHttpURLLoad("content-width-1000.html"); |
| navigateTo(m_baseURL + "content-width-1000.html"); |
| |
| FrameView& frameView = *webViewImpl()->mainFrameImpl()->frameView(); |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| |
| visualViewport.setScale(2); |
| |
| // Fully scroll both viewports. |
| frameView.layoutViewportScrollableArea()->setScrollPosition(DoublePoint(10000, 10000), ProgrammaticScroll); |
| visualViewport.move(FloatSize(10000, 10000)); |
| |
| // Sanity check. |
| ASSERT_POINT_EQ(FloatPoint(400, 300), visualViewport.location()); |
| ASSERT_POINT_EQ(DoublePoint(200, 1400), frameView.layoutViewportScrollableArea()->scrollPositionDouble()); |
| |
| DoublePoint expectedLocation = frameView.scrollableArea()->visibleContentRectDouble().location(); |
| |
| // Shrink the WebView, this should cause both viewports to shrink and |
| // WebView should do whatever it needs to do to preserve the visible |
| // location. |
| webViewImpl()->resize(IntSize(700, 550)); |
| |
| EXPECT_POINT_EQ(expectedLocation, frameView.scrollableArea()->visibleContentRectDouble().location()); |
| |
| webViewImpl()->resize(IntSize(800, 600)); |
| |
| EXPECT_POINT_EQ(expectedLocation, frameView.scrollableArea()->visibleContentRectDouble().location()); |
| } |
| |
| |
| // Test that the VisualViewport works as expected in case of a scaled |
| // and scrolled viewport - scroll down. |
| MAYBE_TEST_P(TestResizeAfterVerticalScroll) |
| { |
| /* |
| 200 200 |
| | | | | |
| | | | | |
| | | 800 | | 800 |
| |-------------------| | | |
| | | | | |
| | | | | |
| | | | | |
| | | --------> | | |
| | 300 | | | |
| | | | | |
| | 400 | | | |
| | | |-------------------| |
| | | | 75 | |
| | 50 | | 50 100| |
| o----- | o---- | |
| | | | | | 25 | |
| | |100 | |-------------------| |
| | | | | | |
| | | | | | |
| -------------------- -------------------- |
| |
| */ |
| |
| // Disable the test on Mac OSX until futher investigation. |
| // Local build on Mac is OK but thes bot fails. |
| #if OS(MACOSX) |
| return; |
| #endif |
| |
| initializeWithAndroidSettings(); |
| |
| registerMockedHttpURLLoad("200-by-800-viewport.html"); |
| navigateTo(m_baseURL + "200-by-800-viewport.html"); |
| |
| webViewImpl()->resize(IntSize(100, 200)); |
| |
| // Scroll main frame to the bottom of the document |
| webViewImpl()->mainFrame()->setScrollOffset(WebSize(0, 400)); |
| EXPECT_POINT_EQ(IntPoint(0, 400), frame()->view()->layoutViewportScrollableArea()->scrollPosition()); |
| |
| webViewImpl()->setPageScaleFactor(2.0); |
| |
| // Scroll visual viewport to the bottom of the main frame |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| visualViewport.setLocation(FloatPoint(0, 300)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 300), visualViewport.location()); |
| |
| // Verify the initial size of the visual viewport in the CSS pixels |
| EXPECT_FLOAT_SIZE_EQ(FloatSize(50, 100), visualViewport.visibleRect().size()); |
| |
| // Perform the resizing |
| webViewImpl()->resize(IntSize(200, 100)); |
| |
| // After resizing the scale changes 2.0 -> 4.0 |
| EXPECT_FLOAT_SIZE_EQ(FloatSize(50, 25), visualViewport.visibleRect().size()); |
| |
| EXPECT_POINT_EQ(IntPoint(0, 625), frame()->view()->layoutViewportScrollableArea()->scrollPosition()); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 75), visualViewport.location()); |
| } |
| |
| // Test that the VisualViewport works as expected in case if a scaled |
| // and scrolled viewport - scroll right. |
| MAYBE_TEST_P(TestResizeAfterHorizontalScroll) |
| { |
| /* |
| 200 200 |
| ---------------o----- ---------------o----- |
| | | | | 25| | |
| | | | | -----| |
| | 100| | |100 50 | |
| | | | | | |
| | ---- | |-------------------| |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| |400 | ---------> | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| |-------------------| | | |
| | | | | |
| |
| */ |
| |
| // Disable the test on Mac OSX until futher investigation. |
| // Local build on Mac is OK but thes bot fails. |
| #if OS(MACOSX) |
| return; |
| #endif |
| |
| initializeWithAndroidSettings(); |
| |
| registerMockedHttpURLLoad("200-by-800-viewport.html"); |
| navigateTo(m_baseURL + "200-by-800-viewport.html"); |
| |
| webViewImpl()->resize(IntSize(100, 200)); |
| |
| // Outer viewport takes the whole width of the document. |
| |
| webViewImpl()->setPageScaleFactor(2.0); |
| |
| // Scroll visual viewport to the right edge of the frame |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| visualViewport.setLocation(FloatPoint(150, 0)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(150, 0), visualViewport.location()); |
| |
| // Verify the initial size of the visual viewport in the CSS pixels |
| EXPECT_FLOAT_SIZE_EQ(FloatSize(50, 100), visualViewport.visibleRect().size()); |
| |
| webViewImpl()->resize(IntSize(200, 100)); |
| |
| // After resizing the scale changes 2.0 -> 4.0 |
| EXPECT_FLOAT_SIZE_EQ(FloatSize(50, 25), visualViewport.visibleRect().size()); |
| |
| EXPECT_POINT_EQ(IntPoint(0, 0), frame()->view()->scrollPosition()); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(150, 0), visualViewport.location()); |
| } |
| |
| static void disableAcceleratedCompositing(WebSettings* settings) |
| { |
| VisualViewportTest::configureSettings(settings); |
| // FIXME: This setting is being removed, so this test needs to be rewritten to |
| // do something else. crbug.com/173949 |
| settings->setAcceleratedCompositingEnabled(false); |
| } |
| |
| // Test that the container layer gets sized properly if the WebView is resized |
| // prior to the VisualViewport being attached to the layer tree. |
| MAYBE_TEST_P(TestWebViewResizedBeforeAttachment) |
| { |
| initializeWithDesktopSettings(disableAcceleratedCompositing); |
| webViewImpl()->resize(IntSize(320, 240)); |
| |
| navigateTo("about:blank"); |
| forceFullCompositingUpdate(); |
| webViewImpl()->settings()->setAcceleratedCompositingEnabled(true); |
| webViewImpl()->updateAllLifecyclePhases(); |
| |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| EXPECT_FLOAT_SIZE_EQ(FloatSize(320, 240), visualViewport.containerLayer()->size()); |
| } |
| |
| // Make sure that the visibleRect method acurately reflects the scale and scroll location |
| // of the viewport. |
| MAYBE_TEST_P(TestVisibleRect) |
| { |
| initializeWithDesktopSettings(); |
| webViewImpl()->resize(IntSize(320, 240)); |
| |
| navigateTo("about:blank"); |
| forceFullCompositingUpdate(); |
| |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| |
| // Initial visible rect should be the whole frame. |
| EXPECT_SIZE_EQ(IntSize(webViewImpl()->size()), visualViewport.size()); |
| |
| // Viewport is whole frame. |
| IntSize size = IntSize(400, 200); |
| webViewImpl()->resize(size); |
| webViewImpl()->updateAllLifecyclePhases(); |
| visualViewport.setSize(size); |
| |
| // Scale the viewport to 2X; size should not change. |
| FloatRect expectedRect(FloatPoint(0, 0), FloatSize(size)); |
| expectedRect.scale(0.5); |
| visualViewport.setScale(2); |
| EXPECT_EQ(2, visualViewport.scale()); |
| EXPECT_SIZE_EQ(size, visualViewport.size()); |
| EXPECT_FLOAT_RECT_EQ(expectedRect, visualViewport.visibleRect()); |
| |
| // Move the viewport. |
| expectedRect.setLocation(FloatPoint(5, 7)); |
| visualViewport.setLocation(expectedRect.location()); |
| EXPECT_FLOAT_RECT_EQ(expectedRect, visualViewport.visibleRect()); |
| |
| expectedRect.setLocation(FloatPoint(200, 100)); |
| visualViewport.setLocation(expectedRect.location()); |
| EXPECT_FLOAT_RECT_EQ(expectedRect, visualViewport.visibleRect()); |
| |
| // Scale the viewport to 3X to introduce some non-int values. |
| FloatPoint oldLocation = expectedRect.location(); |
| expectedRect = FloatRect(FloatPoint(), FloatSize(size)); |
| expectedRect.scale(1 / 3.0f); |
| expectedRect.setLocation(oldLocation); |
| visualViewport.setScale(3); |
| EXPECT_FLOAT_RECT_EQ(expectedRect, visualViewport.visibleRect()); |
| |
| expectedRect.setLocation(FloatPoint(0.25f, 0.333f)); |
| visualViewport.setLocation(expectedRect.location()); |
| EXPECT_FLOAT_RECT_EQ(expectedRect, visualViewport.visibleRect()); |
| } |
| |
| // Make sure that the visibleRectInDocument method acurately reflects the scale |
| // and scroll location of the viewport relative to the document. |
| MAYBE_TEST_P(TestVisibleRectInDocument) |
| { |
| initializeWithDesktopSettings(); |
| webViewImpl()->resize(IntSize(100, 400)); |
| |
| registerMockedHttpURLLoad("200-by-800-viewport.html"); |
| navigateTo(m_baseURL + "200-by-800-viewport.html"); |
| |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| |
| // Scale the viewport to 2X and move it. |
| visualViewport.setScale(2); |
| visualViewport.setLocation(FloatPoint(10, 15)); |
| EXPECT_FLOAT_RECT_EQ(FloatRect(10, 15, 50, 200), visualViewport.visibleRectInDocument()); |
| |
| // Scroll the layout viewport. Ensure its offset is reflected in visibleRectInDocument(). |
| FrameView& frameView = *webViewImpl()->mainFrameImpl()->frameView(); |
| frameView.layoutViewportScrollableArea()->setScrollPosition(DoublePoint(40, 100), ProgrammaticScroll); |
| EXPECT_FLOAT_RECT_EQ(FloatRect(50, 115, 50, 200), visualViewport.visibleRectInDocument()); |
| } |
| |
| MAYBE_TEST_P(TestFractionalScrollOffsetIsNotOverwritten) |
| { |
| initializeWithAndroidSettings(); |
| webViewImpl()->resize(IntSize(200, 250)); |
| |
| registerMockedHttpURLLoad("200-by-800-viewport.html"); |
| navigateTo(m_baseURL + "200-by-800-viewport.html"); |
| |
| FrameView& frameView = *webViewImpl()->mainFrameImpl()->frameView(); |
| frameView.layoutViewportScrollableArea()->setScrollPosition(DoublePoint(0, 10.5), ProgrammaticScroll); |
| webViewImpl()->applyViewportDeltas(WebFloatSize(), WebFloatSize(10, 20), WebFloatSize(), 1, 0); |
| |
| EXPECT_EQ(30.5, frameView.layoutViewportScrollableArea()->scrollPositionDouble().y()); |
| } |
| |
| // Test that the viewport's scroll offset is always appropriately bounded such that the |
| // visual viewport always stays within the bounds of the main frame. |
| MAYBE_TEST_P(TestOffsetClamping) |
| { |
| initializeWithDesktopSettings(); |
| webViewImpl()->resize(IntSize(320, 240)); |
| |
| navigateTo("about:blank"); |
| forceFullCompositingUpdate(); |
| |
| // Visual viewport should be initialized to same size as frame so no scrolling possible. |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), visualViewport.visibleRect().location()); |
| |
| visualViewport.setLocation(FloatPoint(-1, -2)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), visualViewport.visibleRect().location()); |
| |
| visualViewport.setLocation(FloatPoint(100, 200)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), visualViewport.visibleRect().location()); |
| |
| visualViewport.setLocation(FloatPoint(-5, 10)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), visualViewport.visibleRect().location()); |
| |
| // Scale by 2x. The viewport's visible rect should now have a size of 160x120. |
| visualViewport.setScale(2); |
| FloatPoint location(10, 50); |
| visualViewport.setLocation(location); |
| EXPECT_FLOAT_POINT_EQ(location, visualViewport.visibleRect().location()); |
| |
| visualViewport.setLocation(FloatPoint(1000, 2000)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(160, 120), visualViewport.visibleRect().location()); |
| |
| visualViewport.setLocation(FloatPoint(-1000, -2000)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), visualViewport.visibleRect().location()); |
| |
| // Make sure offset gets clamped on scale out. Scale to 1.25 so the viewport is 256x192. |
| visualViewport.setLocation(FloatPoint(160, 120)); |
| visualViewport.setScale(1.25); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(64, 48), visualViewport.visibleRect().location()); |
| |
| // Scale out smaller than 1. |
| visualViewport.setScale(0.25); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), visualViewport.visibleRect().location()); |
| } |
| |
| // Test that the viewport can be scrolled around only within the main frame in the presence |
| // of viewport resizes, as would be the case if the on screen keyboard came up. |
| MAYBE_TEST_P(TestOffsetClampingWithResize) |
| { |
| initializeWithDesktopSettings(); |
| webViewImpl()->resize(IntSize(320, 240)); |
| |
| navigateTo("about:blank"); |
| forceFullCompositingUpdate(); |
| |
| // Visual viewport should be initialized to same size as frame so no scrolling possible. |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), visualViewport.visibleRect().location()); |
| |
| // Shrink the viewport vertically. The resize shouldn't affect the location, but it |
| // should allow vertical scrolling. |
| visualViewport.setSize(IntSize(320, 200)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), visualViewport.visibleRect().location()); |
| visualViewport.setLocation(FloatPoint(10, 20)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 20), visualViewport.visibleRect().location()); |
| visualViewport.setLocation(FloatPoint(0, 100)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 40), visualViewport.visibleRect().location()); |
| visualViewport.setLocation(FloatPoint(0, 10)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 10), visualViewport.visibleRect().location()); |
| visualViewport.setLocation(FloatPoint(0, -100)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), visualViewport.visibleRect().location()); |
| |
| // Repeat the above but for horizontal dimension. |
| visualViewport.setSize(IntSize(280, 240)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), visualViewport.visibleRect().location()); |
| visualViewport.setLocation(FloatPoint(10, 20)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(10, 0), visualViewport.visibleRect().location()); |
| visualViewport.setLocation(FloatPoint(100, 0)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(40, 0), visualViewport.visibleRect().location()); |
| visualViewport.setLocation(FloatPoint(10, 0)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(10, 0), visualViewport.visibleRect().location()); |
| visualViewport.setLocation(FloatPoint(-100, 0)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), visualViewport.visibleRect().location()); |
| |
| // Now with both dimensions. |
| visualViewport.setSize(IntSize(280, 200)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), visualViewport.visibleRect().location()); |
| visualViewport.setLocation(FloatPoint(10, 20)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(10, 20), visualViewport.visibleRect().location()); |
| visualViewport.setLocation(FloatPoint(100, 100)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(40, 40), visualViewport.visibleRect().location()); |
| visualViewport.setLocation(FloatPoint(10, 3)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(10, 3), visualViewport.visibleRect().location()); |
| visualViewport.setLocation(FloatPoint(-10, -4)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), visualViewport.visibleRect().location()); |
| } |
| |
| // Test that the viewport is scrollable but bounded appropriately within the main frame |
| // when we apply both scaling and resizes. |
| MAYBE_TEST_P(TestOffsetClampingWithResizeAndScale) |
| { |
| initializeWithDesktopSettings(); |
| webViewImpl()->resize(IntSize(320, 240)); |
| |
| navigateTo("about:blank"); |
| forceFullCompositingUpdate(); |
| |
| // Visual viewport should be initialized to same size as WebView so no scrolling possible. |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), visualViewport.visibleRect().location()); |
| |
| // Zoom in to 2X so we can scroll the viewport to 160x120. |
| visualViewport.setScale(2); |
| visualViewport.setLocation(FloatPoint(200, 200)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(160, 120), visualViewport.visibleRect().location()); |
| |
| // Now resize the viewport to make it 10px smaller. Since we're zoomed in by 2X it should |
| // allow us to scroll by 5px more. |
| visualViewport.setSize(IntSize(310, 230)); |
| visualViewport.setLocation(FloatPoint(200, 200)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(165, 125), visualViewport.visibleRect().location()); |
| |
| // The viewport can be larger than the main frame (currently 320, 240) though typically |
| // the scale will be clamped to prevent it from actually being larger. |
| visualViewport.setSize(IntSize(330, 250)); |
| EXPECT_SIZE_EQ(IntSize(330, 250), visualViewport.size()); |
| |
| // Resize both the viewport and the frame to be larger. |
| webViewImpl()->resize(IntSize(640, 480)); |
| webViewImpl()->updateAllLifecyclePhases(); |
| EXPECT_SIZE_EQ(IntSize(webViewImpl()->size()), visualViewport.size()); |
| EXPECT_SIZE_EQ(IntSize(webViewImpl()->size()), frame()->view()->frameRect().size()); |
| visualViewport.setLocation(FloatPoint(1000, 1000)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(320, 240), visualViewport.visibleRect().location()); |
| |
| // Make sure resizing the viewport doesn't change its offset if the resize doesn't make |
| // the viewport go out of bounds. |
| visualViewport.setLocation(FloatPoint(200, 200)); |
| visualViewport.setSize(IntSize(880, 560)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(200, 200), visualViewport.visibleRect().location()); |
| } |
| |
| // The main FrameView's size should be set such that its the size of the visual viewport |
| // at minimum scale. If there's no explicit minimum scale set, the FrameView should be |
| // set to the content width and height derived by the aspect ratio. |
| MAYBE_TEST_P(TestFrameViewSizedToContent) |
| { |
| initializeWithAndroidSettings(); |
| webViewImpl()->resize(IntSize(320, 240)); |
| |
| registerMockedHttpURLLoad("200-by-300-viewport.html"); |
| navigateTo(m_baseURL + "200-by-300-viewport.html"); |
| |
| webViewImpl()->resize(IntSize(600, 800)); |
| webViewImpl()->updateAllLifecyclePhases(); |
| |
| // Note: the size is ceiled and should match the behavior in CC's LayerImpl::bounds(). |
| EXPECT_SIZE_EQ(IntSize(200, 267), |
| webViewImpl()->mainFrameImpl()->frameView()->frameRect().size()); |
| } |
| |
| // The main FrameView's size should be set such that its the size of the visual viewport |
| // at minimum scale. On Desktop, the minimum scale is set at 1 so make sure the FrameView |
| // is sized to the viewport. |
| MAYBE_TEST_P(TestFrameViewSizedToMinimumScale) |
| { |
| initializeWithDesktopSettings(); |
| webViewImpl()->resize(IntSize(320, 240)); |
| |
| registerMockedHttpURLLoad("200-by-300.html"); |
| navigateTo(m_baseURL + "200-by-300.html"); |
| |
| webViewImpl()->resize(IntSize(100, 160)); |
| webViewImpl()->updateAllLifecyclePhases(); |
| |
| EXPECT_SIZE_EQ(IntSize(100, 160), |
| webViewImpl()->mainFrameImpl()->frameView()->frameRect().size()); |
| } |
| |
| // Test that attaching a new frame view resets the size of the inner viewport scroll |
| // layer. crbug.com/423189. |
| MAYBE_TEST_P(TestAttachingNewFrameSetsInnerScrollLayerSize) |
| { |
| initializeWithAndroidSettings(); |
| webViewImpl()->resize(IntSize(320, 240)); |
| |
| // Load a wider page first, the navigation should resize the scroll layer to |
| // the smaller size on the second navigation. |
| registerMockedHttpURLLoad("content-width-1000.html"); |
| navigateTo(m_baseURL + "content-width-1000.html"); |
| webViewImpl()->updateAllLifecyclePhases(); |
| |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| visualViewport.setScale(2); |
| visualViewport.move(FloatPoint(50, 60)); |
| |
| // Move and scale the viewport to make sure it gets reset in the navigation. |
| EXPECT_POINT_EQ(FloatPoint(50, 60), visualViewport.location()); |
| EXPECT_EQ(2, visualViewport.scale()); |
| |
| // Navigate again, this time the FrameView should be smaller. |
| registerMockedHttpURLLoad("viewport-device-width.html"); |
| navigateTo(m_baseURL + "viewport-device-width.html"); |
| |
| // Ensure the scroll layer matches the frame view's size. |
| EXPECT_SIZE_EQ(FloatSize(320, 240), visualViewport.scrollLayer()->size()); |
| |
| // Ensure the location and scale were reset. |
| EXPECT_POINT_EQ(FloatPoint(), visualViewport.location()); |
| EXPECT_EQ(1, visualViewport.scale()); |
| } |
| |
| // The main FrameView's size should be set such that its the size of the visual viewport |
| // at minimum scale. Test that the FrameView is appropriately sized in the presence |
| // of a viewport <meta> tag. |
| MAYBE_TEST_P(TestFrameViewSizedToViewportMetaMinimumScale) |
| { |
| initializeWithAndroidSettings(); |
| webViewImpl()->resize(IntSize(320, 240)); |
| |
| registerMockedHttpURLLoad("200-by-300-min-scale-2.html"); |
| navigateTo(m_baseURL + "200-by-300-min-scale-2.html"); |
| |
| webViewImpl()->resize(IntSize(100, 160)); |
| webViewImpl()->updateAllLifecyclePhases(); |
| |
| EXPECT_SIZE_EQ(IntSize(50, 80), |
| webViewImpl()->mainFrameImpl()->frameView()->frameRect().size()); |
| } |
| |
| // Test that the visual viewport still gets sized in AutoSize/AutoResize mode. |
| MAYBE_TEST_P(TestVisualViewportGetsSizeInAutoSizeMode) |
| { |
| initializeWithDesktopSettings(); |
| |
| EXPECT_SIZE_EQ(IntSize(0, 0), IntSize(webViewImpl()->size())); |
| EXPECT_SIZE_EQ(IntSize(0, 0), frame()->page()->frameHost().visualViewport().size()); |
| |
| webViewImpl()->enableAutoResizeMode(WebSize(10, 10), WebSize(1000, 1000)); |
| |
| registerMockedHttpURLLoad("200-by-300.html"); |
| navigateTo(m_baseURL + "200-by-300.html"); |
| |
| EXPECT_SIZE_EQ(IntSize(200, 300), frame()->page()->frameHost().visualViewport().size()); |
| } |
| |
| // Test that the text selection handle's position accounts for the visual viewport. |
| MAYBE_TEST_P(TestTextSelectionHandles) |
| { |
| initializeWithDesktopSettings(); |
| webViewImpl()->resize(IntSize(500, 800)); |
| |
| registerMockedHttpURLLoad("pinch-viewport-input-field.html"); |
| navigateTo(m_baseURL + "pinch-viewport-input-field.html"); |
| |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| webViewImpl()->setInitialFocus(false); |
| |
| WebRect originalAnchor; |
| WebRect originalFocus; |
| webViewImpl()->selectionBounds(originalAnchor, originalFocus); |
| |
| webViewImpl()->setPageScaleFactor(2); |
| visualViewport.setLocation(FloatPoint(100, 400)); |
| |
| WebRect anchor; |
| WebRect focus; |
| webViewImpl()->selectionBounds(anchor, focus); |
| |
| IntPoint expected(IntRect(originalAnchor).location()); |
| expected.moveBy(-flooredIntPoint(visualViewport.visibleRect().location())); |
| expected.scale(visualViewport.scale(), visualViewport.scale()); |
| |
| EXPECT_POINT_EQ(expected, IntRect(anchor).location()); |
| EXPECT_POINT_EQ(expected, IntRect(focus).location()); |
| |
| // FIXME(bokan) - http://crbug.com/364154 - Figure out how to test text selection |
| // as well rather than just carret. |
| } |
| |
| // Test that the HistoryItem for the page stores the visual viewport's offset and scale. |
| MAYBE_TEST_P(TestSavedToHistoryItem) |
| { |
| initializeWithDesktopSettings(); |
| webViewImpl()->resize(IntSize(200, 300)); |
| webViewImpl()->updateAllLifecyclePhases(); |
| |
| registerMockedHttpURLLoad("200-by-300.html"); |
| navigateTo(m_baseURL + "200-by-300.html"); |
| |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), |
| toLocalFrame(webViewImpl()->page()->mainFrame())->loader().currentItem()->visualViewportScrollPoint()); |
| |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| visualViewport.setScale(2); |
| |
| EXPECT_EQ(2, toLocalFrame(webViewImpl()->page()->mainFrame())->loader().currentItem()->pageScaleFactor()); |
| |
| visualViewport.setLocation(FloatPoint(10, 20)); |
| |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(10, 20), |
| toLocalFrame(webViewImpl()->page()->mainFrame())->loader().currentItem()->visualViewportScrollPoint()); |
| } |
| |
| // Test restoring a HistoryItem properly restores the visual viewport's state. |
| MAYBE_TEST_P(TestRestoredFromHistoryItem) |
| { |
| initializeWithDesktopSettings(); |
| webViewImpl()->resize(IntSize(200, 300)); |
| |
| registerMockedHttpURLLoad("200-by-300.html"); |
| |
| WebHistoryItem item; |
| item.initialize(); |
| WebURL destinationURL(URLTestHelpers::toKURL(m_baseURL + "200-by-300.html")); |
| item.setURLString(destinationURL.string()); |
| item.setVisualViewportScrollOffset(WebFloatPoint(100, 120)); |
| item.setPageScaleFactor(2); |
| |
| FrameTestHelpers::loadHistoryItem(webViewImpl()->mainFrame(), item, WebHistoryDifferentDocumentLoad, WebURLRequest::UseProtocolCachePolicy); |
| |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| EXPECT_EQ(2, visualViewport.scale()); |
| |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(100, 120), visualViewport.visibleRect().location()); |
| } |
| |
| // Test restoring a HistoryItem without the visual viewport offset falls back to distributing |
| // the scroll offset between the main frame and the visual viewport. |
| MAYBE_TEST_F(TestRestoredFromLegacyHistoryItem) |
| { |
| initializeWithDesktopSettings(); |
| webViewImpl()->resize(IntSize(100, 150)); |
| |
| registerMockedHttpURLLoad("200-by-300-viewport.html"); |
| |
| WebHistoryItem item; |
| item.initialize(); |
| WebURL destinationURL(URLTestHelpers::toKURL(m_baseURL + "200-by-300-viewport.html")); |
| item.setURLString(destinationURL.string()); |
| // (-1, -1) will be used if the HistoryItem is an older version prior to having |
| // visual viewport scroll offset. |
| item.setVisualViewportScrollOffset(WebFloatPoint(-1, -1)); |
| item.setScrollOffset(WebPoint(120, 180)); |
| item.setPageScaleFactor(2); |
| |
| FrameTestHelpers::loadHistoryItem(webViewImpl()->mainFrame(), item, WebHistoryDifferentDocumentLoad, WebURLRequest::UseProtocolCachePolicy); |
| |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| EXPECT_EQ(2, visualViewport.scale()); |
| EXPECT_POINT_EQ(IntPoint(100, 150), frame()->view()->scrollPosition()); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(20, 30), visualViewport.visibleRect().location()); |
| } |
| |
| // Test that navigation to a new page with a different sized main frame doesn't |
| // clobber the history item's main frame scroll offset. crbug.com/371867 |
| MAYBE_TEST_P(TestNavigateToSmallerFrameViewHistoryItemClobberBug) |
| { |
| initializeWithAndroidSettings(); |
| webViewImpl()->resize(IntSize(400, 400)); |
| webViewImpl()->updateAllLifecyclePhases(); |
| |
| registerMockedHttpURLLoad("content-width-1000.html"); |
| navigateTo(m_baseURL + "content-width-1000.html"); |
| |
| FrameView* frameView = webViewImpl()->mainFrameImpl()->frameView(); |
| frameView->layoutViewportScrollableArea()->setScrollPosition(IntPoint(0, 1000), ProgrammaticScroll); |
| |
| EXPECT_SIZE_EQ(IntSize(1000, 1000), frameView->frameRect().size()); |
| |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| visualViewport.setScale(2); |
| visualViewport.setLocation(FloatPoint(350, 350)); |
| |
| RefPtrWillBePersistent<HistoryItem> firstItem = webViewImpl()->mainFrameImpl()->frame()->loader().currentItem(); |
| EXPECT_POINT_EQ(IntPoint(0, 1000), firstItem->scrollPoint()); |
| |
| // Now navigate to a page which causes a smaller frameView. Make sure that |
| // navigating doesn't cause the history item to set a new scroll offset |
| // before the item was replaced. |
| navigateTo("about:blank"); |
| frameView = webViewImpl()->mainFrameImpl()->frameView(); |
| |
| EXPECT_NE(firstItem, webViewImpl()->mainFrameImpl()->frame()->loader().currentItem()); |
| EXPECT_LT(frameView->frameRect().size().width(), 1000); |
| EXPECT_POINT_EQ(IntPoint(0, 1000), firstItem->scrollPoint()); |
| } |
| |
| // Test that the coordinates sent into moveRangeSelection are offset by the |
| // visual viewport's location. |
| MAYBE_TEST_P(DISABLED_TestWebFrameRangeAccountsForVisualViewportScroll) |
| { |
| initializeWithDesktopSettings(); |
| webViewImpl()->settings()->setDefaultFontSize(12); |
| webViewImpl()->resize(WebSize(640, 480)); |
| registerMockedHttpURLLoad("move_range.html"); |
| navigateTo(m_baseURL + "move_range.html"); |
| |
| WebRect baseRect; |
| WebRect extentRect; |
| |
| webViewImpl()->setPageScaleFactor(2); |
| WebFrame* mainFrame = webViewImpl()->mainFrame(); |
| |
| // Select some text and get the base and extent rects (that's the start of |
| // the range and its end). Do a sanity check that the expected text is |
| // selected |
| mainFrame->executeScript(WebScriptSource("selectRange();")); |
| EXPECT_EQ("ir", mainFrame->selectionAsText().utf8()); |
| |
| webViewImpl()->selectionBounds(baseRect, extentRect); |
| WebPoint initialPoint(baseRect.x, baseRect.y); |
| WebPoint endPoint(extentRect.x, extentRect.y); |
| |
| // Move the visual viewport over and make the selection in the same |
| // screen-space location. The selection should change to two characters to |
| // the right and down one line. |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| visualViewport.move(FloatPoint(60, 25)); |
| mainFrame->moveRangeSelection(initialPoint, endPoint); |
| EXPECT_EQ("t ", mainFrame->selectionAsText().utf8()); |
| } |
| |
| // Test that the scrollFocusedNodeIntoRect method works with the visual viewport. |
| MAYBE_TEST_P(DISABLED_TestScrollFocusedNodeIntoRect) |
| { |
| initializeWithDesktopSettings(); |
| webViewImpl()->resize(IntSize(500, 300)); |
| |
| registerMockedHttpURLLoad("pinch-viewport-input-field.html"); |
| navigateTo(m_baseURL + "pinch-viewport-input-field.html"); |
| |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| webViewImpl()->resizeVisualViewport(IntSize(200, 100)); |
| webViewImpl()->setInitialFocus(false); |
| visualViewport.setLocation(FloatPoint()); |
| webViewImpl()->scrollFocusedNodeIntoRect(IntRect(0, 0, 500, 200)); |
| |
| EXPECT_POINT_EQ(IntPoint(0, frame()->view()->maximumScrollPosition().y()), |
| frame()->view()->scrollPosition()); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(150, 200), visualViewport.visibleRect().location()); |
| |
| // Try it again but with the page zoomed in |
| frame()->view()->setScrollPosition(IntPoint(0, 0), ProgrammaticScroll); |
| webViewImpl()->resizeVisualViewport(IntSize(500, 300)); |
| visualViewport.setLocation(FloatPoint(0, 0)); |
| |
| webViewImpl()->setPageScaleFactor(2); |
| webViewImpl()->scrollFocusedNodeIntoRect(IntRect(0, 0, 500, 200)); |
| EXPECT_POINT_EQ(IntPoint(0, frame()->view()->maximumScrollPosition().y()), |
| frame()->view()->scrollPosition()); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(125, 150), visualViewport.visibleRect().location()); |
| |
| // Once more but make sure that we don't move the visual viewport unless necessary. |
| registerMockedHttpURLLoad("pinch-viewport-input-field-long-and-wide.html"); |
| navigateTo(m_baseURL + "pinch-viewport-input-field-long-and-wide.html"); |
| webViewImpl()->setInitialFocus(false); |
| visualViewport.setLocation(FloatPoint()); |
| frame()->view()->setScrollPosition(IntPoint(0, 0), ProgrammaticScroll); |
| webViewImpl()->resizeVisualViewport(IntSize(500, 300)); |
| visualViewport.setLocation(FloatPoint(30, 50)); |
| |
| webViewImpl()->setPageScaleFactor(2); |
| webViewImpl()->scrollFocusedNodeIntoRect(IntRect(0, 0, 500, 200)); |
| EXPECT_POINT_EQ(IntPoint(200-30-75, 600-50-65), frame()->view()->scrollPosition()); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(30, 50), visualViewport.visibleRect().location()); |
| } |
| |
| // Test that resizing the WebView causes ViewportConstrained objects to relayout. |
| MAYBE_TEST_P(TestWebViewResizeCausesViewportConstrainedLayout) |
| { |
| initializeWithDesktopSettings(); |
| webViewImpl()->resize(IntSize(500, 300)); |
| |
| registerMockedHttpURLLoad("pinch-viewport-fixed-pos.html"); |
| navigateTo(m_baseURL + "pinch-viewport-fixed-pos.html"); |
| |
| LayoutObject* navbar = frame()->document()->getElementById("navbar")->layoutObject(); |
| |
| EXPECT_FALSE(navbar->needsLayout()); |
| |
| frame()->view()->resize(IntSize(500, 200)); |
| |
| EXPECT_TRUE(navbar->needsLayout()); |
| } |
| |
| class MockWebFrameClient : public WebFrameClient { |
| public: |
| MOCK_METHOD1(showContextMenu, void(const WebContextMenuData&)); |
| MOCK_METHOD1(didChangeScrollOffset, void(WebLocalFrame*)); |
| }; |
| |
| MATCHER_P2(ContextMenuAtLocation, x, y, |
| std::string(negation ? "is" : "isn't") |
| + " at expected location [" |
| + PrintToString(x) + ", " + PrintToString(y) + "]") |
| { |
| return arg.mousePosition.x == x && arg.mousePosition.y == y; |
| } |
| |
| // Test that the context menu's location is correct in the presence of visual |
| // viewport offset. |
| MAYBE_TEST_P(TestContextMenuShownInCorrectLocation) |
| { |
| initializeWithDesktopSettings(); |
| webViewImpl()->resize(IntSize(200, 300)); |
| |
| registerMockedHttpURLLoad("200-by-300.html"); |
| navigateTo(m_baseURL + "200-by-300.html"); |
| |
| WebMouseEvent mouseDownEvent; |
| mouseDownEvent.type = WebInputEvent::MouseDown; |
| mouseDownEvent.x = 10; |
| mouseDownEvent.y = 10; |
| mouseDownEvent.windowX = 10; |
| mouseDownEvent.windowY = 10; |
| mouseDownEvent.globalX = 110; |
| mouseDownEvent.globalY = 210; |
| mouseDownEvent.clickCount = 1; |
| mouseDownEvent.button = WebMouseEvent::ButtonRight; |
| |
| // Corresponding release event (Windows shows context menu on release). |
| WebMouseEvent mouseUpEvent(mouseDownEvent); |
| mouseUpEvent.type = WebInputEvent::MouseUp; |
| |
| WebFrameClient* oldClient = webViewImpl()->mainFrameImpl()->client(); |
| MockWebFrameClient mockWebFrameClient; |
| EXPECT_CALL(mockWebFrameClient, showContextMenu(ContextMenuAtLocation(mouseDownEvent.x, mouseDownEvent.y))); |
| |
| // Do a sanity check with no scale applied. |
| webViewImpl()->mainFrameImpl()->setClient(&mockWebFrameClient); |
| webViewImpl()->handleInputEvent(mouseDownEvent); |
| webViewImpl()->handleInputEvent(mouseUpEvent); |
| |
| Mock::VerifyAndClearExpectations(&mockWebFrameClient); |
| mouseDownEvent.button = WebMouseEvent::ButtonLeft; |
| webViewImpl()->handleInputEvent(mouseDownEvent); |
| |
| // Now pinch zoom into the page and move the visual viewport. The context |
| // menu should still appear at the location of the event, relative to the |
| // WebView. |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| webViewImpl()->setPageScaleFactor(2); |
| visualViewport.setLocation(FloatPoint(60, 80)); |
| EXPECT_CALL(mockWebFrameClient, showContextMenu(ContextMenuAtLocation(mouseDownEvent.x, mouseDownEvent.y))); |
| |
| mouseDownEvent.button = WebMouseEvent::ButtonRight; |
| webViewImpl()->handleInputEvent(mouseDownEvent); |
| webViewImpl()->handleInputEvent(mouseUpEvent); |
| |
| // Reset the old client so destruction can occur naturally. |
| webViewImpl()->mainFrameImpl()->setClient(oldClient); |
| } |
| |
| // Test that the client is notified if page scroll events. |
| MAYBE_TEST_P(TestClientNotifiedOfScrollEvents) |
| { |
| initializeWithAndroidSettings(); |
| webViewImpl()->resize(IntSize(200, 300)); |
| |
| registerMockedHttpURLLoad("200-by-300.html"); |
| navigateTo(m_baseURL + "200-by-300.html"); |
| |
| WebFrameClient* oldClient = webViewImpl()->mainFrameImpl()->client(); |
| MockWebFrameClient mockWebFrameClient; |
| webViewImpl()->mainFrameImpl()->setClient(&mockWebFrameClient); |
| |
| webViewImpl()->setPageScaleFactor(2); |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| |
| EXPECT_CALL(mockWebFrameClient, didChangeScrollOffset(_)); |
| visualViewport.setLocation(FloatPoint(60, 80)); |
| Mock::VerifyAndClearExpectations(&mockWebFrameClient); |
| |
| // Scroll vertically. |
| EXPECT_CALL(mockWebFrameClient, didChangeScrollOffset(_)); |
| visualViewport.setLocation(FloatPoint(60, 90)); |
| Mock::VerifyAndClearExpectations(&mockWebFrameClient); |
| |
| // Scroll horizontally. |
| EXPECT_CALL(mockWebFrameClient, didChangeScrollOffset(_)); |
| visualViewport.setLocation(FloatPoint(70, 90)); |
| |
| // Reset the old client so destruction can occur naturally. |
| webViewImpl()->mainFrameImpl()->setClient(oldClient); |
| } |
| |
| // Tests that calling scroll into view on a visible element doesn cause |
| // a scroll due to a fractional offset. Bug crbug.com/463356. |
| TEST_P(ParameterizedVisualViewportTest, ScrollIntoViewFractionalOffset) |
| { |
| initializeWithAndroidSettings(); |
| |
| webViewImpl()->resize(IntSize(1000, 1000)); |
| |
| registerMockedHttpURLLoad("scroll-into-view.html"); |
| navigateTo(m_baseURL + "scroll-into-view.html"); |
| |
| FrameView& frameView = *webViewImpl()->mainFrameImpl()->frameView(); |
| ScrollableArea* layoutViewportScrollableArea = frameView.layoutViewportScrollableArea(); |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| Element* inputBox = frame()->document()->getElementById("box"); |
| |
| webViewImpl()->setPageScaleFactor(2); |
| |
| // The element is already in the view so the scrollIntoView shouldn't move |
| // the viewport at all. |
| webViewImpl()->setVisualViewportOffset(WebFloatPoint(250.25f, 100.25f)); |
| layoutViewportScrollableArea->setScrollPosition(DoublePoint(0, 900.75), ProgrammaticScroll); |
| inputBox->scrollIntoViewIfNeeded(false); |
| |
| EXPECT_POINT_EQ(DoublePoint(0, 900.75), layoutViewportScrollableArea->scrollPositionDouble()); |
| EXPECT_POINT_EQ(FloatPoint(250.25f, 100.25f), visualViewport.location()); |
| |
| // Change the fractional part of the frameview to one that would round down. |
| layoutViewportScrollableArea->setScrollPosition(DoublePoint(0, 900.125), ProgrammaticScroll); |
| inputBox->scrollIntoViewIfNeeded(false); |
| |
| EXPECT_POINT_EQ(DoublePoint(0, 900.125), layoutViewportScrollableArea->scrollPositionDouble()); |
| EXPECT_POINT_EQ(FloatPoint(250.25f, 100.25f), visualViewport.location()); |
| |
| // Repeat both tests above with the visual viewport at a high fractional. |
| webViewImpl()->setVisualViewportOffset(WebFloatPoint(250.875f, 100.875f)); |
| layoutViewportScrollableArea->setScrollPosition(DoublePoint(0, 900.75), ProgrammaticScroll); |
| inputBox->scrollIntoViewIfNeeded(false); |
| |
| EXPECT_POINT_EQ(DoublePoint(0, 900.75), layoutViewportScrollableArea->scrollPositionDouble()); |
| EXPECT_POINT_EQ(FloatPoint(250.875f, 100.875f), visualViewport.location()); |
| |
| // Change the fractional part of the frameview to one that would round down. |
| layoutViewportScrollableArea->setScrollPosition(DoublePoint(0, 900.125), ProgrammaticScroll); |
| inputBox->scrollIntoViewIfNeeded(false); |
| |
| EXPECT_POINT_EQ(DoublePoint(0, 900.125), layoutViewportScrollableArea->scrollPositionDouble()); |
| EXPECT_POINT_EQ(FloatPoint(250.875f, 100.875f), visualViewport.location()); |
| |
| // Both viewports with a 0.5 fraction. |
| webViewImpl()->setVisualViewportOffset(WebFloatPoint(250.5f, 100.5f)); |
| layoutViewportScrollableArea->setScrollPosition(DoublePoint(0, 900.5), ProgrammaticScroll); |
| inputBox->scrollIntoViewIfNeeded(false); |
| |
| EXPECT_POINT_EQ(DoublePoint(0, 900.5), layoutViewportScrollableArea->scrollPositionDouble()); |
| EXPECT_POINT_EQ(FloatPoint(250.5f, 100.5f), visualViewport.location()); |
| } |
| |
| // Top controls can make an unscrollable page temporarily scrollable, causing |
| // a scroll clamp when the page is resized. Make sure this bug is fixed. |
| // crbug.com/437620 |
| MAYBE_TEST_F(TestResizeDoesntChangeScrollOffset) |
| { |
| initializeWithAndroidSettings(); |
| webViewImpl()->resize(IntSize(980, 650)); |
| |
| navigateTo("about:blank"); |
| |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| FrameView& frameView = *webViewImpl()->mainFrameImpl()->frameView(); |
| |
| webViewImpl()->setTopControlsHeight(20, false); |
| |
| // Outer viewport isn't scrollable |
| EXPECT_SIZE_EQ(IntSize(980, 650), frameView.visibleContentRect().size()); |
| |
| visualViewport.setScale(2); |
| visualViewport.move(FloatPoint(0, 40)); |
| |
| // Simulate bringing down the top controls by 20px but counterscrolling the outer viewport. |
| webViewImpl()->applyViewportDeltas(WebFloatSize(), WebFloatSize(0, 20), WebFloatSize(), 1, 1); |
| |
| EXPECT_EQ(20, frameView.layoutViewportScrollableArea()->scrollPosition().y()); |
| |
| webViewImpl()->setTopControlsHeight(20, true); |
| webViewImpl()->resize(WebSize(980, 630)); |
| |
| EXPECT_EQ(0, frameView.layoutViewportScrollableArea()->scrollPosition().y()); |
| EXPECT_EQ(60, visualViewport.location().y()); |
| } |
| |
| static IntPoint expectedMaxFrameViewScrollOffset(VisualViewport& visualViewport, FrameView& frameView) |
| { |
| float aspectRatio = visualViewport.visibleRect().width() / visualViewport.visibleRect().height(); |
| float newHeight = frameView.frameRect().width() / aspectRatio; |
| return IntPoint( |
| frameView.contentsSize().width() - frameView.frameRect().width(), |
| frameView.contentsSize().height() - newHeight); |
| } |
| |
| MAYBE_TEST_F(TestTopControlsAdjustment) |
| { |
| initializeWithAndroidSettings(); |
| webViewImpl()->resize(IntSize(500, 450)); |
| |
| registerMockedHttpURLLoad("content-width-1000.html"); |
| navigateTo(m_baseURL + "content-width-1000.html"); |
| |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| FrameView& frameView = *webViewImpl()->mainFrameImpl()->frameView(); |
| |
| webViewImpl()->setTopControlsHeight(20, false); |
| |
| visualViewport.setScale(1); |
| EXPECT_SIZE_EQ(IntSize(500, 450), visualViewport.visibleRect().size()); |
| EXPECT_SIZE_EQ(IntSize(1000, 900), frameView.frameRect().size()); |
| |
| // Simulate bringing down the top controls by 20px. |
| webViewImpl()->applyViewportDeltas(WebFloatSize(), WebFloatSize(), WebFloatSize(), 1, 1); |
| EXPECT_SIZE_EQ(IntSize(500, 430), visualViewport.visibleRect().size()); |
| |
| // Test that the scroll bounds are adjusted appropriately: the visual viewport |
| // should be shrunk by 20px to 430px. The outer viewport was shrunk to maintain the |
| // aspect ratio so it's height is 860px. |
| visualViewport.move(FloatPoint(10000, 10000)); |
| EXPECT_POINT_EQ(FloatPoint(500, 860 - 430), visualViewport.location()); |
| |
| // The outer viewport (FrameView) should be affected as well. |
| frameView.scrollBy(IntSize(10000, 10000), UserScroll); |
| EXPECT_POINT_EQ( |
| expectedMaxFrameViewScrollOffset(visualViewport, frameView), |
| frameView.scrollPosition()); |
| |
| // Simulate bringing up the top controls by 10.5px. |
| webViewImpl()->applyViewportDeltas(WebFloatSize(), WebFloatSize(), WebFloatSize(), 1, -10.5f / 20); |
| EXPECT_FLOAT_SIZE_EQ(FloatSize(500, 440.5f), visualViewport.visibleRect().size()); |
| |
| // maximumScrollPosition floors the final values. |
| visualViewport.move(FloatPoint(10000, 10000)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(500, 881 - 440.5f), visualViewport.location()); |
| |
| // The outer viewport (FrameView) should be affected as well. |
| frameView.scrollBy(IntSize(10000, 10000), UserScroll); |
| EXPECT_POINT_EQ( |
| expectedMaxFrameViewScrollOffset(visualViewport, frameView), |
| frameView.scrollPosition()); |
| } |
| |
| MAYBE_TEST_F(TestTopControlsAdjustmentWithScale) |
| { |
| initializeWithAndroidSettings(); |
| webViewImpl()->resize(IntSize(500, 450)); |
| |
| registerMockedHttpURLLoad("content-width-1000.html"); |
| navigateTo(m_baseURL + "content-width-1000.html"); |
| |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| FrameView& frameView = *webViewImpl()->mainFrameImpl()->frameView(); |
| |
| webViewImpl()->setTopControlsHeight(20, false); |
| |
| visualViewport.setScale(2); |
| EXPECT_SIZE_EQ(IntSize(250, 225), visualViewport.visibleRect().size()); |
| EXPECT_SIZE_EQ(IntSize(1000, 900), frameView.frameRect().size()); |
| |
| // Simulate bringing down the top controls by 20px. Since we're zoomed in, |
| // the top controls take up half as much space (in document-space) than |
| // they do at an unzoomed level. |
| webViewImpl()->applyViewportDeltas(WebFloatSize(), WebFloatSize(), WebFloatSize(), 1, 1); |
| EXPECT_SIZE_EQ(IntSize(250, 215), visualViewport.visibleRect().size()); |
| |
| // Test that the scroll bounds are adjusted appropriately. |
| visualViewport.move(FloatPoint(10000, 10000)); |
| EXPECT_POINT_EQ(FloatPoint(750, 860 - 215), visualViewport.location()); |
| |
| // The outer viewport (FrameView) should be affected as well. |
| frameView.scrollBy(IntSize(10000, 10000), UserScroll); |
| IntPoint expected = expectedMaxFrameViewScrollOffset(visualViewport, frameView); |
| EXPECT_POINT_EQ(expected, frameView.scrollPosition()); |
| |
| // Scale back out, FrameView max scroll shouldn't have changed. Visual |
| // viewport should be moved up to accomodate larger view. |
| webViewImpl()->applyViewportDeltas(WebFloatSize(), WebFloatSize(), WebFloatSize(), 0.5f, 0); |
| EXPECT_EQ(1, visualViewport.scale()); |
| EXPECT_POINT_EQ(expected, frameView.scrollPosition()); |
| frameView.scrollBy(IntSize(10000, 10000), UserScroll); |
| EXPECT_POINT_EQ(expected, frameView.scrollPosition()); |
| |
| EXPECT_POINT_EQ(FloatPoint(500, 860 - 430), visualViewport.location()); |
| visualViewport.move(FloatPoint(10000, 10000)); |
| EXPECT_POINT_EQ(FloatPoint(500, 860 - 430), visualViewport.location()); |
| |
| // Scale out, use a scale that causes fractional rects. |
| webViewImpl()->applyViewportDeltas(WebFloatSize(), WebFloatSize(), WebFloatSize(), 0.8f, -1); |
| EXPECT_SIZE_EQ(FloatSize(625, 562.5), visualViewport.visibleRect().size()); |
| |
| // Bring out the top controls by 11 |
| webViewImpl()->applyViewportDeltas(WebFloatSize(), WebFloatSize(), WebFloatSize(), 1, 11 / 20.f); |
| EXPECT_SIZE_EQ(FloatSize(625, 548.75), visualViewport.visibleRect().size()); |
| |
| // Ensure max scroll offsets are updated properly. |
| visualViewport.move(FloatPoint(10000, 10000)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(375, 877.5 - 548.75), visualViewport.location()); |
| |
| frameView.scrollBy(IntSize(10000, 10000), UserScroll); |
| EXPECT_POINT_EQ( |
| expectedMaxFrameViewScrollOffset(visualViewport, frameView), |
| frameView.scrollPosition()); |
| |
| } |
| |
| MAYBE_TEST_F(TestTopControlsAdjustmentAndResize) |
| { |
| initializeWithAndroidSettings(); |
| webViewImpl()->resize(IntSize(500, 450)); |
| |
| registerMockedHttpURLLoad("content-width-1000.html"); |
| navigateTo(m_baseURL + "content-width-1000.html"); |
| |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| FrameView& frameView = *webViewImpl()->mainFrameImpl()->frameView(); |
| |
| visualViewport.setScale(2); |
| EXPECT_SIZE_EQ(IntSize(250, 225), visualViewport.visibleRect().size()); |
| EXPECT_SIZE_EQ(IntSize(1000, 900), frameView.frameRect().size()); |
| |
| webViewImpl()->setTopControlsHeight(20, false); |
| |
| webViewImpl()->applyViewportDeltas(WebFloatSize(), WebFloatSize(), WebFloatSize(), 1, 1); |
| EXPECT_SIZE_EQ(IntSize(500, 450), visualViewport.size()); |
| EXPECT_SIZE_EQ(IntSize(250, 215), visualViewport.visibleRect().size()); |
| |
| // Scroll all the way to the bottom. |
| visualViewport.move(FloatPoint(10000, 10000)); |
| frameView.scrollBy(IntSize(10000, 10000), UserScroll); |
| IntPoint frameViewExpected = expectedMaxFrameViewScrollOffset(visualViewport, frameView); |
| FloatPoint visualViewportExpected = FloatPoint(750, 860 - 215); |
| EXPECT_POINT_EQ(visualViewportExpected, visualViewport.location()); |
| EXPECT_POINT_EQ(frameViewExpected, frameView.scrollPosition()); |
| |
| // Resize the widget to match the top controls adjustment. Ensure that scroll |
| // offsets don't get clamped in the the process. |
| webViewImpl()->setTopControlsHeight(20, true); |
| webViewImpl()->resize(WebSize(500, 430)); |
| |
| EXPECT_SIZE_EQ(IntSize(500, 430), visualViewport.size()); |
| EXPECT_SIZE_EQ(IntSize(250, 215), visualViewport.visibleRect().size()); |
| EXPECT_SIZE_EQ(IntSize(1000, 860), frameView.frameRect().size()); |
| |
| EXPECT_POINT_EQ(frameViewExpected, frameView.scrollPosition()); |
| EXPECT_POINT_EQ(visualViewportExpected, visualViewport.location()); |
| } |
| |
| // Tests that a resize due to top controls hiding doesn't incorrectly clamp the |
| // main frame's scroll offset. crbug.com/428193. |
| MAYBE_TEST_F(TestTopControlHidingResizeDoesntClampMainFrame) |
| { |
| initializeWithAndroidSettings(); |
| webViewImpl()->setTopControlsHeight(500, false); |
| webViewImpl()->applyViewportDeltas(WebFloatSize(), WebFloatSize(), WebFloatSize(), 1, 1); |
| webViewImpl()->setTopControlsHeight(500, true); |
| webViewImpl()->resize(IntSize(1000, 1000)); |
| |
| registerMockedHttpURLLoad("content-width-1000.html"); |
| navigateTo(m_baseURL + "content-width-1000.html"); |
| |
| // Scroll the FrameView to the bottom of the page but "hide" the top |
| // controls on the compositor side so the max scroll position should account |
| // for the full viewport height. |
| webViewImpl()->applyViewportDeltas(WebFloatSize(), WebFloatSize(), WebFloatSize(), 1, -1); |
| FrameView& frameView = *webViewImpl()->mainFrameImpl()->frameView(); |
| frameView.setScrollPosition(IntPoint(0, 10000), ProgrammaticScroll); |
| EXPECT_EQ(500, frameView.scrollPositionDouble().y()); |
| |
| // Now send the resize, make sure the scroll offset doesn't change. |
| webViewImpl()->setTopControlsHeight(500, false); |
| webViewImpl()->resize(IntSize(1000, 1500)); |
| EXPECT_EQ(500, frameView.scrollPositionDouble().y()); |
| } |
| |
| // Tests that the layout viewport's scroll layer bounds are updated in a compositing |
| // change update. crbug.com/423188. |
| MAYBE_TEST_P(TestChangingContentSizeAffectsScrollBounds) |
| { |
| initializeWithAndroidSettings(); |
| webViewImpl()->resize(IntSize(100, 150)); |
| |
| registerMockedHttpURLLoad("content-width-1000.html"); |
| navigateTo(m_baseURL + "content-width-1000.html"); |
| |
| FrameView& frameView = *webViewImpl()->mainFrameImpl()->frameView(); |
| WebLayer* scrollLayer = frameView.layerForScrolling()->platformLayer(); |
| |
| webViewImpl()->mainFrame()->executeScript(WebScriptSource( |
| "var content = document.getElementById(\"content\");" |
| "content.style.width = \"1500px\";" |
| "content.style.height = \"2400px\";")); |
| frameView.updateAllLifecyclePhases(); |
| |
| EXPECT_SIZE_EQ(IntSize(1500, 2400), IntSize(scrollLayer->bounds())); |
| } |
| |
| // Tests that resizing the visual viepwort keeps its bounds within the outer |
| // viewport. |
| MAYBE_TEST_P(ResizeVisualViewportStaysWithinOuterViewport) |
| { |
| initializeWithDesktopSettings(); |
| webViewImpl()->resize(IntSize(100, 200)); |
| |
| navigateTo("about:blank"); |
| webViewImpl()->updateAllLifecyclePhases(); |
| |
| webViewImpl()->resizeVisualViewport(IntSize(100, 100)); |
| |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| visualViewport.move(FloatPoint(0, 100)); |
| |
| EXPECT_EQ(100, visualViewport.location().y()); |
| |
| webViewImpl()->resizeVisualViewport(IntSize(100, 200)); |
| |
| EXPECT_EQ(0, visualViewport.location().y()); |
| } |
| |
| MAYBE_TEST_P(ElementBoundsInViewportSpaceAccountsForViewport) |
| { |
| initializeWithAndroidSettings(); |
| |
| webViewImpl()->resize(IntSize(500, 800)); |
| |
| registerMockedHttpURLLoad("pinch-viewport-input-field.html"); |
| navigateTo(m_baseURL + "pinch-viewport-input-field.html"); |
| |
| webViewImpl()->setInitialFocus(false); |
| Element* inputElement = webViewImpl()->focusedElement(); |
| |
| IntRect bounds = inputElement->layoutObject()->absoluteBoundingBoxRect(); |
| |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| IntPoint scrollDelta(250, 400); |
| visualViewport.setScale(2); |
| visualViewport.setLocation(scrollDelta); |
| |
| const IntRect boundsInViewport = inputElement->boundsInViewport(); |
| IntRect expectedBounds = bounds; |
| expectedBounds.scale(2.f); |
| IntPoint expectedScrollDelta = scrollDelta; |
| expectedScrollDelta.scale(2.f, 2.f); |
| |
| EXPECT_POINT_EQ(IntPoint(expectedBounds.location() - expectedScrollDelta), |
| boundsInViewport.location()); |
| EXPECT_SIZE_EQ(expectedBounds.size(), boundsInViewport.size()); |
| } |
| |
| // Test that the various window.scroll and document.body.scroll properties and |
| // methods work unchanged from the pre-virtual viewport mode. |
| TEST_P(ParameterizedVisualViewportTest, bodyAndWindowScrollPropertiesAccountForViewport) |
| { |
| initializeWithAndroidSettings(); |
| |
| webViewImpl()->resize(IntSize(200, 300)); |
| |
| // Load page with no main frame scrolling. |
| registerMockedHttpURLLoad("200-by-300-viewport.html"); |
| navigateTo(m_baseURL + "200-by-300-viewport.html"); |
| |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| visualViewport.setScale(2); |
| |
| // Chrome's quirky behavior regarding viewport scrolling means we treat the |
| // body element as the viewport and don't apply scrolling to the HTML |
| // element. |
| RuntimeEnabledFeatures::setScrollTopLeftInteropEnabled(false); |
| |
| LocalDOMWindow* window = webViewImpl()->mainFrameImpl()->frame()->localDOMWindow(); |
| window->scrollTo(100, 150); |
| EXPECT_EQ(100, window->scrollX()); |
| EXPECT_EQ(150, window->scrollY()); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(100, 150), visualViewport.location()); |
| |
| HTMLElement* body = toHTMLBodyElement(window->document()->body()); |
| body->setScrollLeft(50); |
| body->setScrollTop(130); |
| EXPECT_EQ(50, body->scrollLeft()); |
| EXPECT_EQ(130, body->scrollTop()); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(50, 130), visualViewport.location()); |
| |
| HTMLElement* documentElement = toHTMLElement(window->document()->documentElement()); |
| documentElement->setScrollLeft(40); |
| documentElement->setScrollTop(50); |
| EXPECT_EQ(0, documentElement->scrollLeft()); |
| EXPECT_EQ(0, documentElement->scrollTop()); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(50, 130), visualViewport.location()); |
| |
| visualViewport.setLocation(FloatPoint(10, 20)); |
| EXPECT_EQ(10, body->scrollLeft()); |
| EXPECT_EQ(20, body->scrollTop()); |
| EXPECT_EQ(0, documentElement->scrollLeft()); |
| EXPECT_EQ(0, documentElement->scrollTop()); |
| EXPECT_EQ(10, window->scrollX()); |
| EXPECT_EQ(20, window->scrollY()); |
| |
| // Turning on the standards-compliant viewport scrolling impl should make |
| // the document element the viewport and not body. |
| RuntimeEnabledFeatures::setScrollTopLeftInteropEnabled(true); |
| |
| window->scrollTo(100, 150); |
| EXPECT_EQ(100, window->scrollX()); |
| EXPECT_EQ(150, window->scrollY()); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(100, 150), visualViewport.location()); |
| |
| body->setScrollLeft(50); |
| body->setScrollTop(130); |
| EXPECT_EQ(0, body->scrollLeft()); |
| EXPECT_EQ(0, body->scrollTop()); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(100, 150), visualViewport.location()); |
| |
| documentElement->setScrollLeft(40); |
| documentElement->setScrollTop(50); |
| EXPECT_EQ(40, documentElement->scrollLeft()); |
| EXPECT_EQ(50, documentElement->scrollTop()); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(40, 50), visualViewport.location()); |
| |
| visualViewport.setLocation(FloatPoint(10, 20)); |
| EXPECT_EQ(0, body->scrollLeft()); |
| EXPECT_EQ(0, body->scrollTop()); |
| EXPECT_EQ(10, documentElement->scrollLeft()); |
| EXPECT_EQ(20, documentElement->scrollTop()); |
| EXPECT_EQ(10, window->scrollX()); |
| EXPECT_EQ(20, window->scrollY()); |
| } |
| |
| // Tests that when a new frame is created, it is created with the intended |
| // size (i.e. viewport at minimum scale, 100x200 / 0.5). |
| MAYBE_TEST_P(TestMainFrameInitializationSizing) |
| { |
| initializeWithAndroidSettings(); |
| |
| webViewImpl()->resize(IntSize(100, 200)); |
| |
| registerMockedHttpURLLoad("content-width-1000-min-scale.html"); |
| navigateTo(m_baseURL + "content-width-1000-min-scale.html"); |
| |
| WebLocalFrameImpl* localFrame = webViewImpl()->mainFrameImpl(); |
| // The detach() and dispose() calls are a hack to prevent this test |
| // from violating invariants about frame state during navigation/detach. |
| localFrame->frame()->document()->detach(); |
| localFrame->createFrameView(); |
| |
| FrameView& frameView = *localFrame->frameView(); |
| EXPECT_SIZE_EQ(IntSize(200, 400), frameView.frameRect().size()); |
| frameView.dispose(); |
| } |
| |
| // Tests that the maximum scroll offset of the viewport can be fractional. |
| MAYBE_TEST_P(FractionalMaxScrollOffset) |
| { |
| initializeWithDesktopSettings(); |
| webViewImpl()->resize(IntSize(101, 201)); |
| navigateTo("about:blank"); |
| |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| ScrollableArea* scrollableArea = &visualViewport; |
| |
| webViewImpl()->setPageScaleFactor(1.0); |
| EXPECT_FLOAT_POINT_EQ(DoublePoint(), scrollableArea->maximumScrollPositionDouble()); |
| |
| webViewImpl()->setPageScaleFactor(2); |
| EXPECT_FLOAT_POINT_EQ(DoublePoint(101. / 2., 201. / 2.), scrollableArea->maximumScrollPositionDouble()); |
| } |
| |
| // Tests that the slow scrolling after an impl scroll on the visual viewport |
| // is continuous. crbug.com/453460 was caused by the impl-path not updating the |
| // ScrollAnimatorBase class. |
| MAYBE_TEST_P(SlowScrollAfterImplScroll) |
| { |
| initializeWithDesktopSettings(); |
| webViewImpl()->resize(IntSize(800, 600)); |
| navigateTo("about:blank"); |
| |
| VisualViewport& visualViewport = frame()->page()->frameHost().visualViewport(); |
| |
| // Apply some scroll and scale from the impl-side. |
| webViewImpl()->applyViewportDeltas( |
| WebFloatSize(300, 200), |
| WebFloatSize(0, 0), |
| WebFloatSize(0, 0), |
| 2, |
| 0); |
| |
| EXPECT_POINT_EQ(FloatPoint(300, 200), visualViewport.location()); |
| |
| // Send a scroll event on the main thread path. |
| PlatformGestureEvent gsu( |
| PlatformEvent::GestureScrollUpdate, |
| IntPoint(0, 0), |
| IntPoint(0, 0), |
| IntSize(5, 5), |
| 0, PlatformEvent::NoModifiers, PlatformGestureSourceTouchpad); |
| gsu.setScrollGestureData(-50, -60, ScrollByPrecisePixel, 1, 1, false, false, -1 /* null plugin id */); |
| |
| frame()->eventHandler().handleGestureEvent(gsu); |
| |
| // The scroll sent from the impl-side must not be overwritten. |
| EXPECT_POINT_EQ(FloatPoint(350, 260), visualViewport.location()); |
| } |
| |
| static void accessibilitySettings(WebSettings* settings) |
| { |
| VisualViewportTest::configureSettings(settings); |
| settings->setAccessibilityEnabled(true); |
| } |
| |
| MAYBE_TEST_P(AccessibilityHitTestWhileZoomedIn) |
| { |
| initializeWithDesktopSettings(accessibilitySettings); |
| |
| registerMockedHttpURLLoad("hit-test.html"); |
| navigateTo(m_baseURL + "hit-test.html"); |
| |
| webViewImpl()->resize(IntSize(500, 500)); |
| webViewImpl()->updateAllLifecyclePhases(); |
| |
| WebDocument webDoc = webViewImpl()->mainFrame()->document(); |
| FrameView& frameView = *webViewImpl()->mainFrameImpl()->frameView(); |
| |
| webViewImpl()->setPageScaleFactor(2); |
| webViewImpl()->setVisualViewportOffset(WebFloatPoint(200, 230)); |
| frameView.layoutViewportScrollableArea()->setScrollPosition(DoublePoint(400, 1100), ProgrammaticScroll); |
| |
| // FIXME(504057): PaintLayerScrollableArea dirties the compositing state. |
| forceFullCompositingUpdate(); |
| |
| // Because of where the visual viewport is located, this should hit the bottom right |
| // target (target 4). |
| WebAXObject hitNode = webDoc.accessibilityObject().hitTest(WebPoint(154, 165)); |
| WebAXNameFrom nameFrom; |
| WebVector<WebAXObject> nameObjects; |
| EXPECT_EQ(std::string("Target4"), hitNode.name(nameFrom, nameObjects).utf8()); |
| } |
| |
| // Tests that the maximum scroll offset of the viewport can be fractional. |
| MAYBE_TEST_P(TestCoordinateTransforms) |
| { |
| initializeWithAndroidSettings(); |
| webViewImpl()->resize(IntSize(800, 600)); |
| registerMockedHttpURLLoad("content-width-1000.html"); |
| navigateTo(m_baseURL + "content-width-1000.html"); |
| |
| VisualViewport& visualViewport = webViewImpl()->page()->frameHost().visualViewport(); |
| FrameView& frameView = *webViewImpl()->mainFrameImpl()->frameView(); |
| |
| // At scale = 1 the transform should be a no-op. |
| visualViewport.setScale(1); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(314, 273), visualViewport.viewportToRootFrame(FloatPoint(314, 273))); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(314, 273), visualViewport.rootFrameToViewport(FloatPoint(314, 273))); |
| |
| // At scale = 2. |
| visualViewport.setScale(2); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(55, 75), visualViewport.viewportToRootFrame(FloatPoint(110, 150))); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(110, 150), visualViewport.rootFrameToViewport(FloatPoint(55, 75))); |
| |
| // At scale = 2 and with the visual viewport offset. |
| visualViewport.setLocation(FloatPoint(10, 12)); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(50, 62), visualViewport.viewportToRootFrame(FloatPoint(80, 100))); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(80, 100), visualViewport.rootFrameToViewport(FloatPoint(50, 62))); |
| |
| // Test points that will cause non-integer values. |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(50.5, 62.4), visualViewport.viewportToRootFrame(FloatPoint(81, 100.8))); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(81, 100.8), visualViewport.rootFrameToViewport(FloatPoint(50.5, 62.4))); |
| |
| |
| // Scrolling the main frame should have no effect. |
| frameView.layoutViewportScrollableArea()->setScrollPosition(DoublePoint(100, 120), ProgrammaticScroll); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(50, 62), visualViewport.viewportToRootFrame(FloatPoint(80, 100))); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(80, 100), visualViewport.rootFrameToViewport(FloatPoint(50, 62))); |
| } |
| |
| // Tests that the window dimensions are available before a full layout occurs. |
| // More specifically, it checks that the innerWidth and innerHeight window |
| // properties will trigger a layout which will cause an update to viewport |
| // constraints and a refreshed initial scale. crbug.com/466718 |
| MAYBE_TEST_P(WindowDimensionsOnLoad) |
| { |
| initializeWithAndroidSettings(); |
| registerMockedHttpURLLoad("window_dimensions.html"); |
| webViewImpl()->resize(IntSize(800, 600)); |
| navigateTo(m_baseURL + "window_dimensions.html"); |
| |
| Element* output = frame()->document()->getElementById("output"); |
| ASSERT(output); |
| EXPECT_EQ(std::string("1600x1200"), std::string(output->innerHTML().ascii().data())); |
| } |
| |
| // Similar to above but make sure the initial scale is updated with the content |
| // width for a very wide page. That is, make that innerWidth/Height actually |
| // trigger a layout of the content, and not just an update of the viepwort. |
| // crbug.com/466718 |
| MAYBE_TEST_P(WindowDimensionsOnLoadWideContent) |
| { |
| initializeWithAndroidSettings(); |
| registerMockedHttpURLLoad("window_dimensions_wide_div.html"); |
| webViewImpl()->resize(IntSize(800, 600)); |
| navigateTo(m_baseURL + "window_dimensions_wide_div.html"); |
| |
| Element* output = frame()->document()->getElementById("output"); |
| ASSERT(output); |
| EXPECT_EQ(std::string("2000x1500"), std::string(output->innerHTML().ascii().data())); |
| } |
| |
| MAYBE_TEST_P(PinchZoomGestureScrollsVisualViewportOnly) |
| { |
| initializeWithDesktopSettings(); |
| webViewImpl()->resize(IntSize(100, 100)); |
| |
| registerMockedHttpURLLoad("200-by-800-viewport.html"); |
| navigateTo(m_baseURL + "200-by-800-viewport.html"); |
| |
| WebGestureEvent pinchUpdate; |
| pinchUpdate.type = WebInputEvent::GesturePinchUpdate; |
| pinchUpdate.sourceDevice = WebGestureDeviceTouchpad; |
| pinchUpdate.x = 100; |
| pinchUpdate.y = 100; |
| pinchUpdate.data.pinchUpdate.scale = 2; |
| pinchUpdate.data.pinchUpdate.zoomDisabled = false; |
| |
| webViewImpl()->handleInputEvent(pinchUpdate); |
| |
| VisualViewport& visualViewport = webViewImpl()->page()->frameHost().visualViewport(); |
| FrameView& frameView = *webViewImpl()->mainFrameImpl()->frameView(); |
| |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(50, 50), visualViewport.location()); |
| EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), frameView.scrollPositionDouble()); |
| } |
| |
| } // namespace |