/* * Copyright (C) 2010 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "public/web/WebFrame.h" #include "SkBitmap.h" #include "SkCanvas.h" #include "core/UserAgentStyleSheets.h" #include "core/clipboard/Clipboard.h" #include "core/css/StyleSheetContents.h" #include "core/css/resolver/ViewportStyleResolver.h" #include "core/dom/DocumentMarkerController.h" #include "core/dom/FullscreenElementStack.h" #include "core/dom/Range.h" #include "core/editing/Editor.h" #include "core/editing/FrameSelection.h" #include "core/editing/SpellChecker.h" #include "core/editing/VisiblePosition.h" #include "core/events/MouseEvent.h" #include "core/frame/FrameView.h" #include "core/frame/LocalFrame.h" #include "core/frame/Settings.h" #include "core/html/HTMLFormElement.h" #include "core/loader/FrameLoadRequest.h" #include "core/page/EventHandler.h" #include "core/rendering/HitTestResult.h" #include "core/rendering/RenderView.h" #include "core/rendering/TextAutosizer.h" #include "core/rendering/compositing/RenderLayerCompositor.h" #include "platform/DragImage.h" #include "platform/RuntimeEnabledFeatures.h" #include "platform/UserGestureIndicator.h" #include "platform/geometry/FloatRect.h" #include "platform/network/ResourceError.h" #include "platform/scroll/ScrollbarTheme.h" #include "public/platform/Platform.h" #include "public/platform/WebFloatRect.h" #include "public/platform/WebThread.h" #include "public/platform/WebURL.h" #include "public/platform/WebURLResponse.h" #include "public/platform/WebUnitTestSupport.h" #include "public/web/WebDataSource.h" #include "public/web/WebDocument.h" #include "public/web/WebFindOptions.h" #include "public/web/WebFormElement.h" #include "public/web/WebFrameClient.h" #include "public/web/WebHistoryItem.h" #include "public/web/WebRange.h" #include "public/web/WebScriptSource.h" #include "public/web/WebSearchableFormData.h" #include "public/web/WebSecurityOrigin.h" #include "public/web/WebSecurityPolicy.h" #include "public/web/WebSettings.h" #include "public/web/WebSpellCheckClient.h" #include "public/web/WebTextCheckingCompletion.h" #include "public/web/WebTextCheckingResult.h" #include "public/web/WebViewClient.h" #include "web/WebLocalFrameImpl.h" #include "web/WebViewImpl.h" #include "web/tests/FrameTestHelpers.h" #include "web/tests/URLTestHelpers.h" #include "wtf/Forward.h" #include "wtf/dtoa/utils.h" #include #include #include #include using namespace blink; using WebCore::Document; using WebCore::DocumentMarker; using WebCore::Element; using WebCore::FloatRect; using WebCore::HitTestRequest; using WebCore::Range; using blink::URLTestHelpers::toKURL; using blink::FrameTestHelpers::runPendingTasks; namespace { const int touchPointPadding = 32; #define EXPECT_EQ_RECT(a, b) \ EXPECT_EQ(a.x(), b.x()); \ EXPECT_EQ(a.y(), b.y()); \ EXPECT_EQ(a.width(), b.width()); \ EXPECT_EQ(a.height(), b.height()); class FakeCompositingWebViewClient : public FrameTestHelpers::TestWebViewClient { public: virtual bool enterFullScreen() OVERRIDE { return true; } }; class WebFrameTest : public testing::Test { protected: WebFrameTest() : m_baseURL("http://www.test.com/") , m_chromeURL("chrome://") { } virtual ~WebFrameTest() { Platform::current()->unitTestSupport()->unregisterAllMockedURLs(); } void registerMockedHttpURLLoad(const std::string& fileName) { URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8(fileName.c_str())); } void registerMockedChromeURLLoad(const std::string& fileName) { URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_chromeURL.c_str()), WebString::fromUTF8(fileName.c_str())); } void applyViewportStyleOverride(FrameTestHelpers::WebViewHelper* webViewHelper) { RefPtrWillBeRawPtr styleSheet = WebCore::StyleSheetContents::create(WebCore::CSSParserContext(WebCore::UASheetMode, 0)); styleSheet->parseString(String(WebCore::viewportAndroidUserAgentStyleSheet, sizeof(WebCore::viewportAndroidUserAgentStyleSheet))); OwnPtrWillBeRawPtr ruleSet = WebCore::RuleSet::create(); ruleSet->addRulesFromSheet(styleSheet.get(), WebCore::MediaQueryEvaluator("screen")); Document* document = toLocalFrame(webViewHelper->webViewImpl()->page()->mainFrame())->document(); document->ensureStyleResolver().viewportStyleResolver()->collectViewportRules(ruleSet.get(), WebCore::ViewportStyleResolver::UserAgentOrigin); document->ensureStyleResolver().viewportStyleResolver()->resolve(); } static void configueCompositingWebView(WebSettings* settings) { settings->setAcceleratedCompositingEnabled(true); settings->setAcceleratedCompositingForFixedPositionEnabled(true); settings->setAcceleratedCompositingForOverflowScrollEnabled(true); settings->setCompositedScrollingForFramesEnabled(true); } void initializeTextSelectionWebView(const std::string& url, FrameTestHelpers::WebViewHelper* webViewHelper) { webViewHelper->initializeAndLoad(url, true); webViewHelper->webView()->settings()->setDefaultFontSize(12); webViewHelper->webView()->resize(WebSize(640, 480)); } PassOwnPtr nodeImageTestSetup(FrameTestHelpers::WebViewHelper* webViewHelper, const std::string& testcase) { registerMockedHttpURLLoad("nodeimage.html"); webViewHelper->initializeAndLoad(m_baseURL + "nodeimage.html"); webViewHelper->webView()->resize(WebSize(640, 480)); webViewHelper->webView()->layout(); RefPtr frame = toLocalFrame(webViewHelper->webViewImpl()->page()->mainFrame()); WebCore::Element* element = frame->document()->getElementById(testcase.c_str()); return frame->nodeImage(*element); } std::string m_baseURL; std::string m_chromeURL; }; class UseMockScrollbarSettings { public: UseMockScrollbarSettings() { WebCore::Settings::setMockScrollbarsEnabled(true); WebCore::RuntimeEnabledFeatures::setOverlayScrollbarsEnabled(true); EXPECT_TRUE(WebCore::ScrollbarTheme::theme()->usesOverlayScrollbars()); } ~UseMockScrollbarSettings() { WebCore::Settings::setMockScrollbarsEnabled(false); WebCore::RuntimeEnabledFeatures::setOverlayScrollbarsEnabled(false); } }; TEST_F(WebFrameTest, ContentText) { registerMockedHttpURLLoad("iframes_test.html"); registerMockedHttpURLLoad("visible_iframe.html"); registerMockedHttpURLLoad("invisible_iframe.html"); registerMockedHttpURLLoad("zero_sized_iframe.html"); FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "iframes_test.html"); // Now retrieve the frames text and test it only includes visible elements. std::string content = webViewHelper.webView()->mainFrame()->contentAsText(1024).utf8(); EXPECT_NE(std::string::npos, content.find(" visible paragraph")); EXPECT_NE(std::string::npos, content.find(" visible iframe")); EXPECT_EQ(std::string::npos, content.find(" invisible pararaph")); EXPECT_EQ(std::string::npos, content.find(" invisible iframe")); EXPECT_EQ(std::string::npos, content.find("iframe with zero size")); } TEST_F(WebFrameTest, FrameForEnteredContext) { registerMockedHttpURLLoad("iframes_test.html"); registerMockedHttpURLLoad("visible_iframe.html"); registerMockedHttpURLLoad("invisible_iframe.html"); registerMockedHttpURLLoad("zero_sized_iframe.html"); FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "iframes_test.html", true); v8::HandleScope scope(v8::Isolate::GetCurrent()); EXPECT_EQ(webViewHelper.webView()->mainFrame(), WebLocalFrame::frameForContext(webViewHelper.webView()->mainFrame()->mainWorldScriptContext())); EXPECT_EQ(webViewHelper.webView()->mainFrame()->firstChild(), WebLocalFrame::frameForContext(webViewHelper.webView()->mainFrame()->firstChild()->mainWorldScriptContext())); } TEST_F(WebFrameTest, FormWithNullFrame) { registerMockedHttpURLLoad("form.html"); FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "form.html"); WebVector forms; webViewHelper.webView()->mainFrame()->document().forms(forms); webViewHelper.reset(); EXPECT_EQ(forms.size(), 1U); // This test passes if this doesn't crash. WebSearchableFormData searchableDataForm(forms[0]); } TEST_F(WebFrameTest, ChromePageJavascript) { registerMockedChromeURLLoad("history.html"); // Pass true to enable JavaScript. FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_chromeURL + "history.html", true); // Try to run JS against the chrome-style URL. FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), "javascript:document.body.appendChild(document.createTextNode('Clobbered'))"); // Required to see any updates in contentAsText. webViewHelper.webView()->layout(); // Now retrieve the frame's text and ensure it was modified by running javascript. std::string content = webViewHelper.webView()->mainFrame()->contentAsText(1024).utf8(); EXPECT_NE(std::string::npos, content.find("Clobbered")); } TEST_F(WebFrameTest, ChromePageNoJavascript) { registerMockedChromeURLLoad("history.html"); /// Pass true to enable JavaScript. FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_chromeURL + "history.html", true); // Try to run JS against the chrome-style URL after prohibiting it. WebSecurityPolicy::registerURLSchemeAsNotAllowingJavascriptURLs("chrome"); FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), "javascript:document.body.appendChild(document.createTextNode('Clobbered'))"); // Required to see any updates in contentAsText. webViewHelper.webView()->layout(); // Now retrieve the frame's text and ensure it wasn't modified by running javascript. std::string content = webViewHelper.webView()->mainFrame()->contentAsText(1024).utf8(); EXPECT_EQ(std::string::npos, content.find("Clobbered")); } TEST_F(WebFrameTest, LocationSetHostWithMissingPort) { std::string fileName = "print-location-href.html"; registerMockedHttpURLLoad(fileName); URLTestHelpers::registerMockedURLLoad(toKURL("http://www.test.com:0/" + fileName), WebString::fromUTF8(fileName)); FrameTestHelpers::WebViewHelper webViewHelper; /// Pass true to enable JavaScript. webViewHelper.initializeAndLoad(m_baseURL + fileName, true); // Setting host to "hostname:" should be treated as "hostname:0". FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), "javascript:location.host = 'www.test.com:'; void 0;"); FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), "javascript:document.body.textContent = location.href; void 0;"); std::string content = webViewHelper.webView()->mainFrame()->contentAsText(1024).utf8(); EXPECT_EQ("http://www.test.com:0/" + fileName, content); } TEST_F(WebFrameTest, LocationSetEmptyPort) { std::string fileName = "print-location-href.html"; registerMockedHttpURLLoad(fileName); URLTestHelpers::registerMockedURLLoad(toKURL("http://www.test.com:0/" + fileName), WebString::fromUTF8(fileName)); FrameTestHelpers::WebViewHelper webViewHelper; /// Pass true to enable JavaScript. webViewHelper.initializeAndLoad(m_baseURL + fileName, true); FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), "javascript:location.port = ''; void 0;"); FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), "javascript:document.body.textContent = location.href; void 0;"); std::string content = webViewHelper.webView()->mainFrame()->contentAsText(1024).utf8(); EXPECT_EQ("http://www.test.com:0/" + fileName, content); } class CSSCallbackWebFrameClient : public FrameTestHelpers::TestWebFrameClient { public: CSSCallbackWebFrameClient() : m_updateCount(0) { } virtual void didMatchCSS(WebLocalFrame*, const WebVector& newlyMatchingSelectors, const WebVector& stoppedMatchingSelectors) OVERRIDE; std::map > m_matchedSelectors; int m_updateCount; }; void CSSCallbackWebFrameClient::didMatchCSS(WebLocalFrame* frame, const WebVector& newlyMatchingSelectors, const WebVector& stoppedMatchingSelectors) { ++m_updateCount; std::set& frameSelectors = m_matchedSelectors[frame]; for (size_t i = 0; i < newlyMatchingSelectors.size(); ++i) { std::string selector = newlyMatchingSelectors[i].utf8(); EXPECT_EQ(0U, frameSelectors.count(selector)) << selector; frameSelectors.insert(selector); } for (size_t i = 0; i < stoppedMatchingSelectors.size(); ++i) { std::string selector = stoppedMatchingSelectors[i].utf8(); EXPECT_EQ(1U, frameSelectors.count(selector)) << selector; frameSelectors.erase(selector); } } class WebFrameCSSCallbackTest : public testing::Test { protected: WebFrameCSSCallbackTest() { m_frame = m_helper.initializeAndLoad("about:blank", true, &m_client)->mainFrame()->toWebLocalFrame(); } ~WebFrameCSSCallbackTest() { EXPECT_EQ(1U, m_client.m_matchedSelectors.size()); } WebDocument doc() const { return m_frame->document(); } int updateCount() const { return m_client.m_updateCount; } const std::set& matchedSelectors() { return m_client.m_matchedSelectors[m_frame]; } void loadHTML(const std::string& html) { FrameTestHelpers::loadHTMLString(m_frame, html, toKURL("about:blank")); } void executeScript(const WebString& code) { m_frame->executeScript(WebScriptSource(code)); m_frame->view()->layout(); runPendingTasks(); } CSSCallbackWebFrameClient m_client; FrameTestHelpers::WebViewHelper m_helper; WebLocalFrame* m_frame; }; TEST_F(WebFrameCSSCallbackTest, AuthorStyleSheet) { loadHTML( "" "
" "
"); std::vector selectors; selectors.push_back(WebString::fromUTF8("div.initial_on")); m_frame->document().watchCSSSelectors(WebVector(selectors)); m_frame->view()->layout(); runPendingTasks(); EXPECT_EQ(1, updateCount()); EXPECT_THAT(matchedSelectors(), testing::ElementsAre("div.initial_on")); // Check that adding a watched selector calls back for already-present nodes. selectors.push_back(WebString::fromUTF8("div.initial_off")); doc().watchCSSSelectors(WebVector(selectors)); m_frame->view()->layout(); runPendingTasks(); EXPECT_EQ(2, updateCount()); EXPECT_THAT(matchedSelectors(), testing::ElementsAre("div.initial_off", "div.initial_on")); // Check that we can turn off callbacks for certain selectors. doc().watchCSSSelectors(WebVector()); m_frame->view()->layout(); runPendingTasks(); EXPECT_EQ(3, updateCount()); EXPECT_THAT(matchedSelectors(), testing::ElementsAre()); } TEST_F(WebFrameCSSCallbackTest, SharedRenderStyle) { // Check that adding an element calls back when it matches an existing rule. std::vector selectors; selectors.push_back(WebString::fromUTF8("span")); doc().watchCSSSelectors(WebVector(selectors)); executeScript( "i1 = document.createElement('span');" "i1.id = 'first_span';" "document.body.appendChild(i1)"); EXPECT_EQ(1, updateCount()); EXPECT_THAT(matchedSelectors(), testing::ElementsAre("span")); // Adding a second element that shares a RenderStyle shouldn't call back. // We use s to avoid default style rules that can set // RenderStyle::unique(). executeScript( "i2 = document.createElement('span');" "i2.id = 'second_span';" "i1 = document.getElementById('first_span');" "i1.parentNode.insertBefore(i2, i1.nextSibling);"); EXPECT_EQ(1, updateCount()); EXPECT_THAT(matchedSelectors(), testing::ElementsAre("span")); // Removing the first element shouldn't call back. executeScript( "i1 = document.getElementById('first_span');" "i1.parentNode.removeChild(i1);"); EXPECT_EQ(1, updateCount()); EXPECT_THAT(matchedSelectors(), testing::ElementsAre("span")); // But removing the second element *should* call back. executeScript( "i2 = document.getElementById('second_span');" "i2.parentNode.removeChild(i2);"); EXPECT_EQ(2, updateCount()); EXPECT_THAT(matchedSelectors(), testing::ElementsAre()); } TEST_F(WebFrameCSSCallbackTest, CatchesAttributeChange) { loadHTML(""); std::vector selectors; selectors.push_back(WebString::fromUTF8("span[attr=\"value\"]")); doc().watchCSSSelectors(WebVector(selectors)); runPendingTasks(); EXPECT_EQ(0, updateCount()); EXPECT_THAT(matchedSelectors(), testing::ElementsAre()); executeScript( "document.querySelector('span').setAttribute('attr', 'value');"); EXPECT_EQ(1, updateCount()); EXPECT_THAT(matchedSelectors(), testing::ElementsAre("span[attr=\"value\"]")); } TEST_F(WebFrameCSSCallbackTest, DisplayNone) { loadHTML("
"); std::vector selectors; selectors.push_back(WebString::fromUTF8("span")); doc().watchCSSSelectors(WebVector(selectors)); runPendingTasks(); EXPECT_EQ(0, updateCount()) << "Don't match elements in display:none trees."; executeScript( "d = document.querySelector('div');" "d.style.display = 'block';"); EXPECT_EQ(1, updateCount()) << "Match elements when they become displayed."; EXPECT_THAT(matchedSelectors(), testing::ElementsAre("span")); executeScript( "d = document.querySelector('div');" "d.style.display = 'none';"); EXPECT_EQ(2, updateCount()) << "Unmatch elements when they become undisplayed."; EXPECT_THAT(matchedSelectors(), testing::ElementsAre()); executeScript( "s = document.querySelector('span');" "s.style.display = 'none';"); EXPECT_EQ(2, updateCount()) << "No effect from no-display'ing a span that's already undisplayed."; executeScript( "d = document.querySelector('div');" "d.style.display = 'block';"); EXPECT_EQ(2, updateCount()) << "No effect from displaying a div whose span is display:none."; executeScript( "s = document.querySelector('span');" "s.style.display = 'inline';"); EXPECT_EQ(3, updateCount()) << "Now the span is visible and produces a callback."; EXPECT_THAT(matchedSelectors(), testing::ElementsAre("span")); executeScript( "s = document.querySelector('span');" "s.style.display = 'none';"); EXPECT_EQ(4, updateCount()) << "Undisplaying the span directly should produce another callback."; EXPECT_THAT(matchedSelectors(), testing::ElementsAre()); } TEST_F(WebFrameCSSCallbackTest, Reparenting) { loadHTML( "
" "
"); std::vector selectors; selectors.push_back(WebString::fromUTF8("span")); doc().watchCSSSelectors(WebVector(selectors)); m_frame->view()->layout(); runPendingTasks(); EXPECT_EQ(1, updateCount()); EXPECT_THAT(matchedSelectors(), testing::ElementsAre("span")); executeScript( "s = document.querySelector('span');" "d2 = document.getElementById('d2');" "d2.appendChild(s);"); EXPECT_EQ(1, updateCount()) << "Just moving an element that continues to match shouldn't send a spurious callback."; EXPECT_THAT(matchedSelectors(), testing::ElementsAre("span")); } TEST_F(WebFrameCSSCallbackTest, MultiSelector) { loadHTML(""); // Check that selector lists match as the whole list, not as each element // independently. std::vector selectors; selectors.push_back(WebString::fromUTF8("span")); selectors.push_back(WebString::fromUTF8("span,p")); doc().watchCSSSelectors(WebVector(selectors)); m_frame->view()->layout(); runPendingTasks(); EXPECT_EQ(1, updateCount()); EXPECT_THAT(matchedSelectors(), testing::ElementsAre("span", "span, p")); } TEST_F(WebFrameCSSCallbackTest, InvalidSelector) { loadHTML("

"); // Build a list with one valid selector and one invalid. std::vector selectors; selectors.push_back(WebString::fromUTF8("span")); selectors.push_back(WebString::fromUTF8("[")); // Invalid. selectors.push_back(WebString::fromUTF8("p span")); // Not compound. doc().watchCSSSelectors(WebVector(selectors)); m_frame->view()->layout(); runPendingTasks(); EXPECT_EQ(1, updateCount()); EXPECT_THAT(matchedSelectors(), testing::ElementsAre("span")) << "An invalid selector shouldn't prevent other selectors from matching."; } TEST_F(WebFrameTest, DispatchMessageEventWithOriginCheck) { registerMockedHttpURLLoad("postmessage_test.html"); // Pass true to enable JavaScript. FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "postmessage_test.html", true); // Send a message with the correct origin. WebSecurityOrigin correctOrigin(WebSecurityOrigin::create(toKURL(m_baseURL))); WebDOMEvent event = webViewHelper.webView()->mainFrame()->document().createEvent("MessageEvent"); WebDOMMessageEvent message = event.to(); WebSerializedScriptValue data(WebSerializedScriptValue::fromString("foo")); message.initMessageEvent("message", false, false, data, "http://origin.com", 0, ""); webViewHelper.webView()->mainFrame()->dispatchMessageEventWithOriginCheck(correctOrigin, message); // Send another message with incorrect origin. WebSecurityOrigin incorrectOrigin(WebSecurityOrigin::create(toKURL(m_chromeURL))); webViewHelper.webView()->mainFrame()->dispatchMessageEventWithOriginCheck(incorrectOrigin, message); // Required to see any updates in contentAsText. webViewHelper.webView()->layout(); // Verify that only the first addition is in the body of the page. std::string content = webViewHelper.webView()->mainFrame()->contentAsText(1024).utf8(); EXPECT_NE(std::string::npos, content.find("Message 1.")); EXPECT_EQ(std::string::npos, content.find("Message 2.")); } TEST_F(WebFrameTest, PostMessageThenDetach) { FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad("about:blank"); RefPtr frame = toLocalFrame(webViewHelper.webViewImpl()->page()->mainFrame()); WebCore::NonThrowableExceptionState exceptionState; frame->domWindow()->postMessage(WebCore::SerializedScriptValue::create("message"), 0, "*", frame->domWindow(), exceptionState); webViewHelper.reset(); EXPECT_FALSE(exceptionState.hadException()); // Success is not crashing. runPendingTasks(); } class FixedLayoutTestWebViewClient : public FrameTestHelpers::TestWebViewClient { public: virtual WebScreenInfo screenInfo() OVERRIDE { return m_screenInfo; } WebScreenInfo m_screenInfo; }; // Viewport settings need to be set before the page gets loaded static void enableViewportSettings(WebSettings* settings) { settings->setViewportMetaEnabled(true); settings->setViewportEnabled(true); settings->setMainFrameResizesAreOrientationChanges(true); settings->setShrinksViewportContentToFit(true); } TEST_F(WebFrameTest, FrameViewNeedsLayoutOnFixedLayoutResize) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("fixed_layout.html"); FixedLayoutTestWebViewClient client; int viewportWidth = 640; int viewportHeight = 480; // Make sure we initialize to minimum scale, even if the window size // only becomes available after the load begins. FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "fixed_layout.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->layout(); webViewHelper.webViewImpl()->setFixedLayoutSize(WebCore::IntSize(100, 100)); EXPECT_TRUE(webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->needsLayout()); int prevLayoutCount = webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutCount(); webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->setFrameRect(WebCore::IntRect(0, 0, 641, 481)); EXPECT_EQ(prevLayoutCount, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutCount()); webViewHelper.webViewImpl()->layout(); } // Helper function to check or set text autosizing multipliers on a document. static bool checkOrSetTextAutosizingMultiplier(Document* document, float multiplier, bool setMultiplier) { bool multiplierCheckedOrSetAtLeastOnce = false; for (WebCore::RenderObject* renderer = document->renderView(); renderer; renderer = renderer->nextInPreOrder()) { if (renderer->style()) { if (setMultiplier) renderer->style()->setTextAutosizingMultiplier(multiplier); EXPECT_EQ(multiplier, renderer->style()->textAutosizingMultiplier()); multiplierCheckedOrSetAtLeastOnce = true; } } return multiplierCheckedOrSetAtLeastOnce; } static bool setTextAutosizingMultiplier(Document* document, float multiplier) { return checkOrSetTextAutosizingMultiplier(document, multiplier, true); } static bool checkTextAutosizingMultiplier(Document* document, float multiplier) { return checkOrSetTextAutosizingMultiplier(document, multiplier, false); } TEST_F(WebFrameTest, ChangeInFixedLayoutResetsTextAutosizingMultipliers) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("fixed_layout.html"); FixedLayoutTestWebViewClient client; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "fixed_layout.html", true, 0, &client, enableViewportSettings); WebCore::Document* document = toLocalFrame(webViewHelper.webViewImpl()->page()->mainFrame())->document(); document->settings()->setTextAutosizingEnabled(true); EXPECT_TRUE(document->settings()->textAutosizingEnabled()); webViewHelper.webViewImpl()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webViewImpl()->layout(); EXPECT_TRUE(setTextAutosizingMultiplier(document, 2)); WebCore::ViewportDescription description = document->viewportDescription(); // Choose a width that's not going match the viewport width of the loaded document. description.minWidth = WebCore::Length(100, WebCore::Fixed); description.maxWidth = WebCore::Length(100, WebCore::Fixed); webViewHelper.webViewImpl()->updatePageDefinedViewportConstraints(description); EXPECT_TRUE(checkTextAutosizingMultiplier(document, 1)); } TEST_F(WebFrameTest, SetFrameRectInvalidatesTextAutosizingMultipliers) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("iframe_reload.html"); registerMockedHttpURLLoad("visible_iframe.html"); FixedLayoutTestWebViewClient client; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "iframe_reload.html", true, 0, &client, enableViewportSettings); WebCore::LocalFrame* mainFrame = toLocalFrame(webViewHelper.webViewImpl()->page()->mainFrame()); WebCore::Document* document = mainFrame->document(); WebCore::FrameView* frameView = webViewHelper.webViewImpl()->mainFrameImpl()->frameView(); document->settings()->setTextAutosizingEnabled(true); EXPECT_TRUE(document->settings()->textAutosizingEnabled()); webViewHelper.webViewImpl()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webViewImpl()->layout(); for (WebCore::Frame* frame = mainFrame; frame; frame = frame->tree().traverseNext()) { if (!frame->isLocalFrame()) continue; EXPECT_TRUE(setTextAutosizingMultiplier(toLocalFrame(frame)->document(), 2)); for (WebCore::RenderObject* renderer = toLocalFrame(frame)->document()->renderView(); renderer; renderer = renderer->nextInPreOrder()) { if (renderer->isText()) EXPECT_FALSE(renderer->needsLayout()); } } frameView->setFrameRect(WebCore::IntRect(0, 0, 200, 200)); for (WebCore::Frame* frame = mainFrame; frame; frame = frame->tree().traverseNext()) { if (!frame->isLocalFrame()) continue; for (WebCore::RenderObject* renderer = toLocalFrame(frame)->document()->renderView(); renderer; renderer = renderer->nextInPreOrder()) { if (renderer->isText()) EXPECT_TRUE(renderer->needsLayout()); } } } TEST_F(WebFrameTest, FixedLayoutSizeStopsResizeFromChangingLayoutSize) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("fixed_layout.html"); int viewportWidth = 640; int viewportHeight = 480; int fixedLayoutWidth = viewportWidth / 2; int fixedLayoutHeight = viewportHeight / 2; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "fixed_layout.html", true, 0, 0, enableViewportSettings); webViewHelper.webView()->setFixedLayoutSize(WebSize(fixedLayoutWidth, fixedLayoutHeight)); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->layout(); EXPECT_EQ(fixedLayoutWidth, toLocalFrame(webViewHelper.webViewImpl()->page()->mainFrame())->view()->layoutSize().width()); EXPECT_EQ(fixedLayoutHeight, toLocalFrame(webViewHelper.webViewImpl()->page()->mainFrame())->view()->layoutSize().height()); } TEST_F(WebFrameTest, FixedLayoutSizePreventsResizeFromChangingPageScale) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("fixed_layout.html"); int viewportWidth = 640; int viewportHeight = 480; int fixedLayoutWidth = viewportWidth / 2; int fixedLayoutHeight = viewportHeight / 2; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "fixed_layout.html", true, 0, 0, enableViewportSettings); webViewHelper.webView()->setFixedLayoutSize(WebSize(fixedLayoutWidth, fixedLayoutHeight)); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->layout(); float pageScaleFactor = webViewHelper.webView()->pageScaleFactor(); webViewHelper.webView()->resize(WebSize(viewportWidth * 2, viewportHeight * 2)); EXPECT_EQ(pageScaleFactor, webViewHelper.webView()->pageScaleFactor()); } TEST_F(WebFrameTest, FixedLayoutSizePreventsLayoutFromChangingPageScale) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("fixed_layout.html"); int viewportWidth = 640; int viewportHeight = 480; int fixedLayoutWidth = viewportWidth * 2; int fixedLayoutHeight = viewportHeight * 2; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "fixed_layout.html", true, 0, 0, enableViewportSettings); webViewHelper.webView()->setFixedLayoutSize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->layout(); float pageScaleFactor = webViewHelper.webView()->pageScaleFactor(); webViewHelper.webView()->setFixedLayoutSize(WebSize(fixedLayoutWidth, fixedLayoutHeight)); webViewHelper.webView()->layout(); EXPECT_EQ(pageScaleFactor, webViewHelper.webView()->pageScaleFactor()); } TEST_F(WebFrameTest, PreferredSizeAndContentSizeReportedCorrectlyWithZeroHeightFixedLayout) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("200-by-300.html"); int windowWidth = 100; int windowHeight = 100; int viewportWidth = 100; int viewportHeight = 0; int divWidth = 200; int divHeight = 300; FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "200-by-300.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->resize(WebSize(windowWidth, windowHeight)); webViewHelper.webView()->setFixedLayoutSize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->layout(); EXPECT_EQ(divWidth, webViewHelper.webView()->mainFrame()->contentsSize().width); EXPECT_EQ(divHeight, webViewHelper.webView()->mainFrame()->contentsSize().height); EXPECT_EQ(divWidth, webViewHelper.webView()->contentsPreferredMinimumSize().width); EXPECT_EQ(divHeight, webViewHelper.webView()->contentsPreferredMinimumSize().height); } TEST_F(WebFrameTest, DisablingFixedLayoutSizeSetsCorrectLayoutSize) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("no_viewport_tag.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "no_viewport_tag.html", true, 0, &client, enableViewportSettings); applyViewportStyleOverride(&webViewHelper); webViewHelper.webView()->settings()->setSupportDeprecatedTargetDensityDPI(true); webViewHelper.webView()->settings()->setUseWideViewport(true); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->setFixedLayoutSize(WebSize(viewportWidth, viewportHeight)); EXPECT_TRUE(webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->needsLayout()); webViewHelper.webView()->layout(); EXPECT_EQ(viewportWidth, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->contentsSize().width()); webViewHelper.webView()->setFixedLayoutSize(WebSize(0, 0)); EXPECT_TRUE(webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->needsLayout()); webViewHelper.webView()->layout(); EXPECT_EQ(980, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->contentsSize().width()); } TEST_F(WebFrameTest, ZeroHeightPositiveWidthNotIgnored) { UseMockScrollbarSettings mockScrollbarSettings; FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 1280; int viewportHeight = 0; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initialize(true, 0, &client, enableViewportSettings); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); EXPECT_EQ(viewportWidth, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width()); EXPECT_EQ(viewportHeight, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height()); } TEST_F(WebFrameTest, DeviceScaleFactorUsesDefaultWithoutViewportTag) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("no_viewport_tag.html"); int viewportWidth = 640; int viewportHeight = 480; FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 2; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "no_viewport_tag.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->layout(); EXPECT_EQ(2, webViewHelper.webView()->deviceScaleFactor()); // Device scale factor should be independent of page scale. webViewHelper.webView()->setPageScaleFactorLimits(1, 2); webViewHelper.webView()->setPageScaleFactor(0.5); webViewHelper.webView()->layout(); EXPECT_EQ(1, webViewHelper.webView()->pageScaleFactor()); // Force the layout to happen before leaving the test. webViewHelper.webView()->mainFrame()->contentAsText(1024).utf8(); } TEST_F(WebFrameTest, FixedLayoutInitializeAtMinimumScale) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("fixed_layout.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; // Make sure we initialize to minimum scale, even if the window size // only becomes available after the load begins. FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "fixed_layout.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); int defaultFixedLayoutWidth = 980; float minimumPageScaleFactor = viewportWidth / (float) defaultFixedLayoutWidth; EXPECT_EQ(minimumPageScaleFactor, webViewHelper.webView()->pageScaleFactor()); EXPECT_EQ(minimumPageScaleFactor, webViewHelper.webView()->minimumPageScaleFactor()); // Assume the user has pinch zoomed to page scale factor 2. float userPinchPageScaleFactor = 2; webViewHelper.webView()->setPageScaleFactor(userPinchPageScaleFactor); webViewHelper.webView()->layout(); // Make sure we don't reset to initial scale if the page continues to load. webViewHelper.webViewImpl()->didCommitLoad(false, false); webViewHelper.webViewImpl()->didChangeContentsSize(); EXPECT_EQ(userPinchPageScaleFactor, webViewHelper.webView()->pageScaleFactor()); // Make sure we don't reset to initial scale if the viewport size changes. webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight + 100)); EXPECT_EQ(userPinchPageScaleFactor, webViewHelper.webView()->pageScaleFactor()); } TEST_F(WebFrameTest, WideDocumentInitializeAtMinimumScale) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("wide_document.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; // Make sure we initialize to minimum scale, even if the window size // only becomes available after the load begins. FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "wide_document.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); int wideDocumentWidth = 1500; float minimumPageScaleFactor = viewportWidth / (float) wideDocumentWidth; EXPECT_EQ(minimumPageScaleFactor, webViewHelper.webView()->pageScaleFactor()); EXPECT_EQ(minimumPageScaleFactor, webViewHelper.webView()->minimumPageScaleFactor()); // Assume the user has pinch zoomed to page scale factor 2. float userPinchPageScaleFactor = 2; webViewHelper.webView()->setPageScaleFactor(userPinchPageScaleFactor); webViewHelper.webView()->layout(); // Make sure we don't reset to initial scale if the page continues to load. webViewHelper.webViewImpl()->didCommitLoad(false, false); webViewHelper.webViewImpl()->didChangeContentsSize(); EXPECT_EQ(userPinchPageScaleFactor, webViewHelper.webView()->pageScaleFactor()); // Make sure we don't reset to initial scale if the viewport size changes. webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight + 100)); EXPECT_EQ(userPinchPageScaleFactor, webViewHelper.webView()->pageScaleFactor()); } TEST_F(WebFrameTest, DelayedViewportInitialScale) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("viewport-auto-initial-scale.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "viewport-auto-initial-scale.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); EXPECT_EQ(0.25f, webViewHelper.webView()->pageScaleFactor()); WebCore::Document* document = toLocalFrame(webViewHelper.webViewImpl()->page()->mainFrame())->document(); WebCore::ViewportDescription description = document->viewportDescription(); description.zoom = 2; document->setViewportDescription(description); webViewHelper.webView()->layout(); EXPECT_EQ(2, webViewHelper.webView()->pageScaleFactor()); } TEST_F(WebFrameTest, setLoadWithOverviewModeToFalse) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("viewport-auto-initial-scale.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "viewport-auto-initial-scale.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true); webViewHelper.webView()->settings()->setLoadWithOverviewMode(false); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); // The page must be displayed at 100% zoom. EXPECT_EQ(1.0f, webViewHelper.webView()->pageScaleFactor()); } TEST_F(WebFrameTest, SetLoadWithOverviewModeToFalseAndNoWideViewport) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("large-div.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "large-div.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->settings()->setLoadWithOverviewMode(false); webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true); webViewHelper.webView()->settings()->setUseWideViewport(false); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); // The page must be displayed at 100% zoom, despite that it hosts a wide div element. EXPECT_EQ(1.0f, webViewHelper.webView()->pageScaleFactor()); } TEST_F(WebFrameTest, NoWideViewportIgnoresPageViewportWidth) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("viewport-auto-initial-scale.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "viewport-auto-initial-scale.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true); webViewHelper.webView()->settings()->setUseWideViewport(false); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); // The page sets viewport width to 3000, but with UseWideViewport == false is must be ignored. EXPECT_EQ(viewportWidth, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->contentsSize().width()); EXPECT_EQ(viewportHeight, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->contentsSize().height()); } TEST_F(WebFrameTest, NoWideViewportIgnoresPageViewportWidthButAccountsScale) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("viewport-wide-2x-initial-scale.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "viewport-wide-2x-initial-scale.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true); webViewHelper.webView()->settings()->setUseWideViewport(false); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); // The page sets viewport width to 3000, but with UseWideViewport == false it must be ignored. // While the initial scale specified by the page must be accounted. EXPECT_EQ(viewportWidth / 2, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->contentsSize().width()); EXPECT_EQ(viewportHeight / 2, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->contentsSize().height()); } TEST_F(WebFrameTest, WideViewportSetsTo980WithoutViewportTag) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("no_viewport_tag.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "no_viewport_tag.html", true, 0, &client, enableViewportSettings); applyViewportStyleOverride(&webViewHelper); webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true); webViewHelper.webView()->settings()->setUseWideViewport(true); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); EXPECT_EQ(980, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->contentsSize().width()); EXPECT_EQ(980.0 / viewportWidth * viewportHeight, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->contentsSize().height()); } TEST_F(WebFrameTest, NoWideViewportAndHeightInMeta) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("viewport-height-1000.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "viewport-height-1000.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true); webViewHelper.webView()->settings()->setUseWideViewport(false); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); EXPECT_EQ(viewportWidth, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->contentsSize().width()); } TEST_F(WebFrameTest, WideViewportSetsTo980WithAutoWidth) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("viewport-2x-initial-scale.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "viewport-2x-initial-scale.html", true, 0, &client, enableViewportSettings); applyViewportStyleOverride(&webViewHelper); webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true); webViewHelper.webView()->settings()->setUseWideViewport(true); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); EXPECT_EQ(980, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->contentsSize().width()); EXPECT_EQ(980.0 / viewportWidth * viewportHeight, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->contentsSize().height()); } TEST_F(WebFrameTest, PageViewportInitialScaleOverridesLoadWithOverviewMode) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("viewport-wide-2x-initial-scale.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "viewport-wide-2x-initial-scale.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->settings()->setLoadWithOverviewMode(false); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); // The page must be displayed at 200% zoom, as specified in its viewport meta tag. EXPECT_EQ(2.0f, webViewHelper.webView()->pageScaleFactor()); } TEST_F(WebFrameTest, setInitialPageScaleFactorPermanently) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("fixed_layout.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; float enforcedPageScaleFactor = 2.0f; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "fixed_layout.html", true, 0, &client, enableViewportSettings); applyViewportStyleOverride(&webViewHelper); webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true); webViewHelper.webView()->settings()->setLoadWithOverviewMode(false); webViewHelper.webView()->setInitialPageScaleOverride(enforcedPageScaleFactor); webViewHelper.webView()->layout(); EXPECT_EQ(enforcedPageScaleFactor, webViewHelper.webView()->pageScaleFactor()); int viewportWidth = 640; int viewportHeight = 480; webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->layout(); EXPECT_EQ(enforcedPageScaleFactor, webViewHelper.webView()->pageScaleFactor()); webViewHelper.webView()->setInitialPageScaleOverride(-1); webViewHelper.webView()->layout(); EXPECT_EQ(1.0, webViewHelper.webView()->pageScaleFactor()); } TEST_F(WebFrameTest, PermanentInitialPageScaleFactorOverridesLoadWithOverviewMode) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("viewport-auto-initial-scale.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; float enforcedPageScaleFactor = 0.5f; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "viewport-auto-initial-scale.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->settings()->setLoadWithOverviewMode(false); webViewHelper.webView()->setInitialPageScaleOverride(enforcedPageScaleFactor); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); EXPECT_EQ(enforcedPageScaleFactor, webViewHelper.webView()->pageScaleFactor()); } TEST_F(WebFrameTest, PermanentInitialPageScaleFactorOverridesPageViewportInitialScale) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("viewport-wide-2x-initial-scale.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; float enforcedPageScaleFactor = 0.5f; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "viewport-wide-2x-initial-scale.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->setInitialPageScaleOverride(enforcedPageScaleFactor); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); EXPECT_EQ(enforcedPageScaleFactor, webViewHelper.webView()->pageScaleFactor()); } TEST_F(WebFrameTest, SmallPermanentInitialPageScaleFactorIsClobbered) { UseMockScrollbarSettings mockScrollbarSettings; const char* pages[] = { // These pages trigger the clobbering condition. There must be a matching item in "pageScaleFactors" array. "viewport-device-0.5x-initial-scale.html", "viewport-initial-scale-1.html", // These ones do not. "viewport-auto-initial-scale.html", "viewport-target-densitydpi-device-and-fixed-width.html" }; float pageScaleFactors[] = { 0.5f, 1.0f }; for (size_t i = 0; i < ARRAY_SIZE(pages); ++i) registerMockedHttpURLLoad(pages[i]); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 400; int viewportHeight = 300; float enforcedPageScaleFactor = 0.75f; for (size_t i = 0; i < ARRAY_SIZE(pages); ++i) { for (int quirkEnabled = 0; quirkEnabled <= 1; ++quirkEnabled) { FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + pages[i], true, 0, &client, enableViewportSettings); applyViewportStyleOverride(&webViewHelper); webViewHelper.webView()->settings()->setClobberUserAgentInitialScaleQuirk(quirkEnabled); webViewHelper.webView()->setInitialPageScaleOverride(enforcedPageScaleFactor); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); float expectedPageScaleFactor = quirkEnabled && i < ARRAY_SIZE(pageScaleFactors) ? pageScaleFactors[i] : enforcedPageScaleFactor; EXPECT_EQ(expectedPageScaleFactor, webViewHelper.webView()->pageScaleFactor()); } } } TEST_F(WebFrameTest, PermanentInitialPageScaleFactorAffectsLayoutWidth) { UseMockScrollbarSettings mockScrollbarSettings; FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; float enforcedPageScaleFactor = 0.5; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad("about:blank", true, 0, &client, enableViewportSettings); webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true); webViewHelper.webView()->settings()->setUseWideViewport(false); webViewHelper.webView()->settings()->setLoadWithOverviewMode(false); webViewHelper.webView()->setInitialPageScaleOverride(enforcedPageScaleFactor); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); EXPECT_EQ(viewportWidth / enforcedPageScaleFactor, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->contentsSize().width()); EXPECT_EQ(enforcedPageScaleFactor, webViewHelper.webView()->pageScaleFactor()); } TEST_F(WebFrameTest, WideViewportInitialScaleDoesNotExpandFixedLayoutWidth) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("viewport-device-0.5x-initial-scale.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "viewport-device-0.5x-initial-scale.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true); webViewHelper.webView()->settings()->setUseWideViewport(true); webViewHelper.webView()->settings()->setViewportMetaLayoutSizeQuirk(true); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); EXPECT_EQ(viewportWidth, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width()); EXPECT_EQ(1, webViewHelper.webView()->pageScaleFactor()); webViewHelper.webView()->setFixedLayoutSize(WebSize(2000, 1500)); webViewHelper.webView()->layout(); EXPECT_EQ(2000, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width()); EXPECT_EQ(0.5f, webViewHelper.webView()->pageScaleFactor()); } TEST_F(WebFrameTest, SetForceZeroLayoutHeight) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("200-by-300.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "200-by-300.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->layout(); EXPECT_LE(viewportHeight, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height()); webViewHelper.webView()->settings()->setForceZeroLayoutHeight(true); EXPECT_TRUE(webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->needsLayout()); EXPECT_EQ(0, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height()); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight * 2)); EXPECT_FALSE(webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->needsLayout()); EXPECT_EQ(0, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height()); webViewHelper.webView()->resize(WebSize(viewportWidth * 2, viewportHeight)); webViewHelper.webView()->layout(); EXPECT_EQ(0, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height()); webViewHelper.webView()->settings()->setForceZeroLayoutHeight(false); EXPECT_LE(viewportHeight, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height()); } TEST_F(WebFrameTest, SetForceZeroLayoutHeightWorksAcrossNavigations) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("200-by-300.html"); registerMockedHttpURLLoad("large-div.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "200-by-300.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->settings()->setForceZeroLayoutHeight(true); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->layout(); FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), m_baseURL + "large-div.html"); webViewHelper.webView()->layout(); EXPECT_EQ(0, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height()); } TEST_F(WebFrameTest, SetForceZeroLayoutHeightWithWideViewportQuirk) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("200-by-300.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "200-by-300.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true); webViewHelper.webView()->settings()->setUseWideViewport(true); webViewHelper.webView()->settings()->setForceZeroLayoutHeight(true); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->layout(); EXPECT_EQ(0, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height()); } TEST_F(WebFrameTest, WideViewportAndWideContentWithInitialScale) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("wide_document_width_viewport.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 600; int viewportHeight = 800; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad("about:blank", true, 0, &client, enableViewportSettings); webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true); webViewHelper.webView()->settings()->setUseWideViewport(true); webViewHelper.webView()->settings()->setViewportMetaLayoutSizeQuirk(true); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), m_baseURL + "wide_document_width_viewport.html"); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); int wideDocumentWidth = 800; float minimumPageScaleFactor = viewportWidth / (float) wideDocumentWidth; EXPECT_EQ(minimumPageScaleFactor, webViewHelper.webView()->pageScaleFactor()); EXPECT_EQ(minimumPageScaleFactor, webViewHelper.webView()->minimumPageScaleFactor()); } TEST_F(WebFrameTest, WideViewportQuirkClobbersHeight) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("viewport-height-1000.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 600; int viewportHeight = 800; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad("about:blank", true, 0, &client, enableViewportSettings); webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true); webViewHelper.webView()->settings()->setUseWideViewport(false); webViewHelper.webView()->settings()->setViewportMetaLayoutSizeQuirk(true); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), m_baseURL + "viewport-height-1000.html"); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); EXPECT_EQ(800, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height()); EXPECT_EQ(1, webViewHelper.webView()->pageScaleFactor()); } TEST_F(WebFrameTest, LayoutSize320Quirk) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("viewport/viewport-30.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 600; int viewportHeight = 800; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad("about:blank", true, 0, &client, enableViewportSettings); webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true); webViewHelper.webView()->settings()->setUseWideViewport(true); webViewHelper.webView()->settings()->setViewportMetaLayoutSizeQuirk(true); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), m_baseURL + "viewport/viewport-30.html"); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); EXPECT_EQ(600, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width()); EXPECT_EQ(800, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height()); EXPECT_EQ(1, webViewHelper.webView()->pageScaleFactor()); // The magic number to snap to device-width is 320, so test that 321 is // respected. WebCore::Document* document = toLocalFrame(webViewHelper.webViewImpl()->page()->mainFrame())->document(); WebCore::ViewportDescription description = document->viewportDescription(); description.minWidth = WebCore::Length(321, WebCore::Fixed); description.maxWidth = WebCore::Length(321, WebCore::Fixed); document->setViewportDescription(description); webViewHelper.webView()->layout(); EXPECT_EQ(321, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width()); description.minWidth = WebCore::Length(320, WebCore::Fixed); description.maxWidth = WebCore::Length(320, WebCore::Fixed); document->setViewportDescription(description); webViewHelper.webView()->layout(); EXPECT_EQ(600, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width()); description = document->viewportDescription(); description.maxHeight = WebCore::Length(1000, WebCore::Fixed); document->setViewportDescription(description); webViewHelper.webView()->layout(); EXPECT_EQ(1000, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height()); description.maxHeight = WebCore::Length(320, WebCore::Fixed); document->setViewportDescription(description); webViewHelper.webView()->layout(); EXPECT_EQ(800, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height()); } TEST_F(WebFrameTest, ZeroValuesQuirk) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("viewport-zero-values.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initialize(true, 0, &client, enableViewportSettings); webViewHelper.webView()->settings()->setViewportMetaZeroValuesQuirk(true); webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true); webViewHelper.webView()->settings()->setViewportMetaLayoutSizeQuirk(true); FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), m_baseURL + "viewport-zero-values.html"); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); EXPECT_EQ(viewportWidth, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width()); EXPECT_EQ(1.0f, webViewHelper.webView()->pageScaleFactor()); webViewHelper.webView()->settings()->setUseWideViewport(true); webViewHelper.webView()->layout(); EXPECT_EQ(viewportWidth, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width()); EXPECT_EQ(1.0f, webViewHelper.webView()->pageScaleFactor()); } TEST_F(WebFrameTest, OverflowHiddenDisablesScrolling) { registerMockedHttpURLLoad("body-overflow-hidden.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initialize(true, 0, &client); FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), m_baseURL + "body-overflow-hidden.html"); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); WebCore::FrameView* view = webViewHelper.webViewImpl()->mainFrameImpl()->frameView(); EXPECT_FALSE(view->userInputScrollable(WebCore::VerticalScrollbar)); } TEST_F(WebFrameTest, IgnoreOverflowHiddenQuirk) { registerMockedHttpURLLoad("body-overflow-hidden.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initialize(true, 0, &client); webViewHelper.webView()->settings()->setIgnoreMainFrameOverflowHiddenQuirk(true); FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), m_baseURL + "body-overflow-hidden.html"); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); WebCore::FrameView* view = webViewHelper.webViewImpl()->mainFrameImpl()->frameView(); EXPECT_TRUE(view->userInputScrollable(WebCore::VerticalScrollbar)); } TEST_F(WebFrameTest, NonZeroValuesNoQuirk) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("viewport-nonzero-values.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; float expectedPageScaleFactor = 0.5f; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initialize(true, 0, &client, enableViewportSettings); webViewHelper.webView()->settings()->setViewportMetaZeroValuesQuirk(true); webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true); FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), m_baseURL + "viewport-nonzero-values.html"); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); EXPECT_EQ(viewportWidth / expectedPageScaleFactor, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width()); EXPECT_EQ(expectedPageScaleFactor, webViewHelper.webView()->pageScaleFactor()); webViewHelper.webView()->settings()->setUseWideViewport(true); webViewHelper.webView()->layout(); EXPECT_EQ(viewportWidth / expectedPageScaleFactor, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width()); EXPECT_EQ(expectedPageScaleFactor, webViewHelper.webView()->pageScaleFactor()); } TEST_F(WebFrameTest, setPageScaleFactorDoesNotLayout) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("fixed_layout.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; // Small viewport to ensure there are always scrollbars. int viewportWidth = 64; int viewportHeight = 48; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "fixed_layout.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->layout(); int prevLayoutCount = webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutCount(); webViewHelper.webViewImpl()->setPageScaleFactor(3); EXPECT_FALSE(webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->needsLayout()); EXPECT_EQ(prevLayoutCount, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutCount()); } TEST_F(WebFrameTest, setPageScaleFactorWithOverlayScrollbarsDoesNotLayout) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("fixed_layout.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "fixed_layout.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->layout(); int prevLayoutCount = webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutCount(); webViewHelper.webViewImpl()->setPageScaleFactor(30); EXPECT_FALSE(webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->needsLayout()); EXPECT_EQ(prevLayoutCount, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutCount()); } TEST_F(WebFrameTest, pageScaleFactorWrittenToHistoryItem) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("fixed_layout.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "fixed_layout.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->layout(); webViewHelper.webView()->setPageScaleFactor(3); EXPECT_EQ(3, toLocalFrame(webViewHelper.webViewImpl()->page()->mainFrame())->loader().currentItem()->pageScaleFactor()); } TEST_F(WebFrameTest, initialScaleWrittenToHistoryItem) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("fixed_layout.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "fixed_layout.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->layout(); int defaultFixedLayoutWidth = 980; float minimumPageScaleFactor = viewportWidth / (float) defaultFixedLayoutWidth; EXPECT_EQ(minimumPageScaleFactor, toLocalFrame(webViewHelper.webViewImpl()->page()->mainFrame())->loader().currentItem()->pageScaleFactor()); } TEST_F(WebFrameTest, pageScaleFactorShrinksViewport) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("large-div.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; // Small viewport to ensure there are always scrollbars. int viewportWidth = 64; int viewportHeight = 48; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "large-div.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->layout(); WebCore::FrameView* view = webViewHelper.webViewImpl()->mainFrameImpl()->frameView(); int viewportWidthMinusScrollbar = viewportWidth - (view->verticalScrollbar()->isOverlayScrollbar() ? 0 : 15); int viewportHeightMinusScrollbar = viewportHeight - (view->horizontalScrollbar()->isOverlayScrollbar() ? 0 : 15); webViewHelper.webView()->setPageScaleFactor(2); WebCore::IntSize unscaledSize = view->unscaledVisibleContentSize(WebCore::IncludeScrollbars); EXPECT_EQ(viewportWidth, unscaledSize.width()); EXPECT_EQ(viewportHeight, unscaledSize.height()); WebCore::IntSize unscaledSizeMinusScrollbar = view->unscaledVisibleContentSize(WebCore::ExcludeScrollbars); EXPECT_EQ(viewportWidthMinusScrollbar, unscaledSizeMinusScrollbar.width()); EXPECT_EQ(viewportHeightMinusScrollbar, unscaledSizeMinusScrollbar.height()); WebCore::IntSize scaledSize = view->visibleContentRect().size(); EXPECT_EQ(ceil(viewportWidthMinusScrollbar / 2.0), scaledSize.width()); EXPECT_EQ(ceil(viewportHeightMinusScrollbar / 2.0), scaledSize.height()); } TEST_F(WebFrameTest, pageScaleFactorDoesNotApplyCssTransform) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("fixed_layout.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "fixed_layout.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->layout(); webViewHelper.webView()->setPageScaleFactor(2); EXPECT_EQ(980, toLocalFrame(webViewHelper.webViewImpl()->page()->mainFrame())->contentRenderer()->unscaledDocumentRect().width()); EXPECT_EQ(980, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->contentsSize().width()); } TEST_F(WebFrameTest, targetDensityDpiHigh) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("viewport-target-densitydpi-high.html"); FixedLayoutTestWebViewClient client; // high-dpi = 240 float targetDpi = 240.0f; float deviceScaleFactors[] = { 1.0f, 4.0f / 3.0f, 2.0f }; int viewportWidth = 640; int viewportHeight = 480; for (size_t i = 0; i < ARRAY_SIZE(deviceScaleFactors); ++i) { float deviceScaleFactor = deviceScaleFactors[i]; float deviceDpi = deviceScaleFactor * 160.0f; client.m_screenInfo.deviceScaleFactor = deviceScaleFactor; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "viewport-target-densitydpi-high.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true); webViewHelper.webView()->settings()->setSupportDeprecatedTargetDensityDPI(true); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); // We need to account for the fact that logical pixels are unconditionally multiplied by deviceScaleFactor to produce // physical pixels. float densityDpiScaleRatio = deviceScaleFactor * targetDpi / deviceDpi; EXPECT_NEAR(viewportWidth * densityDpiScaleRatio, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width(), 1.0f); EXPECT_NEAR(viewportHeight * densityDpiScaleRatio, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height(), 1.0f); EXPECT_NEAR(1.0f / densityDpiScaleRatio, webViewHelper.webView()->pageScaleFactor(), 0.01f); } } TEST_F(WebFrameTest, targetDensityDpiDevice) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("viewport-target-densitydpi-device.html"); float deviceScaleFactors[] = { 1.0f, 4.0f / 3.0f, 2.0f }; FixedLayoutTestWebViewClient client; int viewportWidth = 640; int viewportHeight = 480; for (size_t i = 0; i < ARRAY_SIZE(deviceScaleFactors); ++i) { client.m_screenInfo.deviceScaleFactor = deviceScaleFactors[i]; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "viewport-target-densitydpi-device.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true); webViewHelper.webView()->settings()->setSupportDeprecatedTargetDensityDPI(true); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); EXPECT_NEAR(viewportWidth * client.m_screenInfo.deviceScaleFactor, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width(), 1.0f); EXPECT_NEAR(viewportHeight * client.m_screenInfo.deviceScaleFactor, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height(), 1.0f); EXPECT_NEAR(1.0f / client.m_screenInfo.deviceScaleFactor, webViewHelper.webView()->pageScaleFactor(), 0.01f); } } TEST_F(WebFrameTest, targetDensityDpiDeviceAndFixedWidth) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("viewport-target-densitydpi-device-and-fixed-width.html"); float deviceScaleFactors[] = { 1.0f, 4.0f / 3.0f, 2.0f }; FixedLayoutTestWebViewClient client; int viewportWidth = 640; int viewportHeight = 480; for (size_t i = 0; i < ARRAY_SIZE(deviceScaleFactors); ++i) { client.m_screenInfo.deviceScaleFactor = deviceScaleFactors[i]; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "viewport-target-densitydpi-device-and-fixed-width.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true); webViewHelper.webView()->settings()->setSupportDeprecatedTargetDensityDPI(true); webViewHelper.webView()->settings()->setUseWideViewport(true); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); EXPECT_NEAR(viewportWidth, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width(), 1.0f); EXPECT_NEAR(viewportHeight, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height(), 1.0f); EXPECT_NEAR(1.0f, webViewHelper.webView()->pageScaleFactor(), 0.01f); } } TEST_F(WebFrameTest, NoWideViewportAndScaleLessThanOne) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("viewport-initial-scale-less-than-1.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1.33f; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "viewport-initial-scale-less-than-1.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->settings()->setSupportDeprecatedTargetDensityDPI(true); webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true); webViewHelper.webView()->settings()->setUseWideViewport(false); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->layout(); EXPECT_NEAR(viewportWidth * client.m_screenInfo.deviceScaleFactor, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width(), 1.0f); EXPECT_NEAR(viewportHeight * client.m_screenInfo.deviceScaleFactor, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height(), 1.0f); EXPECT_NEAR(1.0f / client.m_screenInfo.deviceScaleFactor, webViewHelper.webView()->pageScaleFactor(), 0.01f); } TEST_F(WebFrameTest, NoWideViewportAndScaleLessThanOneWithDeviceWidth) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("viewport-initial-scale-less-than-1-device-width.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1.33f; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "viewport-initial-scale-less-than-1-device-width.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->settings()->setSupportDeprecatedTargetDensityDPI(true); webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true); webViewHelper.webView()->settings()->setUseWideViewport(false); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->layout(); const float pageZoom = 0.25f; EXPECT_NEAR(viewportWidth * client.m_screenInfo.deviceScaleFactor / pageZoom, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width(), 1.0f); EXPECT_NEAR(viewportHeight * client.m_screenInfo.deviceScaleFactor / pageZoom, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height(), 1.0f); EXPECT_NEAR(1.0f / client.m_screenInfo.deviceScaleFactor, webViewHelper.webView()->pageScaleFactor(), 0.01f); } TEST_F(WebFrameTest, NoWideViewportAndNoViewportWithInitialPageScaleOverride) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("large-div.html"); FixedLayoutTestWebViewClient client; int viewportWidth = 640; int viewportHeight = 480; float enforcedPageScaleFactor = 5.0f; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "large-div.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true); webViewHelper.webView()->settings()->setUseWideViewport(false); webViewHelper.webView()->setInitialPageScaleOverride(enforcedPageScaleFactor); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->layout(); EXPECT_NEAR(viewportWidth / enforcedPageScaleFactor, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width(), 1.0f); EXPECT_NEAR(viewportHeight / enforcedPageScaleFactor, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height(), 1.0f); EXPECT_NEAR(enforcedPageScaleFactor, webViewHelper.webView()->pageScaleFactor(), 0.01f); } TEST_F(WebFrameTest, NoUserScalableQuirkIgnoresViewportScale) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("viewport-initial-scale-and-user-scalable-no.html"); FixedLayoutTestWebViewClient client; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "viewport-initial-scale-and-user-scalable-no.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->settings()->setViewportMetaNonUserScalableQuirk(true); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->layout(); EXPECT_NEAR(viewportWidth, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width(), 1.0f); EXPECT_NEAR(viewportHeight, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height(), 1.0f); EXPECT_NEAR(1.0f, webViewHelper.webView()->pageScaleFactor(), 0.01f); } TEST_F(WebFrameTest, NoUserScalableQuirkIgnoresViewportScaleForNonWideViewport) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("viewport-initial-scale-and-user-scalable-no.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1.33f; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "viewport-initial-scale-and-user-scalable-no.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->settings()->setSupportDeprecatedTargetDensityDPI(true); webViewHelper.webView()->settings()->setViewportMetaNonUserScalableQuirk(true); webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true); webViewHelper.webView()->settings()->setUseWideViewport(false); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->layout(); EXPECT_NEAR(viewportWidth * client.m_screenInfo.deviceScaleFactor, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width(), 1.0f); EXPECT_NEAR(viewportHeight * client.m_screenInfo.deviceScaleFactor, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height(), 1.0f); EXPECT_NEAR(1.0f / client.m_screenInfo.deviceScaleFactor, webViewHelper.webView()->pageScaleFactor(), 0.01f); } TEST_F(WebFrameTest, NoUserScalableQuirkIgnoresViewportScaleForWideViewport) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("viewport-2x-initial-scale-non-user-scalable.html"); FixedLayoutTestWebViewClient client; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "viewport-2x-initial-scale-non-user-scalable.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->settings()->setViewportMetaNonUserScalableQuirk(true); webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true); webViewHelper.webView()->settings()->setUseWideViewport(true); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); EXPECT_NEAR(viewportWidth, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width(), 1.0f); EXPECT_NEAR(viewportHeight, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height(), 1.0f); EXPECT_NEAR(1.0f, webViewHelper.webView()->pageScaleFactor(), 0.01f); } TEST_F(WebFrameTest, DesktopPageCanBeZoomedInWhenWideViewportIsTurnedOff) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("no_viewport_tag.html"); FixedLayoutTestWebViewClient client; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "no_viewport_tag.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true); webViewHelper.webView()->settings()->setUseWideViewport(false); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); EXPECT_NEAR(1.0f, webViewHelper.webView()->pageScaleFactor(), 0.01f); EXPECT_NEAR(1.0f, webViewHelper.webView()->minimumPageScaleFactor(), 0.01f); EXPECT_NEAR(5.0f, webViewHelper.webView()->maximumPageScaleFactor(), 0.01f); } class WebFrameResizeTest : public WebFrameTest { protected: static WebCore::FloatSize computeRelativeOffset(const WebCore::IntPoint& absoluteOffset, const WebCore::LayoutRect& rect) { WebCore::FloatSize relativeOffset = WebCore::FloatPoint(absoluteOffset) - rect.location(); relativeOffset.scale(1.f / rect.width(), 1.f / rect.height()); return relativeOffset; } void testResizeYieldsCorrectScrollAndScale(const char* url, const float initialPageScaleFactor, const WebSize scrollOffset, const WebSize viewportSize, const bool shouldScaleRelativeToViewportWidth) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad(url); const float aspectRatio = static_cast(viewportSize.width) / viewportSize.height; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + url, true, 0, 0, enableViewportSettings); // Origin scrollOffsets preserved under resize. { webViewHelper.webViewImpl()->resize(WebSize(viewportSize.width, viewportSize.height)); webViewHelper.webViewImpl()->setPageScaleFactor(initialPageScaleFactor); ASSERT_EQ(viewportSize, webViewHelper.webViewImpl()->size()); ASSERT_EQ(initialPageScaleFactor, webViewHelper.webViewImpl()->pageScaleFactor()); webViewHelper.webViewImpl()->resize(WebSize(viewportSize.height, viewportSize.width)); float expectedPageScaleFactor = initialPageScaleFactor * (shouldScaleRelativeToViewportWidth ? 1 / aspectRatio : 1); EXPECT_NEAR(expectedPageScaleFactor, webViewHelper.webViewImpl()->pageScaleFactor(), 0.05f); EXPECT_EQ(WebSize(), webViewHelper.webViewImpl()->mainFrame()->scrollOffset()); } // Resizing just the height should not affect pageScaleFactor or scrollOffset. { webViewHelper.webViewImpl()->resize(WebSize(viewportSize.width, viewportSize.height)); webViewHelper.webViewImpl()->setPageScaleFactor(initialPageScaleFactor); webViewHelper.webViewImpl()->setMainFrameScrollOffset(WebPoint(scrollOffset.width, scrollOffset.height)); webViewHelper.webViewImpl()->layout(); const WebSize expectedScrollOffset = webViewHelper.webViewImpl()->mainFrame()->scrollOffset(); webViewHelper.webViewImpl()->resize(WebSize(viewportSize.width, viewportSize.height * 0.8f)); EXPECT_EQ(initialPageScaleFactor, webViewHelper.webViewImpl()->pageScaleFactor()); EXPECT_EQ(expectedScrollOffset, webViewHelper.webViewImpl()->mainFrame()->scrollOffset()); webViewHelper.webViewImpl()->resize(WebSize(viewportSize.width, viewportSize.height * 0.8f)); EXPECT_EQ(initialPageScaleFactor, webViewHelper.webViewImpl()->pageScaleFactor()); EXPECT_EQ(expectedScrollOffset, webViewHelper.webViewImpl()->mainFrame()->scrollOffset()); } // Generic resize preserves scrollOffset relative to anchor node located // the top center of the screen. { webViewHelper.webViewImpl()->resize(WebSize(viewportSize.height, viewportSize.width)); float pageScaleFactor = webViewHelper.webViewImpl()->pageScaleFactor(); webViewHelper.webViewImpl()->resize(WebSize(viewportSize.width, viewportSize.height)); float expectedPageScaleFactor = pageScaleFactor * (shouldScaleRelativeToViewportWidth ? aspectRatio : 1); EXPECT_NEAR(expectedPageScaleFactor, webViewHelper.webViewImpl()->pageScaleFactor(), 0.05f); webViewHelper.webViewImpl()->mainFrame()->setScrollOffset(scrollOffset); WebCore::IntPoint anchorPoint = WebCore::IntPoint(scrollOffset) + WebCore::IntPoint(viewportSize.width / 2, 0); RefPtrWillBeRawPtr anchorNode = webViewHelper.webViewImpl()->mainFrameImpl()->frame()->eventHandler().hitTestResultAtPoint(anchorPoint, HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent).innerNode(); ASSERT(anchorNode); pageScaleFactor = webViewHelper.webViewImpl()->pageScaleFactor(); const WebCore::FloatSize preResizeRelativeOffset = computeRelativeOffset(anchorPoint, anchorNode->boundingBox()); webViewHelper.webViewImpl()->resize(WebSize(viewportSize.height, viewportSize.width)); WebCore::IntPoint newAnchorPoint = WebCore::IntPoint(webViewHelper.webViewImpl()->mainFrame()->scrollOffset()) + WebCore::IntPoint(viewportSize.height / 2, 0); const WebCore::FloatSize postResizeRelativeOffset = computeRelativeOffset(newAnchorPoint, anchorNode->boundingBox()); EXPECT_NEAR(preResizeRelativeOffset.width(), postResizeRelativeOffset.width(), 0.15f); expectedPageScaleFactor = pageScaleFactor * (shouldScaleRelativeToViewportWidth ? 1 / aspectRatio : 1); EXPECT_NEAR(expectedPageScaleFactor, webViewHelper.webViewImpl()->pageScaleFactor(), 0.05f); } } }; TEST_F(WebFrameResizeTest, ResizeYieldsCorrectScrollAndScaleForWidthEqualsDeviceWidth) { // With width=device-width, pageScaleFactor is preserved across resizes as // long as the content adjusts according to the device-width. const char* url = "resize_scroll_mobile.html"; const float initialPageScaleFactor = 1; const WebSize scrollOffset(0, 50); const WebSize viewportSize(120, 160); const bool shouldScaleRelativeToViewportWidth = true; testResizeYieldsCorrectScrollAndScale( url, initialPageScaleFactor, scrollOffset, viewportSize, shouldScaleRelativeToViewportWidth); } TEST_F(WebFrameResizeTest, ResizeYieldsCorrectScrollAndScaleForFixedWidth) { // With a fixed width, pageScaleFactor scales by the relative change in viewport width. const char* url = "resize_scroll_fixed_width.html"; const float initialPageScaleFactor = 2; const WebSize scrollOffset(0, 200); const WebSize viewportSize(240, 320); const bool shouldScaleRelativeToViewportWidth = true; testResizeYieldsCorrectScrollAndScale( url, initialPageScaleFactor, scrollOffset, viewportSize, shouldScaleRelativeToViewportWidth); } TEST_F(WebFrameResizeTest, ResizeYieldsCorrectScrollAndScaleForFixedLayout) { // With a fixed layout, pageScaleFactor scales by the relative change in viewport width. const char* url = "resize_scroll_fixed_layout.html"; const float initialPageScaleFactor = 2; const WebSize scrollOffset(200, 400); const WebSize viewportSize(320, 240); const bool shouldScaleRelativeToViewportWidth = true; testResizeYieldsCorrectScrollAndScale( url, initialPageScaleFactor, scrollOffset, viewportSize, shouldScaleRelativeToViewportWidth); } TEST_F(WebFrameTest, pageScaleFactorScalesPaintClip) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("large-div.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 50; int viewportHeight = 50; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "large-div.html", true, 0, &client); // FIXME: This test breaks if the viewport is enabled before loading the page due to the paint // calls below not working on composited layers. For some reason, enabling the viewport here // doesn't cause compositing webViewHelper.webView()->settings()->setViewportEnabled(true); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->layout(); // Set <1 page scale so that the clip rect should be larger than // the viewport size as passed into resize(). webViewHelper.webView()->setPageScaleFactor(0.5); SkBitmap bitmap; ASSERT_TRUE(bitmap.allocN32Pixels(200, 200)); bitmap.eraseColor(0); SkCanvas canvas(bitmap); WebCore::GraphicsContext context(&canvas); context.setTrackOpaqueRegion(true); EXPECT_EQ_RECT(WebCore::IntRect(0, 0, 0, 0), context.opaqueRegion().asRect()); WebCore::FrameView* view = webViewHelper.webViewImpl()->mainFrameImpl()->frameView(); WebCore::IntRect paintRect(0, 0, 200, 200); view->paint(&context, paintRect); // FIXME: This test broke in release builds when changing the FixedLayoutTestWebViewClient // to return a non-null layerTreeView, which is what all our shipping configurations do, // so this is just exposing an existing bug. // crbug.com/365812 #ifndef NDEBUG int viewportWidthMinusScrollbar = 50 - (view->verticalScrollbar()->isOverlayScrollbar() ? 0 : 15); int viewportHeightMinusScrollbar = 50 - (view->horizontalScrollbar()->isOverlayScrollbar() ? 0 : 15); WebCore::IntRect clippedRect(0, 0, viewportWidthMinusScrollbar * 2, viewportHeightMinusScrollbar * 2); EXPECT_EQ_RECT(clippedRect, context.opaqueRegion().asRect()); #endif } TEST_F(WebFrameTest, pageScaleFactorUpdatesScrollbars) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("fixed_layout.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "fixed_layout.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->layout(); WebCore::FrameView* view = webViewHelper.webViewImpl()->mainFrameImpl()->frameView(); EXPECT_EQ(view->scrollSize(WebCore::HorizontalScrollbar), view->contentsSize().width() - view->visibleContentRect().width()); EXPECT_EQ(view->scrollSize(WebCore::VerticalScrollbar), view->contentsSize().height() - view->visibleContentRect().height()); webViewHelper.webView()->setPageScaleFactor(10); EXPECT_EQ(view->scrollSize(WebCore::HorizontalScrollbar), view->contentsSize().width() - view->visibleContentRect().width()); EXPECT_EQ(view->scrollSize(WebCore::VerticalScrollbar), view->contentsSize().height() - view->visibleContentRect().height()); } TEST_F(WebFrameTest, CanOverrideScaleLimits) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("no_scale_for_you.html"); FixedLayoutTestWebViewClient client; client.m_screenInfo.deviceScaleFactor = 1; int viewportWidth = 640; int viewportHeight = 480; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "no_scale_for_you.html", true, 0, &client, enableViewportSettings); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); EXPECT_EQ(2.0f, webViewHelper.webView()->minimumPageScaleFactor()); EXPECT_EQ(2.0f, webViewHelper.webView()->maximumPageScaleFactor()); webViewHelper.webView()->setIgnoreViewportTagScaleLimits(true); webViewHelper.webView()->layout(); EXPECT_EQ(1.0f, webViewHelper.webView()->minimumPageScaleFactor()); EXPECT_EQ(5.0f, webViewHelper.webView()->maximumPageScaleFactor()); webViewHelper.webView()->setIgnoreViewportTagScaleLimits(false); webViewHelper.webView()->layout(); EXPECT_EQ(2.0f, webViewHelper.webView()->minimumPageScaleFactor()); EXPECT_EQ(2.0f, webViewHelper.webView()->maximumPageScaleFactor()); } TEST_F(WebFrameTest, updateOverlayScrollbarLayers) { UseMockScrollbarSettings mockScrollbarSettings; registerMockedHttpURLLoad("large-div.html"); int viewWidth = 500; int viewHeight = 500; OwnPtr fakeCompositingWebViewClient = adoptPtr(new FakeCompositingWebViewClient()); FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initialize(true, 0, fakeCompositingWebViewClient.get(), &configueCompositingWebView); webViewHelper.webView()->setPageScaleFactorLimits(1, 1); webViewHelper.webView()->resize(WebSize(viewWidth, viewHeight)); FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), m_baseURL + "large-div.html"); WebCore::FrameView* view = webViewHelper.webViewImpl()->mainFrameImpl()->frameView(); EXPECT_TRUE(view->renderView()->compositor()->layerForHorizontalScrollbar()); EXPECT_TRUE(view->renderView()->compositor()->layerForVerticalScrollbar()); webViewHelper.webView()->resize(WebSize(viewWidth * 10, viewHeight * 10)); webViewHelper.webView()->layout(); EXPECT_FALSE(view->renderView()->compositor()->layerForHorizontalScrollbar()); EXPECT_FALSE(view->renderView()->compositor()->layerForVerticalScrollbar()); } void setScaleAndScrollAndLayout(blink::WebView* webView, WebPoint scroll, float scale) { webView->setPageScaleFactor(scale); webView->setMainFrameScrollOffset(WebPoint(scroll.x, scroll.y)); webView->layout(); } TEST_F(WebFrameTest, DivAutoZoomParamsTest) { registerMockedHttpURLLoad("get_scale_for_auto_zoom_into_div_test.html"); const float deviceScaleFactor = 2.0f; int viewportWidth = 640 / deviceScaleFactor; int viewportHeight = 1280 / deviceScaleFactor; float doubleTapZoomAlreadyLegibleRatio = 1.2f; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "get_scale_for_auto_zoom_into_div_test.html"); webViewHelper.webView()->setDeviceScaleFactor(deviceScaleFactor); webViewHelper.webView()->setPageScaleFactorLimits(0.01f, 4); webViewHelper.webView()->setPageScaleFactor(0.5f); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->layout(); WebRect wideDiv(200, 100, 400, 150); WebRect tallDiv(200, 300, 400, 800); WebRect doubleTapPointWide(wideDiv.x + 50, wideDiv.y + 50, touchPointPadding, touchPointPadding); WebRect doubleTapPointTall(tallDiv.x + 50, tallDiv.y + 50, touchPointPadding, touchPointPadding); WebRect wideBlockBounds; WebRect tallBlockBounds; float scale; WebPoint scroll; float doubleTapZoomAlreadyLegibleScale = webViewHelper.webViewImpl()->minimumPageScaleFactor() * doubleTapZoomAlreadyLegibleRatio; // Test double-tap zooming into wide div. wideBlockBounds = webViewHelper.webViewImpl()->computeBlockBounds(doubleTapPointWide, false); webViewHelper.webViewImpl()->computeScaleAndScrollForBlockRect(WebPoint(doubleTapPointWide.x, doubleTapPointWide.y), wideBlockBounds, touchPointPadding, doubleTapZoomAlreadyLegibleScale, scale, scroll); // The div should horizontally fill the screen (modulo margins), and // vertically centered (modulo integer rounding). EXPECT_NEAR(viewportWidth / (float) wideDiv.width, scale, 0.1); EXPECT_NEAR(wideDiv.x, scroll.x, 20); EXPECT_EQ(0, scroll.y); setScaleAndScrollAndLayout(webViewHelper.webViewImpl(), scroll, scale); // Test zoom out back to minimum scale. wideBlockBounds = webViewHelper.webViewImpl()->computeBlockBounds(doubleTapPointWide, false); webViewHelper.webViewImpl()->computeScaleAndScrollForBlockRect(WebPoint(doubleTapPointWide.x, doubleTapPointWide.y), wideBlockBounds, touchPointPadding, doubleTapZoomAlreadyLegibleScale, scale, scroll); scale = webViewHelper.webViewImpl()->minimumPageScaleFactor(); setScaleAndScrollAndLayout(webViewHelper.webViewImpl(), WebPoint(0, 0), scale); // Test double-tap zooming into tall div. tallBlockBounds = webViewHelper.webViewImpl()->computeBlockBounds(doubleTapPointTall, false); webViewHelper.webViewImpl()->computeScaleAndScrollForBlockRect(WebPoint(doubleTapPointTall.x, doubleTapPointTall.y), tallBlockBounds, touchPointPadding, doubleTapZoomAlreadyLegibleScale, scale, scroll); // The div should start at the top left of the viewport. EXPECT_NEAR(viewportWidth / (float) tallDiv.width, scale, 0.1); EXPECT_NEAR(tallDiv.x, scroll.x, 20); EXPECT_NEAR(tallDiv.y, scroll.y, 20); // Test for Non-doubletap scaling // Test zooming into div. webViewHelper.webViewImpl()->computeScaleAndScrollForBlockRect(WebPoint(250, 250), webViewHelper.webViewImpl()->computeBlockBounds(WebRect(250, 250, 10, 10), true), 0, doubleTapZoomAlreadyLegibleScale, scale, scroll); EXPECT_NEAR(viewportWidth / (float) wideDiv.width, scale, 0.1); } void simulatePageScale(WebViewImpl* webViewImpl, float& scale) { WebCore::IntSize scrollDelta = webViewImpl->fakePageScaleAnimationTargetPositionForTesting() - webViewImpl->mainFrameImpl()->frameView()->scrollPosition(); float scaleDelta = webViewImpl->fakePageScaleAnimationPageScaleForTesting() / webViewImpl->pageScaleFactor(); webViewImpl->applyScrollAndScale(scrollDelta, scaleDelta); scale = webViewImpl->pageScaleFactor(); } void simulateMultiTargetZoom(WebViewImpl* webViewImpl, const WebRect& rect, float& scale) { if (webViewImpl->zoomToMultipleTargetsRect(rect)) simulatePageScale(webViewImpl, scale); } void simulateDoubleTap(WebViewImpl* webViewImpl, WebPoint& point, float& scale) { webViewImpl->animateDoubleTapZoom(point); EXPECT_TRUE(webViewImpl->fakeDoubleTapAnimationPendingForTesting()); simulatePageScale(webViewImpl, scale); } TEST_F(WebFrameTest, DivAutoZoomWideDivTest) { registerMockedHttpURLLoad("get_wide_div_for_auto_zoom_test.html"); const float deviceScaleFactor = 2.0f; int viewportWidth = 640 / deviceScaleFactor; int viewportHeight = 1280 / deviceScaleFactor; float doubleTapZoomAlreadyLegibleRatio = 1.2f; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "get_wide_div_for_auto_zoom_test.html"); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->setPageScaleFactorLimits(1.0f, 4); webViewHelper.webView()->setDeviceScaleFactor(deviceScaleFactor); webViewHelper.webView()->setPageScaleFactor(1.0f); webViewHelper.webView()->layout(); webViewHelper.webViewImpl()->enableFakePageScaleAnimationForTesting(true); float doubleTapZoomAlreadyLegibleScale = webViewHelper.webViewImpl()->minimumPageScaleFactor() * doubleTapZoomAlreadyLegibleRatio; WebRect div(0, 100, viewportWidth, 150); WebPoint point(div.x + 50, div.y + 50); float scale; setScaleAndScrollAndLayout(webViewHelper.webViewImpl(), WebPoint(0, 0), (webViewHelper.webViewImpl()->minimumPageScaleFactor()) * (1 + doubleTapZoomAlreadyLegibleRatio) / 2); simulateDoubleTap(webViewHelper.webViewImpl(), point, scale); EXPECT_FLOAT_EQ(doubleTapZoomAlreadyLegibleScale, scale); simulateDoubleTap(webViewHelper.webViewImpl(), point, scale); EXPECT_FLOAT_EQ(webViewHelper.webViewImpl()->minimumPageScaleFactor(), scale); } TEST_F(WebFrameTest, DivAutoZoomVeryTallTest) { // When a block is taller than the viewport and a zoom targets a lower part // of it, then we should keep the target point onscreen instead of snapping // back up the top of the block. registerMockedHttpURLLoad("very_tall_div.html"); const float deviceScaleFactor = 2.0f; int viewportWidth = 640 / deviceScaleFactor; int viewportHeight = 1280 / deviceScaleFactor; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "very_tall_div.html", true, 0, 0, enableViewportSettings); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->setPageScaleFactorLimits(1.0f, 4); webViewHelper.webView()->setDeviceScaleFactor(deviceScaleFactor); webViewHelper.webView()->setPageScaleFactor(1.0f); webViewHelper.webView()->layout(); WebRect div(200, 300, 400, 5000); WebPoint point(div.x + 50, div.y + 3000); float scale; WebPoint scroll; WebRect blockBounds = webViewHelper.webViewImpl()->computeBlockBounds(WebRect(point.x, point.y, 0, 0), true); webViewHelper.webViewImpl()->computeScaleAndScrollForBlockRect(point, blockBounds, 0, 1.0f, scale, scroll); EXPECT_EQ(scale, 1.0f); EXPECT_EQ(scroll.y, 2660); } TEST_F(WebFrameTest, DivAutoZoomMultipleDivsTest) { registerMockedHttpURLLoad("get_multiple_divs_for_auto_zoom_test.html"); const float deviceScaleFactor = 2.0f; int viewportWidth = 640 / deviceScaleFactor; int viewportHeight = 1280 / deviceScaleFactor; float doubleTapZoomAlreadyLegibleRatio = 1.2f; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "get_multiple_divs_for_auto_zoom_test.html"); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->setPageScaleFactorLimits(0.5f, 4); webViewHelper.webView()->setDeviceScaleFactor(deviceScaleFactor); webViewHelper.webView()->setPageScaleFactor(0.5f); webViewHelper.webView()->layout(); webViewHelper.webViewImpl()->enableFakePageScaleAnimationForTesting(true); WebRect topDiv(200, 100, 200, 150); WebRect bottomDiv(200, 300, 200, 150); WebPoint topPoint(topDiv.x + 50, topDiv.y + 50); WebPoint bottomPoint(bottomDiv.x + 50, bottomDiv.y + 50); float scale; setScaleAndScrollAndLayout(webViewHelper.webViewImpl(), WebPoint(0, 0), (webViewHelper.webViewImpl()->minimumPageScaleFactor()) * (1 + doubleTapZoomAlreadyLegibleRatio) / 2); // Test double tap on two different divs // After first zoom, we should go back to minimum page scale with a second double tap. simulateDoubleTap(webViewHelper.webViewImpl(), topPoint, scale); EXPECT_FLOAT_EQ(1, scale); simulateDoubleTap(webViewHelper.webViewImpl(), bottomPoint, scale); EXPECT_FLOAT_EQ(webViewHelper.webViewImpl()->minimumPageScaleFactor(), scale); // If the user pinch zooms after double tap, a second double tap should zoom back to the div. simulateDoubleTap(webViewHelper.webViewImpl(), topPoint, scale); EXPECT_FLOAT_EQ(1, scale); webViewHelper.webViewImpl()->applyScrollAndScale(WebSize(), 0.6f); simulateDoubleTap(webViewHelper.webViewImpl(), bottomPoint, scale); EXPECT_FLOAT_EQ(1, scale); simulateDoubleTap(webViewHelper.webViewImpl(), bottomPoint, scale); EXPECT_FLOAT_EQ(webViewHelper.webViewImpl()->minimumPageScaleFactor(), scale); // If we didn't yet get an auto-zoom update and a second double-tap arrives, should go back to minimum scale. webViewHelper.webViewImpl()->applyScrollAndScale(WebSize(), 1.1f); webViewHelper.webViewImpl()->animateDoubleTapZoom(topPoint); EXPECT_TRUE(webViewHelper.webViewImpl()->fakeDoubleTapAnimationPendingForTesting()); simulateDoubleTap(webViewHelper.webViewImpl(), bottomPoint, scale); EXPECT_FLOAT_EQ(webViewHelper.webViewImpl()->minimumPageScaleFactor(), scale); } TEST_F(WebFrameTest, DivAutoZoomScaleBoundsTest) { registerMockedHttpURLLoad("get_scale_bounds_check_for_auto_zoom_test.html"); int viewportWidth = 320; int viewportHeight = 480; float doubleTapZoomAlreadyLegibleRatio = 1.2f; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "get_scale_bounds_check_for_auto_zoom_test.html"); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->setDeviceScaleFactor(1.5f); webViewHelper.webView()->layout(); webViewHelper.webViewImpl()->enableFakePageScaleAnimationForTesting(true); WebRect div(200, 100, 200, 150); WebPoint doubleTapPoint(div.x + 50, div.y + 50); float scale; // Test double tap scale bounds. // minimumPageScale < doubleTapZoomAlreadyLegibleScale < 1 webViewHelper.webView()->setPageScaleFactorLimits(0.5f, 4); webViewHelper.webView()->layout(); float doubleTapZoomAlreadyLegibleScale = webViewHelper.webViewImpl()->minimumPageScaleFactor() * doubleTapZoomAlreadyLegibleRatio; setScaleAndScrollAndLayout(webViewHelper.webViewImpl(), WebPoint(0, 0), (webViewHelper.webViewImpl()->minimumPageScaleFactor()) * (1 + doubleTapZoomAlreadyLegibleRatio) / 2); simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale); EXPECT_FLOAT_EQ(1, scale); simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale); EXPECT_FLOAT_EQ(webViewHelper.webViewImpl()->minimumPageScaleFactor(), scale); simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale); EXPECT_FLOAT_EQ(1, scale); // Zoom in to reset double_tap_zoom_in_effect flag. webViewHelper.webViewImpl()->applyScrollAndScale(WebSize(), 1.1f); // 1 < minimumPageScale < doubleTapZoomAlreadyLegibleScale webViewHelper.webView()->setPageScaleFactorLimits(1.1f, 4); webViewHelper.webView()->layout(); doubleTapZoomAlreadyLegibleScale = webViewHelper.webViewImpl()->minimumPageScaleFactor() * doubleTapZoomAlreadyLegibleRatio; setScaleAndScrollAndLayout(webViewHelper.webViewImpl(), WebPoint(0, 0), (webViewHelper.webViewImpl()->minimumPageScaleFactor()) * (1 + doubleTapZoomAlreadyLegibleRatio) / 2); simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale); EXPECT_FLOAT_EQ(doubleTapZoomAlreadyLegibleScale, scale); simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale); EXPECT_FLOAT_EQ(webViewHelper.webViewImpl()->minimumPageScaleFactor(), scale); simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale); EXPECT_FLOAT_EQ(doubleTapZoomAlreadyLegibleScale, scale); // Zoom in to reset double_tap_zoom_in_effect flag. webViewHelper.webViewImpl()->applyScrollAndScale(WebSize(), 1.1f); // minimumPageScale < 1 < doubleTapZoomAlreadyLegibleScale webViewHelper.webView()->setPageScaleFactorLimits(0.95f, 4); webViewHelper.webView()->layout(); doubleTapZoomAlreadyLegibleScale = webViewHelper.webViewImpl()->minimumPageScaleFactor() * doubleTapZoomAlreadyLegibleRatio; setScaleAndScrollAndLayout(webViewHelper.webViewImpl(), WebPoint(0, 0), (webViewHelper.webViewImpl()->minimumPageScaleFactor()) * (1 + doubleTapZoomAlreadyLegibleRatio) / 2); simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale); EXPECT_FLOAT_EQ(doubleTapZoomAlreadyLegibleScale, scale); simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale); EXPECT_FLOAT_EQ(webViewHelper.webViewImpl()->minimumPageScaleFactor(), scale); simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale); EXPECT_FLOAT_EQ(doubleTapZoomAlreadyLegibleScale, scale); } TEST_F(WebFrameTest, DivAutoZoomScaleFontScaleFactorTest) { registerMockedHttpURLLoad("get_scale_bounds_check_for_auto_zoom_test.html"); int viewportWidth = 320; int viewportHeight = 480; float doubleTapZoomAlreadyLegibleRatio = 1.2f; float accessibilityFontScaleFactor = 1.13f; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "get_scale_bounds_check_for_auto_zoom_test.html"); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->layout(); webViewHelper.webViewImpl()->enableFakePageScaleAnimationForTesting(true); webViewHelper.webViewImpl()->page()->settings().setTextAutosizingEnabled(true); webViewHelper.webViewImpl()->page()->settings().setAccessibilityFontScaleFactor(accessibilityFontScaleFactor); WebRect div(200, 100, 200, 150); WebPoint doubleTapPoint(div.x + 50, div.y + 50); float scale; // Test double tap scale bounds. // minimumPageScale < doubleTapZoomAlreadyLegibleScale < 1 < accessibilityFontScaleFactor float legibleScale = accessibilityFontScaleFactor; setScaleAndScrollAndLayout(webViewHelper.webViewImpl(), WebPoint(0, 0), (webViewHelper.webViewImpl()->minimumPageScaleFactor()) * (1 + doubleTapZoomAlreadyLegibleRatio) / 2); float doubleTapZoomAlreadyLegibleScale = webViewHelper.webViewImpl()->minimumPageScaleFactor() * doubleTapZoomAlreadyLegibleRatio; webViewHelper.webView()->setPageScaleFactorLimits(0.5f, 4); webViewHelper.webView()->layout(); simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale); EXPECT_FLOAT_EQ(legibleScale, scale); simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale); EXPECT_FLOAT_EQ(webViewHelper.webViewImpl()->minimumPageScaleFactor(), scale); simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale); EXPECT_FLOAT_EQ(legibleScale, scale); // Zoom in to reset double_tap_zoom_in_effect flag. webViewHelper.webViewImpl()->applyScrollAndScale(WebSize(), 1.1f); // 1 < accessibilityFontScaleFactor < minimumPageScale < doubleTapZoomAlreadyLegibleScale webViewHelper.webView()->setPageScaleFactorLimits(1.0f, 4); webViewHelper.webView()->layout(); doubleTapZoomAlreadyLegibleScale = webViewHelper.webViewImpl()->minimumPageScaleFactor() * doubleTapZoomAlreadyLegibleRatio; setScaleAndScrollAndLayout(webViewHelper.webViewImpl(), WebPoint(0, 0), (webViewHelper.webViewImpl()->minimumPageScaleFactor()) * (1 + doubleTapZoomAlreadyLegibleRatio) / 2); simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale); EXPECT_FLOAT_EQ(doubleTapZoomAlreadyLegibleScale, scale); simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale); EXPECT_FLOAT_EQ(webViewHelper.webViewImpl()->minimumPageScaleFactor(), scale); simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale); EXPECT_FLOAT_EQ(doubleTapZoomAlreadyLegibleScale, scale); // Zoom in to reset double_tap_zoom_in_effect flag. webViewHelper.webViewImpl()->applyScrollAndScale(WebSize(), 1.1f); // minimumPageScale < 1 < accessibilityFontScaleFactor < doubleTapZoomAlreadyLegibleScale webViewHelper.webView()->setPageScaleFactorLimits(0.95f, 4); webViewHelper.webView()->layout(); doubleTapZoomAlreadyLegibleScale = webViewHelper.webViewImpl()->minimumPageScaleFactor() * doubleTapZoomAlreadyLegibleRatio; setScaleAndScrollAndLayout(webViewHelper.webViewImpl(), WebPoint(0, 0), (webViewHelper.webViewImpl()->minimumPageScaleFactor()) * (1 + doubleTapZoomAlreadyLegibleRatio) / 2); simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale); EXPECT_FLOAT_EQ(doubleTapZoomAlreadyLegibleScale, scale); simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale); EXPECT_FLOAT_EQ(webViewHelper.webViewImpl()->minimumPageScaleFactor(), scale); simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale); EXPECT_FLOAT_EQ(doubleTapZoomAlreadyLegibleScale, scale); // Zoom in to reset double_tap_zoom_in_effect flag. webViewHelper.webViewImpl()->applyScrollAndScale(WebSize(), 1.1f); // minimumPageScale < 1 < doubleTapZoomAlreadyLegibleScale < accessibilityFontScaleFactor webViewHelper.webView()->setPageScaleFactorLimits(0.9f, 4); webViewHelper.webView()->layout(); doubleTapZoomAlreadyLegibleScale = webViewHelper.webViewImpl()->minimumPageScaleFactor() * doubleTapZoomAlreadyLegibleRatio; setScaleAndScrollAndLayout(webViewHelper.webViewImpl(), WebPoint(0, 0), (webViewHelper.webViewImpl()->minimumPageScaleFactor()) * (1 + doubleTapZoomAlreadyLegibleRatio) / 2); simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale); EXPECT_FLOAT_EQ(legibleScale, scale); simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale); EXPECT_FLOAT_EQ(webViewHelper.webViewImpl()->minimumPageScaleFactor(), scale); simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale); EXPECT_FLOAT_EQ(legibleScale, scale); } TEST_F(WebFrameTest, DivMultipleTargetZoomMultipleDivsTest) { registerMockedHttpURLLoad("get_multiple_divs_for_auto_zoom_test.html"); const float deviceScaleFactor = 2.0f; int viewportWidth = 640 / deviceScaleFactor; int viewportHeight = 1280 / deviceScaleFactor; float doubleTapZoomAlreadyLegibleRatio = 1.2f; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "get_multiple_divs_for_auto_zoom_test.html"); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->setPageScaleFactorLimits(0.5f, 4); webViewHelper.webView()->setDeviceScaleFactor(deviceScaleFactor); webViewHelper.webView()->setPageScaleFactor(0.5f); webViewHelper.webView()->layout(); webViewHelper.webViewImpl()->enableFakePageScaleAnimationForTesting(true); WebRect viewportRect(0, 0, viewportWidth, viewportHeight); WebRect topDiv(200, 100, 200, 150); WebRect bottomDiv(200, 300, 200, 150); float scale; setScaleAndScrollAndLayout(webViewHelper.webViewImpl(), WebPoint(0, 0), (webViewHelper.webViewImpl()->minimumPageScaleFactor()) * (1 + doubleTapZoomAlreadyLegibleRatio) / 2); simulateMultiTargetZoom(webViewHelper.webViewImpl(), topDiv, scale); EXPECT_FLOAT_EQ(1, scale); simulateMultiTargetZoom(webViewHelper.webViewImpl(), bottomDiv, scale); EXPECT_FLOAT_EQ(1, scale); simulateMultiTargetZoom(webViewHelper.webViewImpl(), viewportRect, scale); EXPECT_FLOAT_EQ(1, scale); webViewHelper.webViewImpl()->setPageScaleFactor(webViewHelper.webViewImpl()->minimumPageScaleFactor()); simulateMultiTargetZoom(webViewHelper.webViewImpl(), topDiv, scale); EXPECT_FLOAT_EQ(1, scale); } TEST_F(WebFrameTest, DivScrollIntoEditableTest) { registerMockedHttpURLLoad("get_scale_for_zoom_into_editable_test.html"); int viewportWidth = 450; int viewportHeight = 300; float leftBoxRatio = 0.3f; int caretPadding = 10; float minReadableCaretHeight = 18.0f; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "get_scale_for_zoom_into_editable_test.html"); webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); webViewHelper.webView()->setPageScaleFactorLimits(1, 4); webViewHelper.webView()->layout(); webViewHelper.webView()->setDeviceScaleFactor(1.5f); webViewHelper.webView()->settings()->setAutoZoomFocusedNodeToLegibleScale(true); webViewHelper.webViewImpl()->enableFakePageScaleAnimationForTesting(true); WebRect editBoxWithText(200, 200, 250, 20); WebRect editBoxWithNoText(200, 250, 250, 20); // Test scrolling the focused node // The edit box is shorter and narrower than the viewport when legible. webViewHelper.webView()->advanceFocus(false); // Set the caret to the end of the input box. webViewHelper.webView()->mainFrame()->document().getElementById("EditBoxWithText").to().setSelectionRange(1000, 1000); setScaleAndScrollAndLayout(webViewHelper.webView(), WebPoint(0, 0), 1); WebRect rect, caret; webViewHelper.webViewImpl()->selectionBounds(caret, rect); float scale; WebCore::IntPoint scroll; bool needAnimation; webViewHelper.webViewImpl()->computeScaleAndScrollForFocusedNode(webViewHelper.webViewImpl()->focusedElement(), scale, scroll, needAnimation); EXPECT_TRUE(needAnimation); // The edit box should be left aligned with a margin for possible label. int hScroll = editBoxWithText.x - leftBoxRatio * viewportWidth / scale; EXPECT_NEAR(hScroll, scroll.x(), 1); int vScroll = editBoxWithText.y - (viewportHeight / scale - editBoxWithText.height) / 2; EXPECT_NEAR(vScroll, scroll.y(), 1); EXPECT_NEAR(minReadableCaretHeight / caret.height, scale, 0.1); // The edit box is wider than the viewport when legible. viewportWidth = 200; viewportHeight = 150; webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight)); setScaleAndScrollAndLayout(webViewHelper.webView(), WebPoint(0, 0), 1); webViewHelper.webViewImpl()->selectionBounds(caret, rect); webViewHelper.webViewImpl()->computeScaleAndScrollForFocusedNode(webViewHelper.webViewImpl()->focusedElement(), scale, scroll, needAnimation); EXPECT_TRUE(needAnimation); // The caret should be right aligned since the caret would be offscreen when the edit box is left aligned. hScroll = caret.x + caret.width + caretPadding - viewportWidth / scale; EXPECT_NEAR(hScroll, scroll.x(), 1); EXPECT_NEAR(minReadableCaretHeight / caret.height, scale, 0.1); setScaleAndScrollAndLayout(webViewHelper.webView(), WebPoint(0, 0), 1); // Move focus to edit box with text. webViewHelper.webView()->advanceFocus(false); webViewHelper.webViewImpl()->selectionBounds(caret, rect); webViewHelper.webViewImpl()->computeScaleAndScrollForFocusedNode(webViewHelper.webViewImpl()->focusedElement(), scale, scroll, needAnimation); EXPECT_TRUE(needAnimation); // The edit box should be left aligned. hScroll = editBoxWithNoText.x; EXPECT_NEAR(hScroll, scroll.x(), 1); vScroll = editBoxWithNoText.y - (viewportHeight / scale - editBoxWithNoText.height) / 2; EXPECT_NEAR(vScroll, scroll.y(), 1); EXPECT_NEAR(minReadableCaretHeight / caret.height, scale, 0.1); setScaleAndScrollAndLayout(webViewHelper.webViewImpl(), scroll, scale); // Move focus back to the first edit box. webViewHelper.webView()->advanceFocus(true); webViewHelper.webViewImpl()->computeScaleAndScrollForFocusedNode(webViewHelper.webViewImpl()->focusedElement(), scale, scroll, needAnimation); // The position should have stayed the same since this box was already on screen with the right scale. EXPECT_FALSE(needAnimation); } class TestReloadDoesntRedirectWebFrameClient : public FrameTestHelpers::TestWebFrameClient { public: virtual WebNavigationPolicy decidePolicyForNavigation( WebLocalFrame*, WebDataSource::ExtraData*, const WebURLRequest&, WebNavigationType, WebNavigationPolicy defaultPolicy, bool isRedirect) OVERRIDE { EXPECT_FALSE(isRedirect); return WebNavigationPolicyCurrentTab; } }; TEST_F(WebFrameTest, ReloadDoesntSetRedirect) { // Test for case in http://crbug.com/73104. Reloading a frame very quickly // would sometimes call decidePolicyForNavigation with isRedirect=true registerMockedHttpURLLoad("form.html"); TestReloadDoesntRedirectWebFrameClient webFrameClient; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "form.html", false, &webFrameClient); webViewHelper.webView()->mainFrame()->reload(true); // start another reload before request is delivered. FrameTestHelpers::reloadFrameIgnoringCache(webViewHelper.webView()->mainFrame()); } class ReloadWithOverrideURLTask : public WebThread::Task { public: ReloadWithOverrideURLTask(WebFrame* frame, const WebCore::KURL& url, bool ignoreCache) : m_frame(frame), m_url(url), m_ignoreCache(ignoreCache) { } virtual void run() OVERRIDE { m_frame->reloadWithOverrideURL(m_url, m_ignoreCache); } private: WebFrame* const m_frame; const WebCore::KURL m_url; const bool m_ignoreCache; }; TEST_F(WebFrameTest, ReloadWithOverrideURLPreservesState) { const std::string firstURL = "find.html"; const std::string secondURL = "form.html"; const std::string thirdURL = "history.html"; const float pageScaleFactor = 1.1684f; const int pageWidth = 640; const int pageHeight = 480; registerMockedHttpURLLoad(firstURL); registerMockedHttpURLLoad(secondURL); registerMockedHttpURLLoad(thirdURL); FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + firstURL, true); webViewHelper.webViewImpl()->resize(WebSize(pageWidth, pageHeight)); webViewHelper.webViewImpl()->mainFrame()->setScrollOffset(WebSize(pageWidth / 4, pageHeight / 4)); webViewHelper.webViewImpl()->setPageScaleFactor(pageScaleFactor); WebSize previousOffset = webViewHelper.webViewImpl()->mainFrame()->scrollOffset(); float previousScale = webViewHelper.webViewImpl()->pageScaleFactor(); // Reload the page using the cache. Platform::current()->currentThread()->postTask( new ReloadWithOverrideURLTask(webViewHelper.webViewImpl()->mainFrame(), toKURL(m_baseURL + secondURL), false)); FrameTestHelpers::pumpPendingRequestsDoNotUse(webViewHelper.webViewImpl()->mainFrame()); ASSERT_EQ(previousOffset, webViewHelper.webViewImpl()->mainFrame()->scrollOffset()); ASSERT_EQ(previousScale, webViewHelper.webViewImpl()->pageScaleFactor()); // Reload the page while ignoring the cache. Platform::current()->currentThread()->postTask( new ReloadWithOverrideURLTask(webViewHelper.webViewImpl()->mainFrame(), toKURL(m_baseURL + thirdURL), true)); FrameTestHelpers::pumpPendingRequestsDoNotUse(webViewHelper.webViewImpl()->mainFrame()); ASSERT_EQ(previousOffset, webViewHelper.webViewImpl()->mainFrame()->scrollOffset()); ASSERT_EQ(previousScale, webViewHelper.webViewImpl()->pageScaleFactor()); } TEST_F(WebFrameTest, ReloadWhileProvisional) { // Test that reloading while the previous load is still pending does not cause the initial // request to get lost. registerMockedHttpURLLoad("fixed_layout.html"); FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initialize(); WebURLRequest request; request.initialize(); request.setURL(toKURL(m_baseURL + "fixed_layout.html")); webViewHelper.webView()->mainFrame()->loadRequest(request); // start reload before first request is delivered. FrameTestHelpers::reloadFrameIgnoringCache(webViewHelper.webView()->mainFrame()); WebDataSource* dataSource = webViewHelper.webView()->mainFrame()->dataSource(); ASSERT_TRUE(dataSource); EXPECT_EQ(toKURL(m_baseURL + "fixed_layout.html"), toKURL(dataSource->request().url().spec())); } TEST_F(WebFrameTest, AppendRedirects) { const std::string firstURL = "about:blank"; const std::string secondURL = "http://www.test.com"; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(firstURL, true); WebDataSource* dataSource = webViewHelper.webView()->mainFrame()->dataSource(); ASSERT_TRUE(dataSource); dataSource->appendRedirect(toKURL(secondURL)); WebVector redirects; dataSource->redirectChain(redirects); ASSERT_EQ(2U, redirects.size()); EXPECT_EQ(toKURL(firstURL), toKURL(redirects[0].spec().data())); EXPECT_EQ(toKURL(secondURL), toKURL(redirects[1].spec().data())); } TEST_F(WebFrameTest, IframeRedirect) { registerMockedHttpURLLoad("iframe_redirect.html"); registerMockedHttpURLLoad("visible_iframe.html"); FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "iframe_redirect.html", true); // Pump pending requests one more time. The test page loads script that navigates. FrameTestHelpers::pumpPendingRequestsDoNotUse(webViewHelper.webView()->mainFrame()); WebFrame* iframe = webViewHelper.webView()->findFrameByName(WebString::fromUTF8("ifr")); ASSERT_TRUE(iframe); WebDataSource* iframeDataSource = iframe->dataSource(); ASSERT_TRUE(iframeDataSource); WebVector redirects; iframeDataSource->redirectChain(redirects); ASSERT_EQ(2U, redirects.size()); EXPECT_EQ(toKURL("about:blank"), toKURL(redirects[0].spec().data())); EXPECT_EQ(toKURL("http://www.test.com/visible_iframe.html"), toKURL(redirects[1].spec().data())); } TEST_F(WebFrameTest, ClearFocusedNodeTest) { registerMockedHttpURLLoad("iframe_clear_focused_node_test.html"); registerMockedHttpURLLoad("autofocus_input_field_iframe.html"); FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "iframe_clear_focused_node_test.html", true); // Clear the focused node. webViewHelper.webView()->clearFocusedElement(); // Now retrieve the FocusedNode and test it should be null. EXPECT_EQ(0, webViewHelper.webViewImpl()->focusedElement()); } // Implementation of WebFrameClient that tracks the v8 contexts that are created // and destroyed for verification. class ContextLifetimeTestWebFrameClient : public FrameTestHelpers::TestWebFrameClient { public: struct Notification { public: Notification(WebLocalFrame* frame, v8::Handle context, int worldId) : frame(frame) , context(context->GetIsolate(), context) , worldId(worldId) { } ~Notification() { context.Reset(); } bool Equals(Notification* other) { return other && frame == other->frame && context == other->context && worldId == other->worldId; } WebLocalFrame* frame; v8::Persistent context; int worldId; }; virtual ~ContextLifetimeTestWebFrameClient() { reset(); } void reset() { for (size_t i = 0; i < createNotifications.size(); ++i) delete createNotifications[i]; for (size_t i = 0; i < releaseNotifications.size(); ++i) delete releaseNotifications[i]; createNotifications.clear(); releaseNotifications.clear(); } std::vector createNotifications; std::vector releaseNotifications; private: virtual void didCreateScriptContext(WebLocalFrame* frame, v8::Handle context, int extensionGroup, int worldId) OVERRIDE { createNotifications.push_back(new Notification(frame, context, worldId)); } virtual void willReleaseScriptContext(WebLocalFrame* frame, v8::Handle context, int worldId) OVERRIDE { releaseNotifications.push_back(new Notification(frame, context, worldId)); } }; // TODO(aa): Deflake this test. TEST_F(WebFrameTest, FLAKY_ContextNotificationsLoadUnload) { v8::HandleScope handleScope(v8::Isolate::GetCurrent()); registerMockedHttpURLLoad("context_notifications_test.html"); registerMockedHttpURLLoad("context_notifications_test_frame.html"); // Load a frame with an iframe, make sure we get the right create notifications. ContextLifetimeTestWebFrameClient webFrameClient; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "context_notifications_test.html", true, &webFrameClient); WebFrame* mainFrame = webViewHelper.webView()->mainFrame(); WebFrame* childFrame = mainFrame->firstChild(); ASSERT_EQ(2u, webFrameClient.createNotifications.size()); EXPECT_EQ(0u, webFrameClient.releaseNotifications.size()); ContextLifetimeTestWebFrameClient::Notification* firstCreateNotification = webFrameClient.createNotifications[0]; ContextLifetimeTestWebFrameClient::Notification* secondCreateNotification = webFrameClient.createNotifications[1]; EXPECT_EQ(mainFrame, firstCreateNotification->frame); EXPECT_EQ(mainFrame->mainWorldScriptContext(), firstCreateNotification->context); EXPECT_EQ(0, firstCreateNotification->worldId); EXPECT_EQ(childFrame, secondCreateNotification->frame); EXPECT_EQ(childFrame->mainWorldScriptContext(), secondCreateNotification->context); EXPECT_EQ(0, secondCreateNotification->worldId); // Close the view. We should get two release notifications that are exactly the same as the create ones, in reverse order. webViewHelper.reset(); ASSERT_EQ(2u, webFrameClient.releaseNotifications.size()); ContextLifetimeTestWebFrameClient::Notification* firstReleaseNotification = webFrameClient.releaseNotifications[0]; ContextLifetimeTestWebFrameClient::Notification* secondReleaseNotification = webFrameClient.releaseNotifications[1]; ASSERT_TRUE(firstCreateNotification->Equals(secondReleaseNotification)); ASSERT_TRUE(secondCreateNotification->Equals(firstReleaseNotification)); } TEST_F(WebFrameTest, ContextNotificationsReload) { v8::HandleScope handleScope(v8::Isolate::GetCurrent()); registerMockedHttpURLLoad("context_notifications_test.html"); registerMockedHttpURLLoad("context_notifications_test_frame.html"); ContextLifetimeTestWebFrameClient webFrameClient; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "context_notifications_test.html", true, &webFrameClient); // Refresh, we should get two release notifications and two more create notifications. FrameTestHelpers::reloadFrame(webViewHelper.webView()->mainFrame()); ASSERT_EQ(4u, webFrameClient.createNotifications.size()); ASSERT_EQ(2u, webFrameClient.releaseNotifications.size()); // The two release notifications we got should be exactly the same as the first two create notifications. for (size_t i = 0; i < webFrameClient.releaseNotifications.size(); ++i) { EXPECT_TRUE(webFrameClient.releaseNotifications[i]->Equals( webFrameClient.createNotifications[webFrameClient.createNotifications.size() - 3 - i])); } // The last two create notifications should be for the current frames and context. WebFrame* mainFrame = webViewHelper.webView()->mainFrame(); WebFrame* childFrame = mainFrame->firstChild(); ContextLifetimeTestWebFrameClient::Notification* firstRefreshNotification = webFrameClient.createNotifications[2]; ContextLifetimeTestWebFrameClient::Notification* secondRefreshNotification = webFrameClient.createNotifications[3]; EXPECT_EQ(mainFrame, firstRefreshNotification->frame); EXPECT_EQ(mainFrame->mainWorldScriptContext(), firstRefreshNotification->context); EXPECT_EQ(0, firstRefreshNotification->worldId); EXPECT_EQ(childFrame, secondRefreshNotification->frame); EXPECT_EQ(childFrame->mainWorldScriptContext(), secondRefreshNotification->context); EXPECT_EQ(0, secondRefreshNotification->worldId); } TEST_F(WebFrameTest, ContextNotificationsIsolatedWorlds) { v8::Isolate* isolate = v8::Isolate::GetCurrent(); v8::HandleScope handleScope(isolate); registerMockedHttpURLLoad("context_notifications_test.html"); registerMockedHttpURLLoad("context_notifications_test_frame.html"); ContextLifetimeTestWebFrameClient webFrameClient; FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "context_notifications_test.html", true, &webFrameClient); // Add an isolated world. webFrameClient.reset(); int isolatedWorldId = 42; WebScriptSource scriptSource("hi!"); int numSources = 1; int extensionGroup = 0; webViewHelper.webView()->mainFrame()->executeScriptInIsolatedWorld(isolatedWorldId, &scriptSource, numSources, extensionGroup); // We should now have a new create notification. ASSERT_EQ(1u, webFrameClient.createNotifications.size()); ContextLifetimeTestWebFrameClient::Notification* notification = webFrameClient.createNotifications[0]; ASSERT_EQ(isolatedWorldId, notification->worldId); ASSERT_EQ(webViewHelper.webView()->mainFrame(), notification->frame); // We don't have an API to enumarate isolated worlds for a frame, but we can at least assert that the context we got is *not* the main world's context. ASSERT_NE(webViewHelper.webView()->mainFrame()->mainWorldScriptContext(), v8::Local::New(isolate, notification->context)); webViewHelper.reset(); // We should have gotten three release notifications (one for each of the frames, plus one for the isolated context). ASSERT_EQ(3u, webFrameClient.releaseNotifications.size()); // And one of them should be exactly the same as the create notification for the isolated context. int matchCount = 0; for (size_t i = 0; i < webFrameClient.releaseNotifications.size(); ++i) { if (webFrameClient.releaseNotifications[i]->Equals(webFrameClient.createNotifications[0])) ++matchCount; } EXPECT_EQ(1, matchCount); } TEST_F(WebFrameTest, FindInPage) { registerMockedHttpURLLoad("find.html"); FrameTestHelpers::WebViewHelper webViewHelper; webViewHelper.initializeAndLoad(m_baseURL + "find.html"); WebFrame* frame = webViewHelper.webView()->mainFrame(); const int findIdentifier = 12345; WebFindOptions options; // Find in a
element. EXPECT_TRUE(frame->find(findIdentifier, WebString::fromUTF8("bar1"), options, false, 0)); frame->stopFinding(false); WebRange range = frame->selectionRange(); EXPECT_EQ(5, range.startOffset()); EXPECT_EQ(9, range.endOffset()); EXPECT_TRUE(frame->document().focusedElement().isNull()); // Find in an value. EXPECT_TRUE(frame->find(findIdentifier, WebString::fromUTF8("bar2"), options, false, 0)); // Confirm stopFinding(false) sets the selection on the found text. frame->stopFinding(false); range = frame->selectionRange(); ASSERT_FALSE(range.isNull()); EXPECT_EQ(5, range.startOffset()); EXPECT_EQ(9, range.endOffset()); EXPECT_EQ(WebString::fromUTF8("INPUT"), frame->document().focusedElement().tagName()); // Find in a