1/*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32
33#include "public/web/WebFrame.h"
34
35#include "SkBitmap.h"
36#include "SkCanvas.h"
37#include "bindings/core/v8/V8Node.h"
38#include "core/UserAgentStyleSheets.h"
39#include "core/clipboard/DataTransfer.h"
40#include "core/css/StyleSheetContents.h"
41#include "core/css/resolver/StyleResolver.h"
42#include "core/css/resolver/ViewportStyleResolver.h"
43#include "core/dom/DocumentMarkerController.h"
44#include "core/dom/Fullscreen.h"
45#include "core/dom/NodeRenderStyle.h"
46#include "core/dom/Range.h"
47#include "core/editing/Editor.h"
48#include "core/editing/FrameSelection.h"
49#include "core/editing/SpellChecker.h"
50#include "core/editing/VisiblePosition.h"
51#include "core/events/MouseEvent.h"
52#include "core/fetch/MemoryCache.h"
53#include "core/frame/FrameHost.h"
54#include "core/frame/FrameView.h"
55#include "core/frame/LocalFrame.h"
56#include "core/frame/PinchViewport.h"
57#include "core/frame/Settings.h"
58#include "core/html/HTMLDocument.h"
59#include "core/html/HTMLFormElement.h"
60#include "core/loader/DocumentThreadableLoader.h"
61#include "core/loader/DocumentThreadableLoaderClient.h"
62#include "core/loader/FrameLoadRequest.h"
63#include "core/loader/ThreadableLoader.h"
64#include "core/page/EventHandler.h"
65#include "core/page/Page.h"
66#include "core/rendering/HitTestResult.h"
67#include "core/rendering/RenderView.h"
68#include "core/rendering/compositing/RenderLayerCompositor.h"
69#include "core/testing/URLTestHelpers.h"
70#include "platform/DragImage.h"
71#include "platform/RuntimeEnabledFeatures.h"
72#include "platform/UserGestureIndicator.h"
73#include "platform/geometry/FloatRect.h"
74#include "platform/network/ResourceError.h"
75#include "platform/scroll/ScrollbarTheme.h"
76#include "platform/weborigin/SchemeRegistry.h"
77#include "public/platform/Platform.h"
78#include "public/platform/WebFloatRect.h"
79#include "public/platform/WebSelectionBound.h"
80#include "public/platform/WebThread.h"
81#include "public/platform/WebURL.h"
82#include "public/platform/WebURLResponse.h"
83#include "public/platform/WebUnitTestSupport.h"
84#include "public/web/WebCache.h"
85#include "public/web/WebDataSource.h"
86#include "public/web/WebDocument.h"
87#include "public/web/WebFindOptions.h"
88#include "public/web/WebFormElement.h"
89#include "public/web/WebFrameClient.h"
90#include "public/web/WebHistoryItem.h"
91#include "public/web/WebPrintParams.h"
92#include "public/web/WebRange.h"
93#include "public/web/WebRemoteFrame.h"
94#include "public/web/WebScriptSource.h"
95#include "public/web/WebSearchableFormData.h"
96#include "public/web/WebSecurityOrigin.h"
97#include "public/web/WebSecurityPolicy.h"
98#include "public/web/WebSettings.h"
99#include "public/web/WebSpellCheckClient.h"
100#include "public/web/WebTextCheckingCompletion.h"
101#include "public/web/WebTextCheckingResult.h"
102#include "public/web/WebViewClient.h"
103#include "web/WebLocalFrameImpl.h"
104#include "web/WebRemoteFrameImpl.h"
105#include "web/WebViewImpl.h"
106#include "web/tests/FrameTestHelpers.h"
107#include "wtf/Forward.h"
108#include "wtf/dtoa/utils.h"
109#include <gmock/gmock.h>
110#include <gtest/gtest.h>
111#include <map>
112#include <v8.h>
113
114namespace {
115
116using blink::URLTestHelpers::toKURL;
117using blink::FrameTestHelpers::runPendingTasks;
118using namespace blink;
119
120const int touchPointPadding = 32;
121
122#define EXPECT_RECT_EQ(a, b) \
123    do {                                   \
124        EXPECT_EQ(a.x(), b.x());           \
125        EXPECT_EQ(a.y(), b.y());           \
126        EXPECT_EQ(a.width(), b.width());   \
127        EXPECT_EQ(a.height(), b.height()); \
128    } while (false)
129
130#define EXPECT_POINT_EQ(expected, actual) \
131    do { \
132        EXPECT_EQ((expected).x(), (actual).x()); \
133        EXPECT_EQ((expected).y(), (actual).y()); \
134    } while (false)
135
136#define EXPECT_FLOAT_POINT_EQ(expected, actual) \
137    do { \
138        EXPECT_FLOAT_EQ((expected).x(), (actual).x()); \
139        EXPECT_FLOAT_EQ((expected).y(), (actual).y()); \
140    } while (false)
141
142#define EXPECT_EQ_POINT(a, b) \
143    EXPECT_EQ(a.x(), b.x()); \
144    EXPECT_EQ(a.y(), b.y());
145
146class FakeCompositingWebViewClient : public FrameTestHelpers::TestWebViewClient {
147public:
148    virtual bool enterFullScreen() OVERRIDE { return true; }
149};
150
151class WebFrameTest : public testing::Test {
152protected:
153    WebFrameTest()
154        : m_baseURL("http://www.test.com/")
155        , m_chromeURL("chrome://")
156    {
157    }
158
159    virtual ~WebFrameTest()
160    {
161        Platform::current()->unitTestSupport()->unregisterAllMockedURLs();
162    }
163
164    void registerMockedHttpURLLoad(const std::string& fileName)
165    {
166        URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8(fileName.c_str()));
167    }
168
169    void registerMockedChromeURLLoad(const std::string& fileName)
170    {
171        URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_chromeURL.c_str()), WebString::fromUTF8(fileName.c_str()));
172    }
173
174    void applyViewportStyleOverride(FrameTestHelpers::WebViewHelper* webViewHelper)
175    {
176        RefPtrWillBeRawPtr<StyleSheetContents> styleSheet = StyleSheetContents::create(CSSParserContext(UASheetMode, 0));
177        styleSheet->parseString(String(blink::viewportAndroidCss, sizeof(blink::viewportAndroidCss)));
178        OwnPtrWillBeRawPtr<RuleSet> ruleSet = RuleSet::create();
179        ruleSet->addRulesFromSheet(styleSheet.get(), MediaQueryEvaluator("screen"));
180
181        Document* document = toLocalFrame(webViewHelper->webViewImpl()->page()->mainFrame())->document();
182        document->ensureStyleResolver().viewportStyleResolver()->collectViewportRules(ruleSet.get(), ViewportStyleResolver::UserAgentOrigin);
183        document->ensureStyleResolver().viewportStyleResolver()->resolve();
184    }
185
186    static void configueCompositingWebView(WebSettings* settings)
187    {
188        settings->setAcceleratedCompositingEnabled(true);
189        settings->setPreferCompositingToLCDTextEnabled(true);
190    }
191
192    static void configureLoadsImagesAutomatically(WebSettings* settings)
193    {
194        settings->setLoadsImagesAutomatically(true);
195    }
196
197    void initializeTextSelectionWebView(const std::string& url, FrameTestHelpers::WebViewHelper* webViewHelper)
198    {
199        webViewHelper->initializeAndLoad(url, true);
200        webViewHelper->webView()->settings()->setDefaultFontSize(12);
201        webViewHelper->webView()->resize(WebSize(640, 480));
202    }
203
204    PassOwnPtr<DragImage> nodeImageTestSetup(FrameTestHelpers::WebViewHelper* webViewHelper, const std::string& testcase)
205    {
206        registerMockedHttpURLLoad("nodeimage.html");
207        webViewHelper->initializeAndLoad(m_baseURL + "nodeimage.html");
208        webViewHelper->webView()->resize(WebSize(640, 480));
209        webViewHelper->webView()->layout();
210        RefPtrWillBeRawPtr<LocalFrame> frame = toLocalFrame(webViewHelper->webViewImpl()->page()->mainFrame());
211        ASSERT(frame);
212        Element* element = frame->document()->getElementById(testcase.c_str());
213        return frame->nodeImage(*element);
214    }
215
216    std::string m_baseURL;
217    std::string m_chromeURL;
218};
219
220class UseMockScrollbarSettings {
221public:
222    UseMockScrollbarSettings()
223    {
224        Settings::setMockScrollbarsEnabled(true);
225        RuntimeEnabledFeatures::setOverlayScrollbarsEnabled(true);
226        EXPECT_TRUE(ScrollbarTheme::theme()->usesOverlayScrollbars());
227    }
228
229    ~UseMockScrollbarSettings()
230    {
231        Settings::setMockScrollbarsEnabled(false);
232        RuntimeEnabledFeatures::setOverlayScrollbarsEnabled(false);
233    }
234};
235
236TEST_F(WebFrameTest, ContentText)
237{
238    registerMockedHttpURLLoad("iframes_test.html");
239    registerMockedHttpURLLoad("visible_iframe.html");
240    registerMockedHttpURLLoad("invisible_iframe.html");
241    registerMockedHttpURLLoad("zero_sized_iframe.html");
242
243    FrameTestHelpers::WebViewHelper webViewHelper;
244    webViewHelper.initializeAndLoad(m_baseURL + "iframes_test.html");
245
246    // Now retrieve the frames text and test it only includes visible elements.
247    std::string content = webViewHelper.webView()->mainFrame()->contentAsText(1024).utf8();
248    EXPECT_NE(std::string::npos, content.find(" visible paragraph"));
249    EXPECT_NE(std::string::npos, content.find(" visible iframe"));
250    EXPECT_EQ(std::string::npos, content.find(" invisible pararaph"));
251    EXPECT_EQ(std::string::npos, content.find(" invisible iframe"));
252    EXPECT_EQ(std::string::npos, content.find("iframe with zero size"));
253}
254
255TEST_F(WebFrameTest, FrameForEnteredContext)
256{
257    registerMockedHttpURLLoad("iframes_test.html");
258    registerMockedHttpURLLoad("visible_iframe.html");
259    registerMockedHttpURLLoad("invisible_iframe.html");
260    registerMockedHttpURLLoad("zero_sized_iframe.html");
261
262    FrameTestHelpers::WebViewHelper webViewHelper;
263    webViewHelper.initializeAndLoad(m_baseURL + "iframes_test.html", true);
264
265    v8::HandleScope scope(v8::Isolate::GetCurrent());
266    EXPECT_EQ(webViewHelper.webView()->mainFrame(), WebLocalFrame::frameForContext(webViewHelper.webView()->mainFrame()->mainWorldScriptContext()));
267    EXPECT_EQ(webViewHelper.webView()->mainFrame()->firstChild(), WebLocalFrame::frameForContext(webViewHelper.webView()->mainFrame()->firstChild()->mainWorldScriptContext()));
268}
269
270TEST_F(WebFrameTest, FormWithNullFrame)
271{
272    registerMockedHttpURLLoad("form.html");
273
274    FrameTestHelpers::WebViewHelper webViewHelper;
275    webViewHelper.initializeAndLoad(m_baseURL + "form.html");
276
277    WebVector<WebFormElement> forms;
278    webViewHelper.webView()->mainFrame()->document().forms(forms);
279    webViewHelper.reset();
280
281    EXPECT_EQ(forms.size(), 1U);
282
283    // This test passes if this doesn't crash.
284    WebSearchableFormData searchableDataForm(forms[0]);
285}
286
287TEST_F(WebFrameTest, ChromePageJavascript)
288{
289    registerMockedChromeURLLoad("history.html");
290
291    // Pass true to enable JavaScript.
292    FrameTestHelpers::WebViewHelper webViewHelper;
293    webViewHelper.initializeAndLoad(m_chromeURL + "history.html", true);
294
295    // Try to run JS against the chrome-style URL.
296    FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), "javascript:document.body.appendChild(document.createTextNode('Clobbered'))");
297
298    // Required to see any updates in contentAsText.
299    webViewHelper.webView()->layout();
300
301    // Now retrieve the frame's text and ensure it was modified by running javascript.
302    std::string content = webViewHelper.webView()->mainFrame()->contentAsText(1024).utf8();
303    EXPECT_NE(std::string::npos, content.find("Clobbered"));
304}
305
306TEST_F(WebFrameTest, ChromePageNoJavascript)
307{
308    registerMockedChromeURLLoad("history.html");
309
310    /// Pass true to enable JavaScript.
311    FrameTestHelpers::WebViewHelper webViewHelper;
312    webViewHelper.initializeAndLoad(m_chromeURL + "history.html", true);
313
314    // Try to run JS against the chrome-style URL after prohibiting it.
315    WebSecurityPolicy::registerURLSchemeAsNotAllowingJavascriptURLs("chrome");
316    FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), "javascript:document.body.appendChild(document.createTextNode('Clobbered'))");
317
318    // Required to see any updates in contentAsText.
319    webViewHelper.webView()->layout();
320
321    // Now retrieve the frame's text and ensure it wasn't modified by running javascript.
322    std::string content = webViewHelper.webView()->mainFrame()->contentAsText(1024).utf8();
323    EXPECT_EQ(std::string::npos, content.find("Clobbered"));
324}
325
326TEST_F(WebFrameTest, LocationSetHostWithMissingPort)
327{
328    std::string fileName = "print-location-href.html";
329    registerMockedHttpURLLoad(fileName);
330    URLTestHelpers::registerMockedURLLoad(toKURL("http://www.test.com:0/" + fileName), WebString::fromUTF8(fileName));
331
332    FrameTestHelpers::WebViewHelper webViewHelper;
333
334    /// Pass true to enable JavaScript.
335    webViewHelper.initializeAndLoad(m_baseURL + fileName, true);
336
337    // Setting host to "hostname:" should be treated as "hostname:0".
338    FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), "javascript:location.host = 'www.test.com:'; void 0;");
339
340    FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), "javascript:document.body.textContent = location.href; void 0;");
341
342    std::string content = webViewHelper.webView()->mainFrame()->contentAsText(1024).utf8();
343    EXPECT_EQ("http://www.test.com:0/" + fileName, content);
344}
345
346TEST_F(WebFrameTest, LocationSetEmptyPort)
347{
348    std::string fileName = "print-location-href.html";
349    registerMockedHttpURLLoad(fileName);
350    URLTestHelpers::registerMockedURLLoad(toKURL("http://www.test.com:0/" + fileName), WebString::fromUTF8(fileName));
351
352    FrameTestHelpers::WebViewHelper webViewHelper;
353
354    /// Pass true to enable JavaScript.
355    webViewHelper.initializeAndLoad(m_baseURL + fileName, true);
356
357    FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), "javascript:location.port = ''; void 0;");
358
359    FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), "javascript:document.body.textContent = location.href; void 0;");
360
361    std::string content = webViewHelper.webView()->mainFrame()->contentAsText(1024).utf8();
362    EXPECT_EQ("http://www.test.com:0/" + fileName, content);
363}
364
365class CSSCallbackWebFrameClient : public FrameTestHelpers::TestWebFrameClient {
366public:
367    CSSCallbackWebFrameClient() : m_updateCount(0) { }
368    virtual void didMatchCSS(WebLocalFrame*, const WebVector<WebString>& newlyMatchingSelectors, const WebVector<WebString>& stoppedMatchingSelectors) OVERRIDE;
369
370    std::map<WebLocalFrame*, std::set<std::string> > m_matchedSelectors;
371    int m_updateCount;
372};
373
374void CSSCallbackWebFrameClient::didMatchCSS(WebLocalFrame* frame, const WebVector<WebString>& newlyMatchingSelectors, const WebVector<WebString>& stoppedMatchingSelectors)
375{
376    ++m_updateCount;
377    std::set<std::string>& frameSelectors = m_matchedSelectors[frame];
378    for (size_t i = 0; i < newlyMatchingSelectors.size(); ++i) {
379        std::string selector = newlyMatchingSelectors[i].utf8();
380        EXPECT_EQ(0U, frameSelectors.count(selector)) << selector;
381        frameSelectors.insert(selector);
382    }
383    for (size_t i = 0; i < stoppedMatchingSelectors.size(); ++i) {
384        std::string selector = stoppedMatchingSelectors[i].utf8();
385        EXPECT_EQ(1U, frameSelectors.count(selector)) << selector;
386        frameSelectors.erase(selector);
387    }
388}
389
390class WebFrameCSSCallbackTest : public testing::Test {
391protected:
392    WebFrameCSSCallbackTest()
393    {
394
395        m_frame = m_helper.initializeAndLoad("about:blank", true, &m_client)->mainFrame()->toWebLocalFrame();
396    }
397
398    ~WebFrameCSSCallbackTest()
399    {
400        EXPECT_EQ(1U, m_client.m_matchedSelectors.size());
401    }
402
403    WebDocument doc() const
404    {
405        return m_frame->document();
406    }
407
408    int updateCount() const
409    {
410        return m_client.m_updateCount;
411    }
412
413    const std::set<std::string>& matchedSelectors()
414    {
415        return m_client.m_matchedSelectors[m_frame];
416    }
417
418    void loadHTML(const std::string& html)
419    {
420        FrameTestHelpers::loadHTMLString(m_frame, html, toKURL("about:blank"));
421    }
422
423    void executeScript(const WebString& code)
424    {
425        m_frame->executeScript(WebScriptSource(code));
426        m_frame->view()->layout();
427        runPendingTasks();
428    }
429
430    CSSCallbackWebFrameClient m_client;
431    FrameTestHelpers::WebViewHelper m_helper;
432    WebLocalFrame* m_frame;
433};
434
435TEST_F(WebFrameCSSCallbackTest, AuthorStyleSheet)
436{
437    loadHTML(
438        "<style>"
439        // This stylesheet checks that the internal property and value can't be
440        // set by a stylesheet, only WebDocument::watchCSSSelectors().
441        "div.initial_on { -internal-callback: none; }"
442        "div.initial_off { -internal-callback: -internal-presence; }"
443        "</style>"
444        "<div class=\"initial_on\"></div>"
445        "<div class=\"initial_off\"></div>");
446
447    std::vector<WebString> selectors;
448    selectors.push_back(WebString::fromUTF8("div.initial_on"));
449    m_frame->document().watchCSSSelectors(WebVector<WebString>(selectors));
450    m_frame->view()->layout();
451    runPendingTasks();
452    EXPECT_EQ(1, updateCount());
453    EXPECT_THAT(matchedSelectors(), testing::ElementsAre("div.initial_on"));
454
455    // Check that adding a watched selector calls back for already-present nodes.
456    selectors.push_back(WebString::fromUTF8("div.initial_off"));
457    doc().watchCSSSelectors(WebVector<WebString>(selectors));
458    m_frame->view()->layout();
459    runPendingTasks();
460    EXPECT_EQ(2, updateCount());
461    EXPECT_THAT(matchedSelectors(), testing::ElementsAre("div.initial_off", "div.initial_on"));
462
463    // Check that we can turn off callbacks for certain selectors.
464    doc().watchCSSSelectors(WebVector<WebString>());
465    m_frame->view()->layout();
466    runPendingTasks();
467    EXPECT_EQ(3, updateCount());
468    EXPECT_THAT(matchedSelectors(), testing::ElementsAre());
469}
470
471TEST_F(WebFrameCSSCallbackTest, SharedRenderStyle)
472{
473    // Check that adding an element calls back when it matches an existing rule.
474    std::vector<WebString> selectors;
475    selectors.push_back(WebString::fromUTF8("span"));
476    doc().watchCSSSelectors(WebVector<WebString>(selectors));
477
478    executeScript(
479        "i1 = document.createElement('span');"
480        "i1.id = 'first_span';"
481        "document.body.appendChild(i1)");
482    EXPECT_EQ(1, updateCount());
483    EXPECT_THAT(matchedSelectors(), testing::ElementsAre("span"));
484
485    // Adding a second element that shares a RenderStyle shouldn't call back.
486    // We use <span>s to avoid default style rules that can set
487    // RenderStyle::unique().
488    executeScript(
489        "i2 = document.createElement('span');"
490        "i2.id = 'second_span';"
491        "i1 = document.getElementById('first_span');"
492        "i1.parentNode.insertBefore(i2, i1.nextSibling);");
493    EXPECT_EQ(1, updateCount());
494    EXPECT_THAT(matchedSelectors(), testing::ElementsAre("span"));
495
496    // Removing the first element shouldn't call back.
497    executeScript(
498        "i1 = document.getElementById('first_span');"
499        "i1.parentNode.removeChild(i1);");
500    EXPECT_EQ(1, updateCount());
501    EXPECT_THAT(matchedSelectors(), testing::ElementsAre("span"));
502
503    // But removing the second element *should* call back.
504    executeScript(
505        "i2 = document.getElementById('second_span');"
506        "i2.parentNode.removeChild(i2);");
507    EXPECT_EQ(2, updateCount());
508    EXPECT_THAT(matchedSelectors(), testing::ElementsAre());
509}
510
511TEST_F(WebFrameCSSCallbackTest, CatchesAttributeChange)
512{
513    loadHTML("<span></span>");
514
515    std::vector<WebString> selectors;
516    selectors.push_back(WebString::fromUTF8("span[attr=\"value\"]"));
517    doc().watchCSSSelectors(WebVector<WebString>(selectors));
518    runPendingTasks();
519
520    EXPECT_EQ(0, updateCount());
521    EXPECT_THAT(matchedSelectors(), testing::ElementsAre());
522
523    executeScript(
524        "document.querySelector('span').setAttribute('attr', 'value');");
525    EXPECT_EQ(1, updateCount());
526    EXPECT_THAT(matchedSelectors(), testing::ElementsAre("span[attr=\"value\"]"));
527}
528
529TEST_F(WebFrameCSSCallbackTest, DisplayNone)
530{
531    loadHTML("<div style='display:none'><span></span></div>");
532
533    std::vector<WebString> selectors;
534    selectors.push_back(WebString::fromUTF8("span"));
535    doc().watchCSSSelectors(WebVector<WebString>(selectors));
536    runPendingTasks();
537
538    EXPECT_EQ(0, updateCount()) << "Don't match elements in display:none trees.";
539
540    executeScript(
541        "d = document.querySelector('div');"
542        "d.style.display = 'block';");
543    EXPECT_EQ(1, updateCount()) << "Match elements when they become displayed.";
544    EXPECT_THAT(matchedSelectors(), testing::ElementsAre("span"));
545
546    executeScript(
547        "d = document.querySelector('div');"
548        "d.style.display = 'none';");
549    EXPECT_EQ(2, updateCount()) << "Unmatch elements when they become undisplayed.";
550    EXPECT_THAT(matchedSelectors(), testing::ElementsAre());
551
552    executeScript(
553        "s = document.querySelector('span');"
554        "s.style.display = 'none';");
555    EXPECT_EQ(2, updateCount()) << "No effect from no-display'ing a span that's already undisplayed.";
556
557    executeScript(
558        "d = document.querySelector('div');"
559        "d.style.display = 'block';");
560    EXPECT_EQ(2, updateCount()) << "No effect from displaying a div whose span is display:none.";
561
562    executeScript(
563        "s = document.querySelector('span');"
564        "s.style.display = 'inline';");
565    EXPECT_EQ(3, updateCount()) << "Now the span is visible and produces a callback.";
566    EXPECT_THAT(matchedSelectors(), testing::ElementsAre("span"));
567
568    executeScript(
569        "s = document.querySelector('span');"
570        "s.style.display = 'none';");
571    EXPECT_EQ(4, updateCount()) << "Undisplaying the span directly should produce another callback.";
572    EXPECT_THAT(matchedSelectors(), testing::ElementsAre());
573}
574
575TEST_F(WebFrameCSSCallbackTest, Reparenting)
576{
577    loadHTML(
578        "<div id='d1'><span></span></div>"
579        "<div id='d2'></div>");
580
581    std::vector<WebString> selectors;
582    selectors.push_back(WebString::fromUTF8("span"));
583    doc().watchCSSSelectors(WebVector<WebString>(selectors));
584    m_frame->view()->layout();
585    runPendingTasks();
586
587    EXPECT_EQ(1, updateCount());
588    EXPECT_THAT(matchedSelectors(), testing::ElementsAre("span"));
589
590    executeScript(
591        "s = document.querySelector('span');"
592        "d2 = document.getElementById('d2');"
593        "d2.appendChild(s);");
594    EXPECT_EQ(1, updateCount()) << "Just moving an element that continues to match shouldn't send a spurious callback.";
595    EXPECT_THAT(matchedSelectors(), testing::ElementsAre("span"));
596}
597
598TEST_F(WebFrameCSSCallbackTest, MultiSelector)
599{
600    loadHTML("<span></span>");
601
602    // Check that selector lists match as the whole list, not as each element
603    // independently.
604    std::vector<WebString> selectors;
605    selectors.push_back(WebString::fromUTF8("span"));
606    selectors.push_back(WebString::fromUTF8("span,p"));
607    doc().watchCSSSelectors(WebVector<WebString>(selectors));
608    m_frame->view()->layout();
609    runPendingTasks();
610
611    EXPECT_EQ(1, updateCount());
612    EXPECT_THAT(matchedSelectors(), testing::ElementsAre("span", "span, p"));
613}
614
615TEST_F(WebFrameCSSCallbackTest, InvalidSelector)
616{
617    loadHTML("<p><span></span></p>");
618
619    // Build a list with one valid selector and one invalid.
620    std::vector<WebString> selectors;
621    selectors.push_back(WebString::fromUTF8("span"));
622    selectors.push_back(WebString::fromUTF8("[")); // Invalid.
623    selectors.push_back(WebString::fromUTF8("p span")); // Not compound.
624    doc().watchCSSSelectors(WebVector<WebString>(selectors));
625    m_frame->view()->layout();
626    runPendingTasks();
627
628    EXPECT_EQ(1, updateCount());
629    EXPECT_THAT(matchedSelectors(), testing::ElementsAre("span"))
630        << "An invalid selector shouldn't prevent other selectors from matching.";
631}
632
633TEST_F(WebFrameTest, DispatchMessageEventWithOriginCheck)
634{
635    registerMockedHttpURLLoad("postmessage_test.html");
636
637    // Pass true to enable JavaScript.
638    FrameTestHelpers::WebViewHelper webViewHelper;
639    webViewHelper.initializeAndLoad(m_baseURL + "postmessage_test.html", true);
640
641    // Send a message with the correct origin.
642    WebSecurityOrigin correctOrigin(WebSecurityOrigin::create(toKURL(m_baseURL)));
643    WebDOMEvent event = webViewHelper.webView()->mainFrame()->document().createEvent("MessageEvent");
644    WebDOMMessageEvent message = event.to<WebDOMMessageEvent>();
645    WebSerializedScriptValue data(WebSerializedScriptValue::fromString("foo"));
646    message.initMessageEvent("message", false, false, data, "http://origin.com", 0, "");
647    webViewHelper.webView()->mainFrame()->dispatchMessageEventWithOriginCheck(correctOrigin, message);
648
649    // Send another message with incorrect origin.
650    WebSecurityOrigin incorrectOrigin(WebSecurityOrigin::create(toKURL(m_chromeURL)));
651    webViewHelper.webView()->mainFrame()->dispatchMessageEventWithOriginCheck(incorrectOrigin, message);
652
653    // Required to see any updates in contentAsText.
654    webViewHelper.webView()->layout();
655
656    // Verify that only the first addition is in the body of the page.
657    std::string content = webViewHelper.webView()->mainFrame()->contentAsText(1024).utf8();
658    EXPECT_NE(std::string::npos, content.find("Message 1."));
659    EXPECT_EQ(std::string::npos, content.find("Message 2."));
660}
661
662TEST_F(WebFrameTest, PostMessageThenDetach)
663{
664    FrameTestHelpers::WebViewHelper webViewHelper;
665    webViewHelper.initializeAndLoad("about:blank");
666
667    RefPtrWillBeRawPtr<LocalFrame> frame = toLocalFrame(webViewHelper.webViewImpl()->page()->mainFrame());
668    NonThrowableExceptionState exceptionState;
669    frame->domWindow()->postMessage(SerializedScriptValue::create("message"), 0, "*", frame->domWindow(), exceptionState);
670    webViewHelper.reset();
671    EXPECT_FALSE(exceptionState.hadException());
672
673    // Success is not crashing.
674    runPendingTasks();
675}
676
677class FixedLayoutTestWebViewClient : public FrameTestHelpers::TestWebViewClient {
678 public:
679    virtual WebScreenInfo screenInfo() OVERRIDE { return m_screenInfo; }
680
681    WebScreenInfo m_screenInfo;
682};
683
684// Viewport settings need to be set before the page gets loaded
685static void enableViewportSettings(WebSettings* settings)
686{
687    settings->setViewportMetaEnabled(true);
688    settings->setViewportEnabled(true);
689    settings->setMainFrameResizesAreOrientationChanges(true);
690    settings->setShrinksViewportContentToFit(true);
691}
692
693// Helper function to check or set text autosizing multipliers on a document.
694static bool checkOrSetTextAutosizingMultiplier(Document* document, float multiplier, bool setMultiplier)
695{
696    bool multiplierCheckedOrSetAtLeastOnce = false;
697    for (RenderObject* renderer = document->renderView(); renderer; renderer = renderer->nextInPreOrder()) {
698        if (renderer->style()) {
699            if (setMultiplier)
700                renderer->style()->setTextAutosizingMultiplier(multiplier);
701            EXPECT_EQ(multiplier, renderer->style()->textAutosizingMultiplier());
702            multiplierCheckedOrSetAtLeastOnce = true;
703        }
704    }
705    return multiplierCheckedOrSetAtLeastOnce;
706
707}
708
709static bool setTextAutosizingMultiplier(Document* document, float multiplier)
710{
711    return checkOrSetTextAutosizingMultiplier(document, multiplier, true);
712}
713
714static bool checkTextAutosizingMultiplier(Document* document, float multiplier)
715{
716    return checkOrSetTextAutosizingMultiplier(document, multiplier, false);
717}
718
719TEST_F(WebFrameTest, ChangeInFixedLayoutResetsTextAutosizingMultipliers)
720{
721    UseMockScrollbarSettings mockScrollbarSettings;
722    registerMockedHttpURLLoad("fixed_layout.html");
723
724    FixedLayoutTestWebViewClient client;
725    int viewportWidth = 640;
726    int viewportHeight = 480;
727
728    FrameTestHelpers::WebViewHelper webViewHelper;
729    webViewHelper.initializeAndLoad(m_baseURL + "fixed_layout.html", true, 0, &client, enableViewportSettings);
730
731    Document* document = toLocalFrame(webViewHelper.webViewImpl()->page()->mainFrame())->document();
732    document->settings()->setTextAutosizingEnabled(true);
733    EXPECT_TRUE(document->settings()->textAutosizingEnabled());
734    webViewHelper.webViewImpl()->resize(WebSize(viewportWidth, viewportHeight));
735    webViewHelper.webViewImpl()->layout();
736
737    EXPECT_TRUE(setTextAutosizingMultiplier(document, 2));
738
739    ViewportDescription description = document->viewportDescription();
740    // Choose a width that's not going match the viewport width of the loaded document.
741    description.minWidth = Length(100, blink::Fixed);
742    description.maxWidth = Length(100, blink::Fixed);
743    webViewHelper.webViewImpl()->updatePageDefinedViewportConstraints(description);
744
745    EXPECT_TRUE(checkTextAutosizingMultiplier(document, 1));
746}
747
748TEST_F(WebFrameTest, SetFrameRectInvalidatesTextAutosizingMultipliers)
749{
750    UseMockScrollbarSettings mockScrollbarSettings;
751    registerMockedHttpURLLoad("iframe_reload.html");
752    registerMockedHttpURLLoad("visible_iframe.html");
753
754    FixedLayoutTestWebViewClient client;
755    int viewportWidth = 640;
756    int viewportHeight = 480;
757
758    FrameTestHelpers::WebViewHelper webViewHelper;
759    webViewHelper.initializeAndLoad(m_baseURL + "iframe_reload.html", true, 0, &client, enableViewportSettings);
760
761    LocalFrame* mainFrame = toLocalFrame(webViewHelper.webViewImpl()->page()->mainFrame());
762    Document* document = mainFrame->document();
763    FrameView* frameView = webViewHelper.webViewImpl()->mainFrameImpl()->frameView();
764    document->settings()->setTextAutosizingEnabled(true);
765    EXPECT_TRUE(document->settings()->textAutosizingEnabled());
766    webViewHelper.webViewImpl()->resize(WebSize(viewportWidth, viewportHeight));
767    webViewHelper.webViewImpl()->layout();
768
769    for (Frame* frame = mainFrame; frame; frame = frame->tree().traverseNext()) {
770        if (!frame->isLocalFrame())
771            continue;
772        EXPECT_TRUE(setTextAutosizingMultiplier(toLocalFrame(frame)->document(), 2));
773        for (RenderObject* renderer = toLocalFrame(frame)->document()->renderView(); renderer; renderer = renderer->nextInPreOrder()) {
774            if (renderer->isText())
775                EXPECT_FALSE(renderer->needsLayout());
776        }
777    }
778
779    frameView->setFrameRect(IntRect(0, 0, 200, 200));
780    for (Frame* frame = mainFrame; frame; frame = frame->tree().traverseNext()) {
781        if (!frame->isLocalFrame())
782            continue;
783        for (RenderObject* renderer = toLocalFrame(frame)->document()->renderView(); renderer; renderer = renderer->nextInPreOrder()) {
784            if (renderer->isText())
785                EXPECT_TRUE(renderer->needsLayout());
786        }
787    }
788}
789
790TEST_F(WebFrameTest, ZeroHeightPositiveWidthNotIgnored)
791{
792    UseMockScrollbarSettings mockScrollbarSettings;
793
794    FixedLayoutTestWebViewClient client;
795    client.m_screenInfo.deviceScaleFactor = 1;
796    int viewportWidth = 1280;
797    int viewportHeight = 0;
798
799    FrameTestHelpers::WebViewHelper webViewHelper;
800    webViewHelper.initialize(true, 0, &client, enableViewportSettings);
801    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
802
803    EXPECT_EQ(viewportWidth, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width());
804    EXPECT_EQ(viewportHeight, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height());
805}
806
807TEST_F(WebFrameTest, DeviceScaleFactorUsesDefaultWithoutViewportTag)
808{
809    UseMockScrollbarSettings mockScrollbarSettings;
810    registerMockedHttpURLLoad("no_viewport_tag.html");
811
812    int viewportWidth = 640;
813    int viewportHeight = 480;
814
815    FixedLayoutTestWebViewClient client;
816    client.m_screenInfo.deviceScaleFactor = 2;
817
818    FrameTestHelpers::WebViewHelper webViewHelper;
819    webViewHelper.initializeAndLoad(m_baseURL + "no_viewport_tag.html", true, 0, &client, enableViewportSettings);
820
821    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
822    webViewHelper.webView()->layout();
823
824    EXPECT_EQ(2, webViewHelper.webView()->deviceScaleFactor());
825
826    // Device scale factor should be independent of page scale.
827    webViewHelper.webView()->setPageScaleFactorLimits(1, 2);
828    webViewHelper.webView()->setPageScaleFactor(0.5);
829    webViewHelper.webView()->layout();
830    EXPECT_EQ(1, webViewHelper.webView()->pageScaleFactor());
831
832    // Force the layout to happen before leaving the test.
833    webViewHelper.webView()->mainFrame()->contentAsText(1024).utf8();
834}
835
836TEST_F(WebFrameTest, FixedLayoutInitializeAtMinimumScale)
837{
838    UseMockScrollbarSettings mockScrollbarSettings;
839
840    registerMockedHttpURLLoad("fixed_layout.html");
841
842    FixedLayoutTestWebViewClient client;
843    client.m_screenInfo.deviceScaleFactor = 1;
844    int viewportWidth = 640;
845    int viewportHeight = 480;
846
847    // Make sure we initialize to minimum scale, even if the window size
848    // only becomes available after the load begins.
849    FrameTestHelpers::WebViewHelper webViewHelper;
850    webViewHelper.initializeAndLoad(m_baseURL + "fixed_layout.html", true, 0, &client, enableViewportSettings);
851    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
852
853    int defaultFixedLayoutWidth = 980;
854    float minimumPageScaleFactor = viewportWidth / (float) defaultFixedLayoutWidth;
855    EXPECT_EQ(minimumPageScaleFactor, webViewHelper.webView()->pageScaleFactor());
856    EXPECT_EQ(minimumPageScaleFactor, webViewHelper.webView()->minimumPageScaleFactor());
857
858    // Assume the user has pinch zoomed to page scale factor 2.
859    float userPinchPageScaleFactor = 2;
860    webViewHelper.webView()->setPageScaleFactor(userPinchPageScaleFactor);
861    webViewHelper.webView()->layout();
862
863    // Make sure we don't reset to initial scale if the page continues to load.
864    webViewHelper.webViewImpl()->didCommitLoad(false, false);
865    webViewHelper.webViewImpl()->didChangeContentsSize();
866    EXPECT_EQ(userPinchPageScaleFactor, webViewHelper.webView()->pageScaleFactor());
867
868    // Make sure we don't reset to initial scale if the viewport size changes.
869    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight + 100));
870    EXPECT_EQ(userPinchPageScaleFactor, webViewHelper.webView()->pageScaleFactor());
871}
872
873TEST_F(WebFrameTest, WideDocumentInitializeAtMinimumScale)
874{
875    UseMockScrollbarSettings mockScrollbarSettings;
876
877    registerMockedHttpURLLoad("wide_document.html");
878
879    FixedLayoutTestWebViewClient client;
880    client.m_screenInfo.deviceScaleFactor = 1;
881    int viewportWidth = 640;
882    int viewportHeight = 480;
883
884    // Make sure we initialize to minimum scale, even if the window size
885    // only becomes available after the load begins.
886    FrameTestHelpers::WebViewHelper webViewHelper;
887    webViewHelper.initializeAndLoad(m_baseURL + "wide_document.html", true, 0, &client, enableViewportSettings);
888    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
889
890    int wideDocumentWidth = 1500;
891    float minimumPageScaleFactor = viewportWidth / (float) wideDocumentWidth;
892    EXPECT_EQ(minimumPageScaleFactor, webViewHelper.webView()->pageScaleFactor());
893    EXPECT_EQ(minimumPageScaleFactor, webViewHelper.webView()->minimumPageScaleFactor());
894
895    // Assume the user has pinch zoomed to page scale factor 2.
896    float userPinchPageScaleFactor = 2;
897    webViewHelper.webView()->setPageScaleFactor(userPinchPageScaleFactor);
898    webViewHelper.webView()->layout();
899
900    // Make sure we don't reset to initial scale if the page continues to load.
901    webViewHelper.webViewImpl()->didCommitLoad(false, false);
902    webViewHelper.webViewImpl()->didChangeContentsSize();
903    EXPECT_EQ(userPinchPageScaleFactor, webViewHelper.webView()->pageScaleFactor());
904
905    // Make sure we don't reset to initial scale if the viewport size changes.
906    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight + 100));
907    EXPECT_EQ(userPinchPageScaleFactor, webViewHelper.webView()->pageScaleFactor());
908}
909
910TEST_F(WebFrameTest, DelayedViewportInitialScale)
911{
912    UseMockScrollbarSettings mockScrollbarSettings;
913    registerMockedHttpURLLoad("viewport-auto-initial-scale.html");
914
915    FixedLayoutTestWebViewClient client;
916    client.m_screenInfo.deviceScaleFactor = 1;
917    int viewportWidth = 640;
918    int viewportHeight = 480;
919
920    FrameTestHelpers::WebViewHelper webViewHelper;
921    webViewHelper.initializeAndLoad(m_baseURL + "viewport-auto-initial-scale.html", true, 0, &client, enableViewportSettings);
922    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
923
924    EXPECT_EQ(0.25f, webViewHelper.webView()->pageScaleFactor());
925
926    Document* document = toLocalFrame(webViewHelper.webViewImpl()->page()->mainFrame())->document();
927    ViewportDescription description = document->viewportDescription();
928    description.zoom = 2;
929    document->setViewportDescription(description);
930    webViewHelper.webView()->layout();
931    EXPECT_EQ(2, webViewHelper.webView()->pageScaleFactor());
932}
933
934TEST_F(WebFrameTest, setLoadWithOverviewModeToFalse)
935{
936    UseMockScrollbarSettings mockScrollbarSettings;
937    registerMockedHttpURLLoad("viewport-auto-initial-scale.html");
938
939    FixedLayoutTestWebViewClient client;
940    client.m_screenInfo.deviceScaleFactor = 1;
941    int viewportWidth = 640;
942    int viewportHeight = 480;
943
944    FrameTestHelpers::WebViewHelper webViewHelper;
945    webViewHelper.initializeAndLoad(m_baseURL + "viewport-auto-initial-scale.html", true, 0, &client, enableViewportSettings);
946    webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true);
947    webViewHelper.webView()->settings()->setLoadWithOverviewMode(false);
948    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
949
950    // The page must be displayed at 100% zoom.
951    EXPECT_EQ(1.0f, webViewHelper.webView()->pageScaleFactor());
952}
953
954TEST_F(WebFrameTest, SetLoadWithOverviewModeToFalseAndNoWideViewport)
955{
956    UseMockScrollbarSettings mockScrollbarSettings;
957    registerMockedHttpURLLoad("large-div.html");
958
959    FixedLayoutTestWebViewClient client;
960    client.m_screenInfo.deviceScaleFactor = 1;
961    int viewportWidth = 640;
962    int viewportHeight = 480;
963
964    FrameTestHelpers::WebViewHelper webViewHelper;
965    webViewHelper.initializeAndLoad(m_baseURL + "large-div.html", true, 0, &client, enableViewportSettings);
966    webViewHelper.webView()->settings()->setLoadWithOverviewMode(false);
967    webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true);
968    webViewHelper.webView()->settings()->setUseWideViewport(false);
969    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
970
971    // The page must be displayed at 100% zoom, despite that it hosts a wide div element.
972    EXPECT_EQ(1.0f, webViewHelper.webView()->pageScaleFactor());
973}
974
975TEST_F(WebFrameTest, NoWideViewportIgnoresPageViewportWidth)
976{
977    UseMockScrollbarSettings mockScrollbarSettings;
978    registerMockedHttpURLLoad("viewport-auto-initial-scale.html");
979
980    FixedLayoutTestWebViewClient client;
981    client.m_screenInfo.deviceScaleFactor = 1;
982    int viewportWidth = 640;
983    int viewportHeight = 480;
984
985    FrameTestHelpers::WebViewHelper webViewHelper;
986    webViewHelper.initializeAndLoad(m_baseURL + "viewport-auto-initial-scale.html", true, 0, &client, enableViewportSettings);
987    webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true);
988    webViewHelper.webView()->settings()->setUseWideViewport(false);
989    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
990
991    // The page sets viewport width to 3000, but with UseWideViewport == false is must be ignored.
992    EXPECT_EQ(viewportWidth, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->contentsSize().width());
993    EXPECT_EQ(viewportHeight, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->contentsSize().height());
994}
995
996TEST_F(WebFrameTest, NoWideViewportIgnoresPageViewportWidthButAccountsScale)
997{
998    UseMockScrollbarSettings mockScrollbarSettings;
999    registerMockedHttpURLLoad("viewport-wide-2x-initial-scale.html");
1000
1001    FixedLayoutTestWebViewClient client;
1002    client.m_screenInfo.deviceScaleFactor = 1;
1003    int viewportWidth = 640;
1004    int viewportHeight = 480;
1005
1006    FrameTestHelpers::WebViewHelper webViewHelper;
1007    webViewHelper.initializeAndLoad(m_baseURL + "viewport-wide-2x-initial-scale.html", true, 0, &client, enableViewportSettings);
1008    webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true);
1009    webViewHelper.webView()->settings()->setUseWideViewport(false);
1010    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1011
1012    // The page sets viewport width to 3000, but with UseWideViewport == false it must be ignored.
1013    // While the initial scale specified by the page must be accounted.
1014    EXPECT_EQ(viewportWidth / 2, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->contentsSize().width());
1015    EXPECT_EQ(viewportHeight / 2, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->contentsSize().height());
1016}
1017
1018TEST_F(WebFrameTest, WideViewportSetsTo980WithoutViewportTag)
1019{
1020    UseMockScrollbarSettings mockScrollbarSettings;
1021    registerMockedHttpURLLoad("no_viewport_tag.html");
1022
1023    FixedLayoutTestWebViewClient client;
1024    client.m_screenInfo.deviceScaleFactor = 1;
1025    int viewportWidth = 640;
1026    int viewportHeight = 480;
1027
1028    FrameTestHelpers::WebViewHelper webViewHelper;
1029    webViewHelper.initializeAndLoad(m_baseURL + "no_viewport_tag.html", true, 0, &client, enableViewportSettings);
1030    applyViewportStyleOverride(&webViewHelper);
1031    webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true);
1032    webViewHelper.webView()->settings()->setUseWideViewport(true);
1033    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1034
1035    EXPECT_EQ(980, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->contentsSize().width());
1036    EXPECT_EQ(980.0 / viewportWidth * viewportHeight, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->contentsSize().height());
1037}
1038
1039TEST_F(WebFrameTest, NoWideViewportAndHeightInMeta)
1040{
1041    UseMockScrollbarSettings mockScrollbarSettings;
1042    registerMockedHttpURLLoad("viewport-height-1000.html");
1043
1044    FixedLayoutTestWebViewClient client;
1045    client.m_screenInfo.deviceScaleFactor = 1;
1046    int viewportWidth = 640;
1047    int viewportHeight = 480;
1048
1049    FrameTestHelpers::WebViewHelper webViewHelper;
1050    webViewHelper.initializeAndLoad(m_baseURL + "viewport-height-1000.html", true, 0, &client, enableViewportSettings);
1051    webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true);
1052    webViewHelper.webView()->settings()->setUseWideViewport(false);
1053    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1054
1055    EXPECT_EQ(viewportWidth, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->contentsSize().width());
1056}
1057
1058TEST_F(WebFrameTest, WideViewportSetsTo980WithAutoWidth)
1059{
1060    UseMockScrollbarSettings mockScrollbarSettings;
1061    registerMockedHttpURLLoad("viewport-2x-initial-scale.html");
1062
1063    FixedLayoutTestWebViewClient client;
1064    client.m_screenInfo.deviceScaleFactor = 1;
1065    int viewportWidth = 640;
1066    int viewportHeight = 480;
1067
1068    FrameTestHelpers::WebViewHelper webViewHelper;
1069    webViewHelper.initializeAndLoad(m_baseURL + "viewport-2x-initial-scale.html", true, 0, &client, enableViewportSettings);
1070    applyViewportStyleOverride(&webViewHelper);
1071    webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true);
1072    webViewHelper.webView()->settings()->setUseWideViewport(true);
1073    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1074
1075    EXPECT_EQ(980, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->contentsSize().width());
1076    EXPECT_EQ(980.0 / viewportWidth * viewportHeight, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->contentsSize().height());
1077}
1078
1079TEST_F(WebFrameTest, PageViewportInitialScaleOverridesLoadWithOverviewMode)
1080{
1081    UseMockScrollbarSettings mockScrollbarSettings;
1082    registerMockedHttpURLLoad("viewport-wide-2x-initial-scale.html");
1083
1084    FixedLayoutTestWebViewClient client;
1085    client.m_screenInfo.deviceScaleFactor = 1;
1086    int viewportWidth = 640;
1087    int viewportHeight = 480;
1088
1089    FrameTestHelpers::WebViewHelper webViewHelper;
1090    webViewHelper.initializeAndLoad(m_baseURL + "viewport-wide-2x-initial-scale.html", true, 0, &client, enableViewportSettings);
1091    webViewHelper.webView()->settings()->setLoadWithOverviewMode(false);
1092    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1093
1094    // The page must be displayed at 200% zoom, as specified in its viewport meta tag.
1095    EXPECT_EQ(2.0f, webViewHelper.webView()->pageScaleFactor());
1096}
1097
1098TEST_F(WebFrameTest, setInitialPageScaleFactorPermanently)
1099{
1100    UseMockScrollbarSettings mockScrollbarSettings;
1101
1102    registerMockedHttpURLLoad("fixed_layout.html");
1103
1104    FixedLayoutTestWebViewClient client;
1105    client.m_screenInfo.deviceScaleFactor = 1;
1106    float enforcedPageScaleFactor = 2.0f;
1107
1108    FrameTestHelpers::WebViewHelper webViewHelper;
1109    webViewHelper.initializeAndLoad(m_baseURL + "fixed_layout.html", true, 0, &client, enableViewportSettings);
1110    applyViewportStyleOverride(&webViewHelper);
1111    webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true);
1112    webViewHelper.webView()->settings()->setLoadWithOverviewMode(false);
1113    webViewHelper.webView()->setInitialPageScaleOverride(enforcedPageScaleFactor);
1114    webViewHelper.webView()->layout();
1115
1116    EXPECT_EQ(enforcedPageScaleFactor, webViewHelper.webView()->pageScaleFactor());
1117
1118    int viewportWidth = 640;
1119    int viewportHeight = 480;
1120    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1121    webViewHelper.webView()->layout();
1122
1123    EXPECT_EQ(enforcedPageScaleFactor, webViewHelper.webView()->pageScaleFactor());
1124
1125    webViewHelper.webView()->setInitialPageScaleOverride(-1);
1126    webViewHelper.webView()->layout();
1127    EXPECT_EQ(1.0, webViewHelper.webView()->pageScaleFactor());
1128}
1129
1130TEST_F(WebFrameTest, PermanentInitialPageScaleFactorOverridesLoadWithOverviewMode)
1131{
1132    UseMockScrollbarSettings mockScrollbarSettings;
1133    registerMockedHttpURLLoad("viewport-auto-initial-scale.html");
1134
1135    FixedLayoutTestWebViewClient client;
1136    client.m_screenInfo.deviceScaleFactor = 1;
1137    int viewportWidth = 640;
1138    int viewportHeight = 480;
1139    float enforcedPageScaleFactor = 0.5f;
1140
1141    FrameTestHelpers::WebViewHelper webViewHelper;
1142    webViewHelper.initializeAndLoad(m_baseURL + "viewport-auto-initial-scale.html", true, 0, &client, enableViewportSettings);
1143    webViewHelper.webView()->settings()->setLoadWithOverviewMode(false);
1144    webViewHelper.webView()->setInitialPageScaleOverride(enforcedPageScaleFactor);
1145    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1146
1147    EXPECT_EQ(enforcedPageScaleFactor, webViewHelper.webView()->pageScaleFactor());
1148}
1149
1150TEST_F(WebFrameTest, PermanentInitialPageScaleFactorOverridesPageViewportInitialScale)
1151{
1152    UseMockScrollbarSettings mockScrollbarSettings;
1153    registerMockedHttpURLLoad("viewport-wide-2x-initial-scale.html");
1154
1155    FixedLayoutTestWebViewClient client;
1156    client.m_screenInfo.deviceScaleFactor = 1;
1157    int viewportWidth = 640;
1158    int viewportHeight = 480;
1159    float enforcedPageScaleFactor = 0.5f;
1160
1161    FrameTestHelpers::WebViewHelper webViewHelper;
1162    webViewHelper.initializeAndLoad(m_baseURL + "viewport-wide-2x-initial-scale.html", true, 0, &client, enableViewportSettings);
1163    webViewHelper.webView()->setInitialPageScaleOverride(enforcedPageScaleFactor);
1164    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1165
1166    EXPECT_EQ(enforcedPageScaleFactor, webViewHelper.webView()->pageScaleFactor());
1167}
1168
1169TEST_F(WebFrameTest, SmallPermanentInitialPageScaleFactorIsClobbered)
1170{
1171    UseMockScrollbarSettings mockScrollbarSettings;
1172    const char* pages[] = {
1173        // These pages trigger the clobbering condition. There must be a matching item in "pageScaleFactors" array.
1174        "viewport-device-0.5x-initial-scale.html",
1175        "viewport-initial-scale-1.html",
1176        // These ones do not.
1177        "viewport-auto-initial-scale.html",
1178        "viewport-target-densitydpi-device-and-fixed-width.html"
1179    };
1180    float pageScaleFactors[] = { 0.5f, 1.0f };
1181    for (size_t i = 0; i < ARRAY_SIZE(pages); ++i)
1182        registerMockedHttpURLLoad(pages[i]);
1183
1184    FixedLayoutTestWebViewClient client;
1185    client.m_screenInfo.deviceScaleFactor = 1;
1186    int viewportWidth = 400;
1187    int viewportHeight = 300;
1188    float enforcedPageScaleFactor = 0.75f;
1189
1190    for (size_t i = 0; i < ARRAY_SIZE(pages); ++i) {
1191        for (int quirkEnabled = 0; quirkEnabled <= 1; ++quirkEnabled) {
1192            FrameTestHelpers::WebViewHelper webViewHelper;
1193            webViewHelper.initializeAndLoad(m_baseURL + pages[i], true, 0, &client, enableViewportSettings);
1194            applyViewportStyleOverride(&webViewHelper);
1195            webViewHelper.webView()->settings()->setClobberUserAgentInitialScaleQuirk(quirkEnabled);
1196            webViewHelper.webView()->setInitialPageScaleOverride(enforcedPageScaleFactor);
1197            webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1198
1199            float expectedPageScaleFactor = quirkEnabled && i < ARRAY_SIZE(pageScaleFactors) ? pageScaleFactors[i] : enforcedPageScaleFactor;
1200            EXPECT_EQ(expectedPageScaleFactor, webViewHelper.webView()->pageScaleFactor());
1201        }
1202    }
1203}
1204
1205TEST_F(WebFrameTest, PermanentInitialPageScaleFactorAffectsLayoutWidth)
1206{
1207    UseMockScrollbarSettings mockScrollbarSettings;
1208
1209    FixedLayoutTestWebViewClient client;
1210    client.m_screenInfo.deviceScaleFactor = 1;
1211    int viewportWidth = 640;
1212    int viewportHeight = 480;
1213    float enforcedPageScaleFactor = 0.5;
1214
1215    FrameTestHelpers::WebViewHelper webViewHelper;
1216    webViewHelper.initializeAndLoad("about:blank", true, 0, &client, enableViewportSettings);
1217    webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true);
1218    webViewHelper.webView()->settings()->setUseWideViewport(false);
1219    webViewHelper.webView()->settings()->setLoadWithOverviewMode(false);
1220    webViewHelper.webView()->setInitialPageScaleOverride(enforcedPageScaleFactor);
1221    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1222
1223    EXPECT_EQ(viewportWidth / enforcedPageScaleFactor, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->contentsSize().width());
1224    EXPECT_EQ(enforcedPageScaleFactor, webViewHelper.webView()->pageScaleFactor());
1225}
1226
1227TEST_F(WebFrameTest, SetForceZeroLayoutHeight)
1228{
1229    UseMockScrollbarSettings mockScrollbarSettings;
1230    registerMockedHttpURLLoad("200-by-300.html");
1231
1232    FixedLayoutTestWebViewClient client;
1233    client.m_screenInfo.deviceScaleFactor = 1;
1234    int viewportWidth = 640;
1235    int viewportHeight = 480;
1236
1237    FrameTestHelpers::WebViewHelper webViewHelper;
1238
1239    webViewHelper.initializeAndLoad(m_baseURL + "200-by-300.html", true, 0, &client, enableViewportSettings);
1240    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1241    webViewHelper.webView()->layout();
1242
1243    EXPECT_LE(viewportHeight, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height());
1244    webViewHelper.webView()->settings()->setForceZeroLayoutHeight(true);
1245    EXPECT_TRUE(webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->needsLayout());
1246
1247    EXPECT_EQ(0, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height());
1248
1249    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight * 2));
1250    EXPECT_FALSE(webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->needsLayout());
1251    EXPECT_EQ(0, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height());
1252
1253    webViewHelper.webView()->resize(WebSize(viewportWidth * 2, viewportHeight));
1254    webViewHelper.webView()->layout();
1255    EXPECT_EQ(0, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height());
1256
1257    webViewHelper.webView()->settings()->setForceZeroLayoutHeight(false);
1258    EXPECT_LE(viewportHeight, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height());
1259}
1260
1261TEST_F(WebFrameTest, SetForceZeroLayoutHeightWorksAcrossNavigations)
1262{
1263    UseMockScrollbarSettings mockScrollbarSettings;
1264    registerMockedHttpURLLoad("200-by-300.html");
1265    registerMockedHttpURLLoad("large-div.html");
1266
1267    FixedLayoutTestWebViewClient client;
1268    client.m_screenInfo.deviceScaleFactor = 1;
1269    int viewportWidth = 640;
1270    int viewportHeight = 480;
1271
1272    FrameTestHelpers::WebViewHelper webViewHelper;
1273
1274    webViewHelper.initializeAndLoad(m_baseURL + "200-by-300.html", true, 0, &client, enableViewportSettings);
1275    webViewHelper.webView()->settings()->setForceZeroLayoutHeight(true);
1276    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1277    webViewHelper.webView()->layout();
1278
1279    FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), m_baseURL + "large-div.html");
1280    webViewHelper.webView()->layout();
1281
1282    EXPECT_EQ(0, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height());
1283}
1284
1285TEST_F(WebFrameTest, SetForceZeroLayoutHeightWithWideViewportQuirk)
1286{
1287    UseMockScrollbarSettings mockScrollbarSettings;
1288    registerMockedHttpURLLoad("200-by-300.html");
1289
1290    FixedLayoutTestWebViewClient client;
1291    client.m_screenInfo.deviceScaleFactor = 1;
1292    int viewportWidth = 640;
1293    int viewportHeight = 480;
1294
1295    FrameTestHelpers::WebViewHelper webViewHelper;
1296
1297    webViewHelper.initializeAndLoad(m_baseURL + "200-by-300.html", true, 0, &client, enableViewportSettings);
1298    webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true);
1299    webViewHelper.webView()->settings()->setUseWideViewport(true);
1300    webViewHelper.webView()->settings()->setForceZeroLayoutHeight(true);
1301    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1302    webViewHelper.webView()->layout();
1303
1304    EXPECT_EQ(0, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height());
1305}
1306
1307TEST_F(WebFrameTest, WideViewportAndWideContentWithInitialScale)
1308{
1309    UseMockScrollbarSettings mockScrollbarSettings;
1310    registerMockedHttpURLLoad("wide_document_width_viewport.html");
1311
1312    FixedLayoutTestWebViewClient client;
1313    client.m_screenInfo.deviceScaleFactor = 1;
1314    int viewportWidth = 600;
1315    int viewportHeight = 800;
1316
1317    FrameTestHelpers::WebViewHelper webViewHelper;
1318    webViewHelper.initializeAndLoad("about:blank", true, 0, &client, enableViewportSettings);
1319    webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true);
1320    webViewHelper.webView()->settings()->setUseWideViewport(true);
1321    webViewHelper.webView()->settings()->setViewportMetaLayoutSizeQuirk(true);
1322    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1323
1324    FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), m_baseURL + "wide_document_width_viewport.html");
1325    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1326
1327    int wideDocumentWidth = 800;
1328    float minimumPageScaleFactor = viewportWidth / (float) wideDocumentWidth;
1329    EXPECT_EQ(minimumPageScaleFactor, webViewHelper.webView()->pageScaleFactor());
1330    EXPECT_EQ(minimumPageScaleFactor, webViewHelper.webView()->minimumPageScaleFactor());
1331}
1332
1333TEST_F(WebFrameTest, WideViewportQuirkClobbersHeight)
1334{
1335    UseMockScrollbarSettings mockScrollbarSettings;
1336    registerMockedHttpURLLoad("viewport-height-1000.html");
1337
1338    FixedLayoutTestWebViewClient client;
1339    client.m_screenInfo.deviceScaleFactor = 1;
1340    int viewportWidth = 600;
1341    int viewportHeight = 800;
1342
1343    FrameTestHelpers::WebViewHelper webViewHelper;
1344    webViewHelper.initializeAndLoad("about:blank", true, 0, &client, enableViewportSettings);
1345    webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true);
1346    webViewHelper.webView()->settings()->setUseWideViewport(false);
1347    webViewHelper.webView()->settings()->setViewportMetaLayoutSizeQuirk(true);
1348    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1349
1350    FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), m_baseURL + "viewport-height-1000.html");
1351    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1352
1353    EXPECT_EQ(800, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height());
1354    EXPECT_EQ(1, webViewHelper.webView()->pageScaleFactor());
1355}
1356
1357TEST_F(WebFrameTest, LayoutSize320Quirk)
1358{
1359    UseMockScrollbarSettings mockScrollbarSettings;
1360    registerMockedHttpURLLoad("viewport/viewport-30.html");
1361
1362    FixedLayoutTestWebViewClient client;
1363    client.m_screenInfo.deviceScaleFactor = 1;
1364    int viewportWidth = 600;
1365    int viewportHeight = 800;
1366
1367    FrameTestHelpers::WebViewHelper webViewHelper;
1368    webViewHelper.initializeAndLoad("about:blank", true, 0, &client, enableViewportSettings);
1369    webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true);
1370    webViewHelper.webView()->settings()->setUseWideViewport(true);
1371    webViewHelper.webView()->settings()->setViewportMetaLayoutSizeQuirk(true);
1372    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1373
1374    FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), m_baseURL + "viewport/viewport-30.html");
1375    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1376
1377    EXPECT_EQ(600, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width());
1378    EXPECT_EQ(800, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height());
1379    EXPECT_EQ(1, webViewHelper.webView()->pageScaleFactor());
1380
1381    // The magic number to snap to device-width is 320, so test that 321 is
1382    // respected.
1383    Document* document = toLocalFrame(webViewHelper.webViewImpl()->page()->mainFrame())->document();
1384    ViewportDescription description = document->viewportDescription();
1385    description.minWidth = Length(321, blink::Fixed);
1386    description.maxWidth = Length(321, blink::Fixed);
1387    document->setViewportDescription(description);
1388    webViewHelper.webView()->layout();
1389    EXPECT_EQ(321, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width());
1390
1391    description.minWidth = Length(320, blink::Fixed);
1392    description.maxWidth = Length(320, blink::Fixed);
1393    document->setViewportDescription(description);
1394    webViewHelper.webView()->layout();
1395    EXPECT_EQ(600, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width());
1396
1397    description = document->viewportDescription();
1398    description.maxHeight = Length(1000, blink::Fixed);
1399    document->setViewportDescription(description);
1400    webViewHelper.webView()->layout();
1401    EXPECT_EQ(1000, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height());
1402
1403    description.maxHeight = Length(320, blink::Fixed);
1404    document->setViewportDescription(description);
1405    webViewHelper.webView()->layout();
1406    EXPECT_EQ(800, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height());
1407}
1408
1409TEST_F(WebFrameTest, ZeroValuesQuirk)
1410{
1411    UseMockScrollbarSettings mockScrollbarSettings;
1412    registerMockedHttpURLLoad("viewport-zero-values.html");
1413
1414    FixedLayoutTestWebViewClient client;
1415    client.m_screenInfo.deviceScaleFactor = 1;
1416    int viewportWidth = 640;
1417    int viewportHeight = 480;
1418
1419    FrameTestHelpers::WebViewHelper webViewHelper;
1420    webViewHelper.initialize(true, 0, &client, enableViewportSettings);
1421    webViewHelper.webView()->settings()->setViewportMetaZeroValuesQuirk(true);
1422    webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true);
1423    webViewHelper.webView()->settings()->setViewportMetaLayoutSizeQuirk(true);
1424    FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), m_baseURL + "viewport-zero-values.html");
1425    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1426
1427    EXPECT_EQ(viewportWidth, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width());
1428    EXPECT_EQ(1.0f, webViewHelper.webView()->pageScaleFactor());
1429
1430    webViewHelper.webView()->settings()->setUseWideViewport(true);
1431    webViewHelper.webView()->layout();
1432    EXPECT_EQ(viewportWidth, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width());
1433    EXPECT_EQ(1.0f, webViewHelper.webView()->pageScaleFactor());
1434}
1435
1436TEST_F(WebFrameTest, OverflowHiddenDisablesScrolling)
1437{
1438    registerMockedHttpURLLoad("body-overflow-hidden.html");
1439
1440    FixedLayoutTestWebViewClient client;
1441    client.m_screenInfo.deviceScaleFactor = 1;
1442    int viewportWidth = 640;
1443    int viewportHeight = 480;
1444
1445    FrameTestHelpers::WebViewHelper webViewHelper;
1446    webViewHelper.initialize(true, 0, &client);
1447    FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), m_baseURL + "body-overflow-hidden.html");
1448    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1449
1450    FrameView* view = webViewHelper.webViewImpl()->mainFrameImpl()->frameView();
1451    EXPECT_FALSE(view->userInputScrollable(VerticalScrollbar));
1452}
1453
1454TEST_F(WebFrameTest, IgnoreOverflowHiddenQuirk)
1455{
1456    registerMockedHttpURLLoad("body-overflow-hidden.html");
1457
1458    FixedLayoutTestWebViewClient client;
1459    client.m_screenInfo.deviceScaleFactor = 1;
1460    int viewportWidth = 640;
1461    int viewportHeight = 480;
1462
1463    FrameTestHelpers::WebViewHelper webViewHelper;
1464    webViewHelper.initialize(true, 0, &client);
1465    webViewHelper.webView()->settings()->setIgnoreMainFrameOverflowHiddenQuirk(true);
1466    FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), m_baseURL + "body-overflow-hidden.html");
1467    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1468
1469    FrameView* view = webViewHelper.webViewImpl()->mainFrameImpl()->frameView();
1470    EXPECT_TRUE(view->userInputScrollable(VerticalScrollbar));
1471}
1472
1473TEST_F(WebFrameTest, NonZeroValuesNoQuirk)
1474{
1475    UseMockScrollbarSettings mockScrollbarSettings;
1476    registerMockedHttpURLLoad("viewport-nonzero-values.html");
1477
1478    FixedLayoutTestWebViewClient client;
1479    client.m_screenInfo.deviceScaleFactor = 1;
1480    int viewportWidth = 640;
1481    int viewportHeight = 480;
1482    float expectedPageScaleFactor = 0.5f;
1483
1484    FrameTestHelpers::WebViewHelper webViewHelper;
1485    webViewHelper.initialize(true, 0, &client, enableViewportSettings);
1486    webViewHelper.webView()->settings()->setViewportMetaZeroValuesQuirk(true);
1487    webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true);
1488    FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), m_baseURL + "viewport-nonzero-values.html");
1489    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1490
1491    EXPECT_EQ(viewportWidth / expectedPageScaleFactor, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width());
1492    EXPECT_EQ(expectedPageScaleFactor, webViewHelper.webView()->pageScaleFactor());
1493
1494    webViewHelper.webView()->settings()->setUseWideViewport(true);
1495    webViewHelper.webView()->layout();
1496    EXPECT_EQ(viewportWidth / expectedPageScaleFactor, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width());
1497    EXPECT_EQ(expectedPageScaleFactor, webViewHelper.webView()->pageScaleFactor());
1498}
1499
1500TEST_F(WebFrameTest, setPageScaleFactorDoesNotLayout)
1501{
1502    UseMockScrollbarSettings mockScrollbarSettings;
1503    registerMockedHttpURLLoad("fixed_layout.html");
1504
1505    FixedLayoutTestWebViewClient client;
1506    client.m_screenInfo.deviceScaleFactor = 1;
1507    // Small viewport to ensure there are always scrollbars.
1508    int viewportWidth = 64;
1509    int viewportHeight = 48;
1510
1511    FrameTestHelpers::WebViewHelper webViewHelper;
1512    webViewHelper.initializeAndLoad(m_baseURL + "fixed_layout.html", true, 0, &client, enableViewportSettings);
1513    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1514    webViewHelper.webView()->layout();
1515
1516    int prevLayoutCount = webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutCount();
1517    webViewHelper.webViewImpl()->setPageScaleFactor(3);
1518    EXPECT_FALSE(webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->needsLayout());
1519    EXPECT_EQ(prevLayoutCount, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutCount());
1520}
1521
1522TEST_F(WebFrameTest, setPageScaleFactorWithOverlayScrollbarsDoesNotLayout)
1523{
1524    UseMockScrollbarSettings mockScrollbarSettings;
1525
1526    registerMockedHttpURLLoad("fixed_layout.html");
1527
1528    FixedLayoutTestWebViewClient client;
1529    client.m_screenInfo.deviceScaleFactor = 1;
1530    int viewportWidth = 640;
1531    int viewportHeight = 480;
1532
1533    FrameTestHelpers::WebViewHelper webViewHelper;
1534    webViewHelper.initializeAndLoad(m_baseURL + "fixed_layout.html", true, 0, &client, enableViewportSettings);
1535    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1536    webViewHelper.webView()->layout();
1537
1538    int prevLayoutCount = webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutCount();
1539    webViewHelper.webViewImpl()->setPageScaleFactor(30);
1540    EXPECT_FALSE(webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->needsLayout());
1541    EXPECT_EQ(prevLayoutCount, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutCount());
1542
1543}
1544
1545TEST_F(WebFrameTest, pageScaleFactorWrittenToHistoryItem)
1546{
1547    UseMockScrollbarSettings mockScrollbarSettings;
1548    registerMockedHttpURLLoad("fixed_layout.html");
1549
1550    FixedLayoutTestWebViewClient client;
1551    client.m_screenInfo.deviceScaleFactor = 1;
1552    int viewportWidth = 640;
1553    int viewportHeight = 480;
1554
1555    FrameTestHelpers::WebViewHelper webViewHelper;
1556    webViewHelper.initializeAndLoad(m_baseURL + "fixed_layout.html", true, 0, &client, enableViewportSettings);
1557    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1558    webViewHelper.webView()->layout();
1559
1560    webViewHelper.webView()->setPageScaleFactor(3);
1561    EXPECT_EQ(3, toLocalFrame(webViewHelper.webViewImpl()->page()->mainFrame())->loader().currentItem()->pageScaleFactor());
1562}
1563
1564TEST_F(WebFrameTest, initialScaleWrittenToHistoryItem)
1565{
1566    UseMockScrollbarSettings mockScrollbarSettings;
1567    registerMockedHttpURLLoad("fixed_layout.html");
1568
1569    FixedLayoutTestWebViewClient client;
1570    client.m_screenInfo.deviceScaleFactor = 1;
1571    int viewportWidth = 640;
1572    int viewportHeight = 480;
1573
1574    FrameTestHelpers::WebViewHelper webViewHelper;
1575    webViewHelper.initializeAndLoad(m_baseURL + "fixed_layout.html", true, 0, &client, enableViewportSettings);
1576    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1577    webViewHelper.webView()->layout();
1578
1579    int defaultFixedLayoutWidth = 980;
1580    float minimumPageScaleFactor = viewportWidth / (float) defaultFixedLayoutWidth;
1581    EXPECT_EQ(minimumPageScaleFactor, toLocalFrame(webViewHelper.webViewImpl()->page()->mainFrame())->loader().currentItem()->pageScaleFactor());
1582}
1583
1584TEST_F(WebFrameTest, pageScaleFactorShrinksViewport)
1585{
1586    UseMockScrollbarSettings mockScrollbarSettings;
1587    registerMockedHttpURLLoad("large-div.html");
1588
1589    FixedLayoutTestWebViewClient client;
1590    client.m_screenInfo.deviceScaleFactor = 1;
1591    // Small viewport to ensure there are always scrollbars.
1592    int viewportWidth = 64;
1593    int viewportHeight = 48;
1594
1595    FrameTestHelpers::WebViewHelper webViewHelper;
1596    webViewHelper.initializeAndLoad(m_baseURL + "large-div.html", true, 0, &client, enableViewportSettings);
1597    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1598    webViewHelper.webView()->layout();
1599
1600    FrameView* view = webViewHelper.webViewImpl()->mainFrameImpl()->frameView();
1601    int viewportWidthMinusScrollbar = viewportWidth - (view->verticalScrollbar()->isOverlayScrollbar() ? 0 : 15);
1602    int viewportHeightMinusScrollbar = viewportHeight - (view->horizontalScrollbar()->isOverlayScrollbar() ? 0 : 15);
1603
1604    webViewHelper.webView()->setPageScaleFactor(2);
1605
1606    IntSize unscaledSize = view->unscaledVisibleContentSize(IncludeScrollbars);
1607    EXPECT_EQ(viewportWidth, unscaledSize.width());
1608    EXPECT_EQ(viewportHeight, unscaledSize.height());
1609
1610    IntSize unscaledSizeMinusScrollbar = view->unscaledVisibleContentSize(ExcludeScrollbars);
1611    EXPECT_EQ(viewportWidthMinusScrollbar, unscaledSizeMinusScrollbar.width());
1612    EXPECT_EQ(viewportHeightMinusScrollbar, unscaledSizeMinusScrollbar.height());
1613
1614    IntSize scaledSize = view->visibleContentRect().size();
1615    EXPECT_EQ(ceil(viewportWidthMinusScrollbar / 2.0), scaledSize.width());
1616    EXPECT_EQ(ceil(viewportHeightMinusScrollbar / 2.0), scaledSize.height());
1617}
1618
1619TEST_F(WebFrameTest, pageScaleFactorDoesNotApplyCssTransform)
1620{
1621    UseMockScrollbarSettings mockScrollbarSettings;
1622    registerMockedHttpURLLoad("fixed_layout.html");
1623
1624    FixedLayoutTestWebViewClient client;
1625    client.m_screenInfo.deviceScaleFactor = 1;
1626    int viewportWidth = 640;
1627    int viewportHeight = 480;
1628
1629    FrameTestHelpers::WebViewHelper webViewHelper;
1630    webViewHelper.initializeAndLoad(m_baseURL + "fixed_layout.html", true, 0, &client, enableViewportSettings);
1631    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1632    webViewHelper.webView()->layout();
1633
1634    webViewHelper.webView()->setPageScaleFactor(2);
1635
1636    EXPECT_EQ(980, toLocalFrame(webViewHelper.webViewImpl()->page()->mainFrame())->contentRenderer()->unscaledDocumentRect().width());
1637    EXPECT_EQ(980, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->contentsSize().width());
1638}
1639
1640TEST_F(WebFrameTest, targetDensityDpiHigh)
1641{
1642    UseMockScrollbarSettings mockScrollbarSettings;
1643    registerMockedHttpURLLoad("viewport-target-densitydpi-high.html");
1644
1645    FixedLayoutTestWebViewClient client;
1646    // high-dpi = 240
1647    float targetDpi = 240.0f;
1648    float deviceScaleFactors[] = { 1.0f, 4.0f / 3.0f, 2.0f };
1649    int viewportWidth = 640;
1650    int viewportHeight = 480;
1651
1652    for (size_t i = 0; i < ARRAY_SIZE(deviceScaleFactors); ++i) {
1653        float deviceScaleFactor = deviceScaleFactors[i];
1654        float deviceDpi = deviceScaleFactor * 160.0f;
1655        client.m_screenInfo.deviceScaleFactor = deviceScaleFactor;
1656
1657        FrameTestHelpers::WebViewHelper webViewHelper;
1658        webViewHelper.initializeAndLoad(m_baseURL + "viewport-target-densitydpi-high.html", true, 0, &client, enableViewportSettings);
1659        webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true);
1660        webViewHelper.webView()->settings()->setSupportDeprecatedTargetDensityDPI(true);
1661        webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1662
1663        // We need to account for the fact that logical pixels are unconditionally multiplied by deviceScaleFactor to produce
1664        // physical pixels.
1665        float densityDpiScaleRatio = deviceScaleFactor * targetDpi / deviceDpi;
1666        EXPECT_NEAR(viewportWidth * densityDpiScaleRatio, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width(), 1.0f);
1667        EXPECT_NEAR(viewportHeight * densityDpiScaleRatio, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height(), 1.0f);
1668        EXPECT_NEAR(1.0f / densityDpiScaleRatio, webViewHelper.webView()->pageScaleFactor(), 0.01f);
1669    }
1670}
1671
1672TEST_F(WebFrameTest, targetDensityDpiDevice)
1673{
1674    UseMockScrollbarSettings mockScrollbarSettings;
1675    registerMockedHttpURLLoad("viewport-target-densitydpi-device.html");
1676
1677    float deviceScaleFactors[] = { 1.0f, 4.0f / 3.0f, 2.0f };
1678
1679    FixedLayoutTestWebViewClient client;
1680    int viewportWidth = 640;
1681    int viewportHeight = 480;
1682
1683    for (size_t i = 0; i < ARRAY_SIZE(deviceScaleFactors); ++i) {
1684        client.m_screenInfo.deviceScaleFactor = deviceScaleFactors[i];
1685
1686        FrameTestHelpers::WebViewHelper webViewHelper;
1687        webViewHelper.initializeAndLoad(m_baseURL + "viewport-target-densitydpi-device.html", true, 0, &client, enableViewportSettings);
1688        webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true);
1689        webViewHelper.webView()->settings()->setSupportDeprecatedTargetDensityDPI(true);
1690        webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1691
1692        EXPECT_NEAR(viewportWidth * client.m_screenInfo.deviceScaleFactor, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width(), 1.0f);
1693        EXPECT_NEAR(viewportHeight * client.m_screenInfo.deviceScaleFactor, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height(), 1.0f);
1694        EXPECT_NEAR(1.0f / client.m_screenInfo.deviceScaleFactor, webViewHelper.webView()->pageScaleFactor(), 0.01f);
1695    }
1696}
1697
1698TEST_F(WebFrameTest, targetDensityDpiDeviceAndFixedWidth)
1699{
1700    UseMockScrollbarSettings mockScrollbarSettings;
1701    registerMockedHttpURLLoad("viewport-target-densitydpi-device-and-fixed-width.html");
1702
1703    float deviceScaleFactors[] = { 1.0f, 4.0f / 3.0f, 2.0f };
1704
1705    FixedLayoutTestWebViewClient client;
1706    int viewportWidth = 640;
1707    int viewportHeight = 480;
1708
1709    for (size_t i = 0; i < ARRAY_SIZE(deviceScaleFactors); ++i) {
1710        client.m_screenInfo.deviceScaleFactor = deviceScaleFactors[i];
1711
1712        FrameTestHelpers::WebViewHelper webViewHelper;
1713        webViewHelper.initializeAndLoad(m_baseURL + "viewport-target-densitydpi-device-and-fixed-width.html", true, 0, &client, enableViewportSettings);
1714        webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true);
1715        webViewHelper.webView()->settings()->setSupportDeprecatedTargetDensityDPI(true);
1716        webViewHelper.webView()->settings()->setUseWideViewport(true);
1717        webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1718
1719        EXPECT_NEAR(viewportWidth, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width(), 1.0f);
1720        EXPECT_NEAR(viewportHeight, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height(), 1.0f);
1721        EXPECT_NEAR(1.0f, webViewHelper.webView()->pageScaleFactor(), 0.01f);
1722    }
1723}
1724
1725TEST_F(WebFrameTest, NoWideViewportAndScaleLessThanOne)
1726{
1727    UseMockScrollbarSettings mockScrollbarSettings;
1728    registerMockedHttpURLLoad("viewport-initial-scale-less-than-1.html");
1729
1730    FixedLayoutTestWebViewClient client;
1731    client.m_screenInfo.deviceScaleFactor = 1.33f;
1732    int viewportWidth = 640;
1733    int viewportHeight = 480;
1734
1735    FrameTestHelpers::WebViewHelper webViewHelper;
1736    webViewHelper.initializeAndLoad(m_baseURL + "viewport-initial-scale-less-than-1.html", true, 0, &client, enableViewportSettings);
1737    webViewHelper.webView()->settings()->setSupportDeprecatedTargetDensityDPI(true);
1738    webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true);
1739    webViewHelper.webView()->settings()->setUseWideViewport(false);
1740    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1741    webViewHelper.webView()->layout();
1742
1743    EXPECT_NEAR(viewportWidth * client.m_screenInfo.deviceScaleFactor, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width(), 1.0f);
1744    EXPECT_NEAR(viewportHeight * client.m_screenInfo.deviceScaleFactor, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height(), 1.0f);
1745    EXPECT_NEAR(1.0f / client.m_screenInfo.deviceScaleFactor, webViewHelper.webView()->pageScaleFactor(), 0.01f);
1746}
1747
1748TEST_F(WebFrameTest, NoWideViewportAndScaleLessThanOneWithDeviceWidth)
1749{
1750    UseMockScrollbarSettings mockScrollbarSettings;
1751    registerMockedHttpURLLoad("viewport-initial-scale-less-than-1-device-width.html");
1752
1753    FixedLayoutTestWebViewClient client;
1754    client.m_screenInfo.deviceScaleFactor = 1.33f;
1755    int viewportWidth = 640;
1756    int viewportHeight = 480;
1757
1758    FrameTestHelpers::WebViewHelper webViewHelper;
1759    webViewHelper.initializeAndLoad(m_baseURL + "viewport-initial-scale-less-than-1-device-width.html", true, 0, &client, enableViewportSettings);
1760    webViewHelper.webView()->settings()->setSupportDeprecatedTargetDensityDPI(true);
1761    webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true);
1762    webViewHelper.webView()->settings()->setUseWideViewport(false);
1763    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1764    webViewHelper.webView()->layout();
1765
1766    const float pageZoom = 0.25f;
1767    EXPECT_NEAR(viewportWidth * client.m_screenInfo.deviceScaleFactor / pageZoom, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width(), 1.0f);
1768    EXPECT_NEAR(viewportHeight * client.m_screenInfo.deviceScaleFactor / pageZoom, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height(), 1.0f);
1769    EXPECT_NEAR(1.0f / client.m_screenInfo.deviceScaleFactor, webViewHelper.webView()->pageScaleFactor(), 0.01f);
1770}
1771
1772TEST_F(WebFrameTest, NoWideViewportAndNoViewportWithInitialPageScaleOverride)
1773{
1774    UseMockScrollbarSettings mockScrollbarSettings;
1775    registerMockedHttpURLLoad("large-div.html");
1776
1777    FixedLayoutTestWebViewClient client;
1778    int viewportWidth = 640;
1779    int viewportHeight = 480;
1780    float enforcedPageScaleFactor = 5.0f;
1781
1782    FrameTestHelpers::WebViewHelper webViewHelper;
1783    webViewHelper.initializeAndLoad(m_baseURL + "large-div.html", true, 0, &client, enableViewportSettings);
1784    webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true);
1785    webViewHelper.webView()->settings()->setUseWideViewport(false);
1786    webViewHelper.webView()->setInitialPageScaleOverride(enforcedPageScaleFactor);
1787    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1788    webViewHelper.webView()->layout();
1789
1790    EXPECT_NEAR(viewportWidth / enforcedPageScaleFactor, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width(), 1.0f);
1791    EXPECT_NEAR(viewportHeight / enforcedPageScaleFactor, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height(), 1.0f);
1792    EXPECT_NEAR(enforcedPageScaleFactor, webViewHelper.webView()->pageScaleFactor(), 0.01f);
1793}
1794
1795TEST_F(WebFrameTest, NoUserScalableQuirkIgnoresViewportScale)
1796{
1797    UseMockScrollbarSettings mockScrollbarSettings;
1798    registerMockedHttpURLLoad("viewport-initial-scale-and-user-scalable-no.html");
1799
1800    FixedLayoutTestWebViewClient client;
1801    int viewportWidth = 640;
1802    int viewportHeight = 480;
1803
1804    FrameTestHelpers::WebViewHelper webViewHelper;
1805    webViewHelper.initializeAndLoad(m_baseURL + "viewport-initial-scale-and-user-scalable-no.html", true, 0, &client, enableViewportSettings);
1806    webViewHelper.webView()->settings()->setViewportMetaNonUserScalableQuirk(true);
1807    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1808    webViewHelper.webView()->layout();
1809
1810    EXPECT_NEAR(viewportWidth, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width(), 1.0f);
1811    EXPECT_NEAR(viewportHeight, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height(), 1.0f);
1812    EXPECT_NEAR(1.0f, webViewHelper.webView()->pageScaleFactor(), 0.01f);
1813}
1814
1815TEST_F(WebFrameTest, NoUserScalableQuirkIgnoresViewportScaleForNonWideViewport)
1816{
1817    UseMockScrollbarSettings mockScrollbarSettings;
1818    registerMockedHttpURLLoad("viewport-initial-scale-and-user-scalable-no.html");
1819
1820    FixedLayoutTestWebViewClient client;
1821    client.m_screenInfo.deviceScaleFactor = 1.33f;
1822    int viewportWidth = 640;
1823    int viewportHeight = 480;
1824
1825    FrameTestHelpers::WebViewHelper webViewHelper;
1826    webViewHelper.initializeAndLoad(m_baseURL + "viewport-initial-scale-and-user-scalable-no.html", true, 0, &client, enableViewportSettings);
1827    webViewHelper.webView()->settings()->setSupportDeprecatedTargetDensityDPI(true);
1828    webViewHelper.webView()->settings()->setViewportMetaNonUserScalableQuirk(true);
1829    webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true);
1830    webViewHelper.webView()->settings()->setUseWideViewport(false);
1831    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1832    webViewHelper.webView()->layout();
1833
1834    EXPECT_NEAR(viewportWidth * client.m_screenInfo.deviceScaleFactor, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width(), 1.0f);
1835    EXPECT_NEAR(viewportHeight * client.m_screenInfo.deviceScaleFactor, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height(), 1.0f);
1836    EXPECT_NEAR(1.0f / client.m_screenInfo.deviceScaleFactor, webViewHelper.webView()->pageScaleFactor(), 0.01f);
1837}
1838
1839TEST_F(WebFrameTest, NoUserScalableQuirkIgnoresViewportScaleForWideViewport)
1840{
1841    UseMockScrollbarSettings mockScrollbarSettings;
1842    registerMockedHttpURLLoad("viewport-2x-initial-scale-non-user-scalable.html");
1843
1844    FixedLayoutTestWebViewClient client;
1845    int viewportWidth = 640;
1846    int viewportHeight = 480;
1847
1848    FrameTestHelpers::WebViewHelper webViewHelper;
1849    webViewHelper.initializeAndLoad(m_baseURL + "viewport-2x-initial-scale-non-user-scalable.html", true, 0, &client, enableViewportSettings);
1850    webViewHelper.webView()->settings()->setViewportMetaNonUserScalableQuirk(true);
1851    webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true);
1852    webViewHelper.webView()->settings()->setUseWideViewport(true);
1853    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1854
1855    EXPECT_NEAR(viewportWidth, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().width(), 1.0f);
1856    EXPECT_NEAR(viewportHeight, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height(), 1.0f);
1857    EXPECT_NEAR(1.0f, webViewHelper.webView()->pageScaleFactor(), 0.01f);
1858}
1859
1860TEST_F(WebFrameTest, DesktopPageCanBeZoomedInWhenWideViewportIsTurnedOff)
1861{
1862    UseMockScrollbarSettings mockScrollbarSettings;
1863    registerMockedHttpURLLoad("no_viewport_tag.html");
1864
1865    FixedLayoutTestWebViewClient client;
1866    int viewportWidth = 640;
1867    int viewportHeight = 480;
1868
1869    FrameTestHelpers::WebViewHelper webViewHelper;
1870    webViewHelper.initializeAndLoad(m_baseURL + "no_viewport_tag.html", true, 0, &client, enableViewportSettings);
1871    webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true);
1872    webViewHelper.webView()->settings()->setUseWideViewport(false);
1873    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
1874
1875    EXPECT_NEAR(1.0f, webViewHelper.webView()->pageScaleFactor(), 0.01f);
1876    EXPECT_NEAR(1.0f, webViewHelper.webView()->minimumPageScaleFactor(), 0.01f);
1877    EXPECT_NEAR(5.0f, webViewHelper.webView()->maximumPageScaleFactor(), 0.01f);
1878}
1879
1880class WebFrameResizeTest : public WebFrameTest {
1881protected:
1882
1883    static FloatSize computeRelativeOffset(const IntPoint& absoluteOffset, const LayoutRect& rect)
1884    {
1885        FloatSize relativeOffset = FloatPoint(absoluteOffset) - rect.location();
1886        relativeOffset.scale(1.f / rect.width(), 1.f / rect.height());
1887        return relativeOffset;
1888    }
1889
1890    void testResizeYieldsCorrectScrollAndScale(const char* url,
1891                                               const float initialPageScaleFactor,
1892                                               const WebSize scrollOffset,
1893                                               const WebSize viewportSize,
1894                                               const bool shouldScaleRelativeToViewportWidth) {
1895        UseMockScrollbarSettings mockScrollbarSettings;
1896        registerMockedHttpURLLoad(url);
1897
1898        const float aspectRatio = static_cast<float>(viewportSize.width) / viewportSize.height;
1899
1900        FrameTestHelpers::WebViewHelper webViewHelper;
1901        webViewHelper.initializeAndLoad(m_baseURL + url, true, 0, 0, enableViewportSettings);
1902
1903        // Origin scrollOffsets preserved under resize.
1904        {
1905            webViewHelper.webViewImpl()->resize(WebSize(viewportSize.width, viewportSize.height));
1906            webViewHelper.webViewImpl()->setPageScaleFactor(initialPageScaleFactor);
1907            ASSERT_EQ(viewportSize, webViewHelper.webViewImpl()->size());
1908            ASSERT_EQ(initialPageScaleFactor, webViewHelper.webViewImpl()->pageScaleFactor());
1909            webViewHelper.webViewImpl()->resize(WebSize(viewportSize.height, viewportSize.width));
1910            float expectedPageScaleFactor = initialPageScaleFactor * (shouldScaleRelativeToViewportWidth ? 1 / aspectRatio : 1);
1911            EXPECT_NEAR(expectedPageScaleFactor, webViewHelper.webViewImpl()->pageScaleFactor(), 0.05f);
1912            EXPECT_EQ(WebSize(), webViewHelper.webViewImpl()->mainFrame()->scrollOffset());
1913        }
1914
1915        // Resizing just the height should not affect pageScaleFactor or scrollOffset.
1916        {
1917            webViewHelper.webViewImpl()->resize(WebSize(viewportSize.width, viewportSize.height));
1918            webViewHelper.webViewImpl()->setPageScaleFactor(initialPageScaleFactor);
1919            webViewHelper.webViewImpl()->setMainFrameScrollOffset(WebPoint(scrollOffset.width, scrollOffset.height));
1920            webViewHelper.webViewImpl()->layout();
1921            const WebSize expectedScrollOffset = webViewHelper.webViewImpl()->mainFrame()->scrollOffset();
1922            webViewHelper.webViewImpl()->resize(WebSize(viewportSize.width, viewportSize.height * 0.8f));
1923            EXPECT_EQ(initialPageScaleFactor, webViewHelper.webViewImpl()->pageScaleFactor());
1924            EXPECT_EQ(expectedScrollOffset, webViewHelper.webViewImpl()->mainFrame()->scrollOffset());
1925            webViewHelper.webViewImpl()->resize(WebSize(viewportSize.width, viewportSize.height * 0.8f));
1926            EXPECT_EQ(initialPageScaleFactor, webViewHelper.webViewImpl()->pageScaleFactor());
1927            EXPECT_EQ(expectedScrollOffset, webViewHelper.webViewImpl()->mainFrame()->scrollOffset());
1928        }
1929
1930        // Generic resize preserves scrollOffset relative to anchor node located
1931        // the top center of the screen.
1932        {
1933            webViewHelper.webViewImpl()->resize(WebSize(viewportSize.height, viewportSize.width));
1934            float pageScaleFactor = webViewHelper.webViewImpl()->pageScaleFactor();
1935            webViewHelper.webViewImpl()->resize(WebSize(viewportSize.width, viewportSize.height));
1936            float expectedPageScaleFactor = pageScaleFactor * (shouldScaleRelativeToViewportWidth ? aspectRatio : 1);
1937            EXPECT_NEAR(expectedPageScaleFactor, webViewHelper.webViewImpl()->pageScaleFactor(), 0.05f);
1938            webViewHelper.webViewImpl()->mainFrame()->setScrollOffset(scrollOffset);
1939
1940            IntPoint anchorPoint = IntPoint(scrollOffset) + IntPoint(viewportSize.width / 2, 0);
1941            RefPtrWillBeRawPtr<Node> anchorNode = webViewHelper.webViewImpl()->mainFrameImpl()->frame()->eventHandler().hitTestResultAtPoint(anchorPoint, HitTestRequest::ReadOnly | HitTestRequest::Active).innerNode();
1942            ASSERT(anchorNode);
1943
1944            pageScaleFactor = webViewHelper.webViewImpl()->pageScaleFactor();
1945            const FloatSize preResizeRelativeOffset
1946                = computeRelativeOffset(anchorPoint, anchorNode->boundingBox());
1947            webViewHelper.webViewImpl()->resize(WebSize(viewportSize.height, viewportSize.width));
1948            IntPoint newAnchorPoint = IntPoint(webViewHelper.webViewImpl()->mainFrame()->scrollOffset()) + IntPoint(viewportSize.height / 2, 0);
1949            const FloatSize postResizeRelativeOffset
1950                = computeRelativeOffset(newAnchorPoint, anchorNode->boundingBox());
1951            EXPECT_NEAR(preResizeRelativeOffset.width(), postResizeRelativeOffset.width(), 0.15f);
1952            expectedPageScaleFactor = pageScaleFactor * (shouldScaleRelativeToViewportWidth ? 1 / aspectRatio : 1);
1953            EXPECT_NEAR(expectedPageScaleFactor, webViewHelper.webViewImpl()->pageScaleFactor(), 0.05f);
1954        }
1955    }
1956};
1957
1958TEST_F(WebFrameResizeTest, ResizeYieldsCorrectScrollAndScaleForWidthEqualsDeviceWidth)
1959{
1960    // With width=device-width, pageScaleFactor is preserved across resizes as
1961    // long as the content adjusts according to the device-width.
1962    const char* url = "resize_scroll_mobile.html";
1963    const float initialPageScaleFactor = 1;
1964    const WebSize scrollOffset(0, 50);
1965    const WebSize viewportSize(120, 160);
1966    const bool shouldScaleRelativeToViewportWidth = true;
1967
1968    testResizeYieldsCorrectScrollAndScale(
1969        url, initialPageScaleFactor, scrollOffset, viewportSize, shouldScaleRelativeToViewportWidth);
1970}
1971
1972TEST_F(WebFrameResizeTest, ResizeYieldsCorrectScrollAndScaleForMinimumScale)
1973{
1974    // This tests a scenario where minimum-scale is set to 1.0, but some element
1975    // on the page is slightly larger than the portrait width, so our "natural"
1976    // minimum-scale would be lower. In that case, we should stick to 1.0 scale
1977    // on rotation and not do anything strange.
1978    const char* url = "resize_scroll_minimum_scale.html";
1979    const float initialPageScaleFactor = 1;
1980    const WebSize scrollOffset(0, 0);
1981    const WebSize viewportSize(240, 320);
1982    const bool shouldScaleRelativeToViewportWidth = false;
1983
1984    testResizeYieldsCorrectScrollAndScale(
1985        url, initialPageScaleFactor, scrollOffset, viewportSize, shouldScaleRelativeToViewportWidth);
1986}
1987
1988TEST_F(WebFrameResizeTest, ResizeYieldsCorrectScrollAndScaleForFixedWidth)
1989{
1990    // With a fixed width, pageScaleFactor scales by the relative change in viewport width.
1991    const char* url = "resize_scroll_fixed_width.html";
1992    const float initialPageScaleFactor = 2;
1993    const WebSize scrollOffset(0, 200);
1994    const WebSize viewportSize(240, 320);
1995    const bool shouldScaleRelativeToViewportWidth = true;
1996
1997    testResizeYieldsCorrectScrollAndScale(
1998        url, initialPageScaleFactor, scrollOffset, viewportSize, shouldScaleRelativeToViewportWidth);
1999}
2000
2001TEST_F(WebFrameResizeTest, ResizeYieldsCorrectScrollAndScaleForFixedLayout)
2002{
2003    // With a fixed layout, pageScaleFactor scales by the relative change in viewport width.
2004    const char* url = "resize_scroll_fixed_layout.html";
2005    const float initialPageScaleFactor = 2;
2006    const WebSize scrollOffset(200, 400);
2007    const WebSize viewportSize(320, 240);
2008    const bool shouldScaleRelativeToViewportWidth = true;
2009
2010    testResizeYieldsCorrectScrollAndScale(
2011        url, initialPageScaleFactor, scrollOffset, viewportSize, shouldScaleRelativeToViewportWidth);
2012}
2013
2014TEST_F(WebFrameTest, pageScaleFactorScalesPaintClip)
2015{
2016    UseMockScrollbarSettings mockScrollbarSettings;
2017    registerMockedHttpURLLoad("large-div.html");
2018
2019    FixedLayoutTestWebViewClient client;
2020    client.m_screenInfo.deviceScaleFactor = 1;
2021    int viewportWidth = 50;
2022    int viewportHeight = 50;
2023
2024    FrameTestHelpers::WebViewHelper webViewHelper;
2025    webViewHelper.initializeAndLoad(m_baseURL + "large-div.html", true, 0, &client);
2026    // FIXME: This test breaks if the viewport is enabled before loading the page due to the paint
2027    // calls below not working on composited layers. For some reason, enabling the viewport here
2028    // doesn't cause compositing
2029    webViewHelper.webView()->settings()->setViewportEnabled(true);
2030    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
2031    webViewHelper.webView()->layout();
2032
2033    // Set <1 page scale so that the clip rect should be larger than
2034    // the viewport size as passed into resize().
2035    webViewHelper.webView()->setPageScaleFactor(0.5);
2036
2037    SkBitmap bitmap;
2038    bitmap.allocN32Pixels(200, 200);
2039    bitmap.eraseColor(0);
2040    SkCanvas canvas(bitmap);
2041
2042    GraphicsContext context(&canvas);
2043    context.setRegionTrackingMode(GraphicsContext::RegionTrackingOpaque);
2044
2045    EXPECT_RECT_EQ(IntRect(0, 0, 0, 0), context.opaqueRegion().asRect());
2046
2047    FrameView* view = webViewHelper.webViewImpl()->mainFrameImpl()->frameView();
2048    IntRect paintRect(0, 0, 200, 200);
2049    view->paint(&context, paintRect);
2050
2051    // FIXME: This test broke in release builds when changing the FixedLayoutTestWebViewClient
2052    // to return a non-null layerTreeView, which is what all our shipping configurations do,
2053    // so this is just exposing an existing bug.
2054    // crbug.com/365812
2055#ifndef NDEBUG
2056    int viewportWidthMinusScrollbar = 50 - (view->verticalScrollbar()->isOverlayScrollbar() ? 0 : 15);
2057    int viewportHeightMinusScrollbar = 50 - (view->horizontalScrollbar()->isOverlayScrollbar() ? 0 : 15);
2058    IntRect clippedRect(0, 0, viewportWidthMinusScrollbar * 2, viewportHeightMinusScrollbar * 2);
2059    EXPECT_RECT_EQ(clippedRect, context.opaqueRegion().asRect());
2060#endif
2061}
2062
2063TEST_F(WebFrameTest, pageScaleFactorUpdatesScrollbars)
2064{
2065    UseMockScrollbarSettings mockScrollbarSettings;
2066    registerMockedHttpURLLoad("fixed_layout.html");
2067
2068    FixedLayoutTestWebViewClient client;
2069    client.m_screenInfo.deviceScaleFactor = 1;
2070    int viewportWidth = 640;
2071    int viewportHeight = 480;
2072
2073    FrameTestHelpers::WebViewHelper webViewHelper;
2074    webViewHelper.initializeAndLoad(m_baseURL + "fixed_layout.html", true, 0, &client, enableViewportSettings);
2075    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
2076    webViewHelper.webView()->layout();
2077
2078    FrameView* view = webViewHelper.webViewImpl()->mainFrameImpl()->frameView();
2079    EXPECT_EQ(view->scrollSize(HorizontalScrollbar), view->contentsSize().width() - view->visibleContentRect().width());
2080    EXPECT_EQ(view->scrollSize(VerticalScrollbar), view->contentsSize().height() - view->visibleContentRect().height());
2081
2082    webViewHelper.webView()->setPageScaleFactor(10);
2083
2084    EXPECT_EQ(view->scrollSize(HorizontalScrollbar), view->contentsSize().width() - view->visibleContentRect().width());
2085    EXPECT_EQ(view->scrollSize(VerticalScrollbar), view->contentsSize().height() - view->visibleContentRect().height());
2086}
2087
2088TEST_F(WebFrameTest, CanOverrideScaleLimits)
2089{
2090    UseMockScrollbarSettings mockScrollbarSettings;
2091
2092    registerMockedHttpURLLoad("no_scale_for_you.html");
2093
2094    FixedLayoutTestWebViewClient client;
2095    client.m_screenInfo.deviceScaleFactor = 1;
2096    int viewportWidth = 640;
2097    int viewportHeight = 480;
2098
2099    FrameTestHelpers::WebViewHelper webViewHelper;
2100    webViewHelper.initializeAndLoad(m_baseURL + "no_scale_for_you.html", true, 0, &client, enableViewportSettings);
2101    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
2102
2103    EXPECT_EQ(2.0f, webViewHelper.webView()->minimumPageScaleFactor());
2104    EXPECT_EQ(2.0f, webViewHelper.webView()->maximumPageScaleFactor());
2105
2106    webViewHelper.webView()->setIgnoreViewportTagScaleLimits(true);
2107    webViewHelper.webView()->layout();
2108
2109    EXPECT_EQ(1.0f, webViewHelper.webView()->minimumPageScaleFactor());
2110    EXPECT_EQ(5.0f, webViewHelper.webView()->maximumPageScaleFactor());
2111
2112    webViewHelper.webView()->setIgnoreViewportTagScaleLimits(false);
2113    webViewHelper.webView()->layout();
2114
2115    EXPECT_EQ(2.0f, webViewHelper.webView()->minimumPageScaleFactor());
2116    EXPECT_EQ(2.0f, webViewHelper.webView()->maximumPageScaleFactor());
2117}
2118
2119TEST_F(WebFrameTest, updateOverlayScrollbarLayers)
2120{
2121    UseMockScrollbarSettings mockScrollbarSettings;
2122
2123    registerMockedHttpURLLoad("large-div.html");
2124
2125    int viewWidth = 500;
2126    int viewHeight = 500;
2127
2128    OwnPtr<FakeCompositingWebViewClient> fakeCompositingWebViewClient = adoptPtr(new FakeCompositingWebViewClient());
2129    FrameTestHelpers::WebViewHelper webViewHelper;
2130    webViewHelper.initialize(true, 0, fakeCompositingWebViewClient.get(), &configueCompositingWebView);
2131
2132    webViewHelper.webView()->setPageScaleFactorLimits(1, 1);
2133    webViewHelper.webView()->resize(WebSize(viewWidth, viewHeight));
2134    FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), m_baseURL + "large-div.html");
2135
2136    FrameView* view = webViewHelper.webViewImpl()->mainFrameImpl()->frameView();
2137    EXPECT_TRUE(view->renderView()->compositor()->layerForHorizontalScrollbar());
2138    EXPECT_TRUE(view->renderView()->compositor()->layerForVerticalScrollbar());
2139
2140    webViewHelper.webView()->resize(WebSize(viewWidth * 10, viewHeight * 10));
2141    webViewHelper.webView()->layout();
2142    EXPECT_FALSE(view->renderView()->compositor()->layerForHorizontalScrollbar());
2143    EXPECT_FALSE(view->renderView()->compositor()->layerForVerticalScrollbar());
2144}
2145
2146void setScaleAndScrollAndLayout(WebView* webView, WebPoint scroll, float scale)
2147{
2148    webView->setPageScaleFactor(scale);
2149    webView->setMainFrameScrollOffset(WebPoint(scroll.x, scroll.y));
2150    webView->layout();
2151}
2152
2153TEST_F(WebFrameTest, DivAutoZoomParamsTest)
2154{
2155    registerMockedHttpURLLoad("get_scale_for_auto_zoom_into_div_test.html");
2156
2157    const float deviceScaleFactor = 2.0f;
2158    int viewportWidth = 640 / deviceScaleFactor;
2159    int viewportHeight = 1280 / deviceScaleFactor;
2160    float doubleTapZoomAlreadyLegibleRatio = 1.2f;
2161    FrameTestHelpers::WebViewHelper webViewHelper;
2162    webViewHelper.initializeAndLoad(m_baseURL + "get_scale_for_auto_zoom_into_div_test.html");
2163    webViewHelper.webView()->setDeviceScaleFactor(deviceScaleFactor);
2164    webViewHelper.webView()->setPageScaleFactorLimits(0.01f, 4);
2165    webViewHelper.webView()->setPageScaleFactor(0.5f);
2166    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
2167    webViewHelper.webView()->layout();
2168
2169    WebRect wideDiv(200, 100, 400, 150);
2170    WebRect tallDiv(200, 300, 400, 800);
2171    WebRect doubleTapPointWide(wideDiv.x + 50, wideDiv.y + 50, touchPointPadding, touchPointPadding);
2172    WebRect doubleTapPointTall(tallDiv.x + 50, tallDiv.y + 50, touchPointPadding, touchPointPadding);
2173    WebRect wideBlockBounds;
2174    WebRect tallBlockBounds;
2175    float scale;
2176    WebPoint scroll;
2177
2178    float doubleTapZoomAlreadyLegibleScale = webViewHelper.webViewImpl()->minimumPageScaleFactor() * doubleTapZoomAlreadyLegibleRatio;
2179
2180    // Test double-tap zooming into wide div.
2181    wideBlockBounds = webViewHelper.webViewImpl()->computeBlockBounds(doubleTapPointWide, false);
2182    webViewHelper.webViewImpl()->computeScaleAndScrollForBlockRect(WebPoint(doubleTapPointWide.x, doubleTapPointWide.y), wideBlockBounds, touchPointPadding, doubleTapZoomAlreadyLegibleScale, scale, scroll);
2183    // The div should horizontally fill the screen (modulo margins), and
2184    // vertically centered (modulo integer rounding).
2185    EXPECT_NEAR(viewportWidth / (float) wideDiv.width, scale, 0.1);
2186    EXPECT_NEAR(wideDiv.x, scroll.x, 20);
2187    EXPECT_EQ(0, scroll.y);
2188
2189    setScaleAndScrollAndLayout(webViewHelper.webViewImpl(), scroll, scale);
2190
2191    // Test zoom out back to minimum scale.
2192    wideBlockBounds = webViewHelper.webViewImpl()->computeBlockBounds(doubleTapPointWide, false);
2193    webViewHelper.webViewImpl()->computeScaleAndScrollForBlockRect(WebPoint(doubleTapPointWide.x, doubleTapPointWide.y), wideBlockBounds, touchPointPadding, doubleTapZoomAlreadyLegibleScale, scale, scroll);
2194
2195    scale = webViewHelper.webViewImpl()->minimumPageScaleFactor();
2196    setScaleAndScrollAndLayout(webViewHelper.webViewImpl(), WebPoint(0, 0), scale);
2197
2198    // Test double-tap zooming into tall div.
2199    tallBlockBounds = webViewHelper.webViewImpl()->computeBlockBounds(doubleTapPointTall, false);
2200    webViewHelper.webViewImpl()->computeScaleAndScrollForBlockRect(WebPoint(doubleTapPointTall.x, doubleTapPointTall.y), tallBlockBounds, touchPointPadding, doubleTapZoomAlreadyLegibleScale, scale, scroll);
2201    // The div should start at the top left of the viewport.
2202    EXPECT_NEAR(viewportWidth / (float) tallDiv.width, scale, 0.1);
2203    EXPECT_NEAR(tallDiv.x, scroll.x, 20);
2204    EXPECT_NEAR(tallDiv.y, scroll.y, 20);
2205
2206    // Test for Non-doubletap scaling
2207    // Test zooming into div.
2208    webViewHelper.webViewImpl()->computeScaleAndScrollForBlockRect(WebPoint(250, 250), webViewHelper.webViewImpl()->computeBlockBounds(WebRect(250, 250, 10, 10), true), 0, doubleTapZoomAlreadyLegibleScale, scale, scroll);
2209    EXPECT_NEAR(viewportWidth / (float) wideDiv.width, scale, 0.1);
2210}
2211
2212void simulatePageScale(WebViewImpl* webViewImpl, float& scale)
2213{
2214    IntSize scrollDelta = webViewImpl->fakePageScaleAnimationTargetPositionForTesting() - webViewImpl->mainFrameImpl()->frameView()->scrollPosition();
2215    float scaleDelta = webViewImpl->fakePageScaleAnimationPageScaleForTesting() / webViewImpl->pageScaleFactor();
2216    webViewImpl->applyViewportDeltas(scrollDelta, scaleDelta, 0);
2217    scale = webViewImpl->pageScaleFactor();
2218}
2219
2220void simulateMultiTargetZoom(WebViewImpl* webViewImpl, const WebRect& rect, float& scale)
2221{
2222    if (webViewImpl->zoomToMultipleTargetsRect(rect))
2223        simulatePageScale(webViewImpl, scale);
2224}
2225
2226void simulateDoubleTap(WebViewImpl* webViewImpl, WebPoint& point, float& scale)
2227{
2228    webViewImpl->animateDoubleTapZoom(point);
2229    EXPECT_TRUE(webViewImpl->fakeDoubleTapAnimationPendingForTesting());
2230    simulatePageScale(webViewImpl, scale);
2231}
2232
2233TEST_F(WebFrameTest, DivAutoZoomWideDivTest)
2234{
2235    registerMockedHttpURLLoad("get_wide_div_for_auto_zoom_test.html");
2236
2237    const float deviceScaleFactor = 2.0f;
2238    int viewportWidth = 640 / deviceScaleFactor;
2239    int viewportHeight = 1280 / deviceScaleFactor;
2240    float doubleTapZoomAlreadyLegibleRatio = 1.2f;
2241    FrameTestHelpers::WebViewHelper webViewHelper;
2242    webViewHelper.initializeAndLoad(m_baseURL + "get_wide_div_for_auto_zoom_test.html");
2243    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
2244    webViewHelper.webView()->setPageScaleFactorLimits(1.0f, 4);
2245    webViewHelper.webView()->setDeviceScaleFactor(deviceScaleFactor);
2246    webViewHelper.webView()->setPageScaleFactor(1.0f);
2247    webViewHelper.webView()->layout();
2248
2249    webViewHelper.webViewImpl()->enableFakePageScaleAnimationForTesting(true);
2250
2251    float doubleTapZoomAlreadyLegibleScale = webViewHelper.webViewImpl()->minimumPageScaleFactor() * doubleTapZoomAlreadyLegibleRatio;
2252
2253    WebRect div(0, 100, viewportWidth, 150);
2254    WebPoint point(div.x + 50, div.y + 50);
2255    float scale;
2256    setScaleAndScrollAndLayout(webViewHelper.webViewImpl(), WebPoint(0, 0), (webViewHelper.webViewImpl()->minimumPageScaleFactor()) * (1 + doubleTapZoomAlreadyLegibleRatio) / 2);
2257
2258    simulateDoubleTap(webViewHelper.webViewImpl(), point, scale);
2259    EXPECT_FLOAT_EQ(doubleTapZoomAlreadyLegibleScale, scale);
2260    simulateDoubleTap(webViewHelper.webViewImpl(), point, scale);
2261    EXPECT_FLOAT_EQ(webViewHelper.webViewImpl()->minimumPageScaleFactor(), scale);
2262}
2263
2264TEST_F(WebFrameTest, DivAutoZoomVeryTallTest)
2265{
2266    // When a block is taller than the viewport and a zoom targets a lower part
2267    // of it, then we should keep the target point onscreen instead of snapping
2268    // back up the top of the block.
2269    registerMockedHttpURLLoad("very_tall_div.html");
2270
2271    const float deviceScaleFactor = 2.0f;
2272    int viewportWidth = 640 / deviceScaleFactor;
2273    int viewportHeight = 1280 / deviceScaleFactor;
2274    FrameTestHelpers::WebViewHelper webViewHelper;
2275    webViewHelper.initializeAndLoad(m_baseURL + "very_tall_div.html", true, 0, 0, enableViewportSettings);
2276    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
2277    webViewHelper.webView()->setPageScaleFactorLimits(1.0f, 4);
2278    webViewHelper.webView()->setDeviceScaleFactor(deviceScaleFactor);
2279    webViewHelper.webView()->setPageScaleFactor(1.0f);
2280    webViewHelper.webView()->layout();
2281
2282    WebRect div(200, 300, 400, 5000);
2283    WebPoint point(div.x + 50, div.y + 3000);
2284    float scale;
2285    WebPoint scroll;
2286
2287    WebRect blockBounds = webViewHelper.webViewImpl()->computeBlockBounds(WebRect(point.x, point.y, 0, 0), true);
2288    webViewHelper.webViewImpl()->computeScaleAndScrollForBlockRect(point, blockBounds, 0, 1.0f, scale, scroll);
2289    EXPECT_EQ(scale, 1.0f);
2290    EXPECT_EQ(scroll.y, 2660);
2291}
2292
2293TEST_F(WebFrameTest, DivAutoZoomMultipleDivsTest)
2294{
2295    registerMockedHttpURLLoad("get_multiple_divs_for_auto_zoom_test.html");
2296
2297    const float deviceScaleFactor = 2.0f;
2298    int viewportWidth = 640 / deviceScaleFactor;
2299    int viewportHeight = 1280 / deviceScaleFactor;
2300    float doubleTapZoomAlreadyLegibleRatio = 1.2f;
2301    FrameTestHelpers::WebViewHelper webViewHelper;
2302    webViewHelper.initializeAndLoad(m_baseURL + "get_multiple_divs_for_auto_zoom_test.html");
2303    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
2304    webViewHelper.webView()->setPageScaleFactorLimits(0.5f, 4);
2305    webViewHelper.webView()->setDeviceScaleFactor(deviceScaleFactor);
2306    webViewHelper.webView()->setPageScaleFactor(0.5f);
2307    webViewHelper.webView()->layout();
2308
2309    webViewHelper.webViewImpl()->enableFakePageScaleAnimationForTesting(true);
2310
2311    WebRect topDiv(200, 100, 200, 150);
2312    WebRect bottomDiv(200, 300, 200, 150);
2313    WebPoint topPoint(topDiv.x + 50, topDiv.y + 50);
2314    WebPoint bottomPoint(bottomDiv.x + 50, bottomDiv.y + 50);
2315    float scale;
2316    setScaleAndScrollAndLayout(webViewHelper.webViewImpl(), WebPoint(0, 0), (webViewHelper.webViewImpl()->minimumPageScaleFactor()) * (1 + doubleTapZoomAlreadyLegibleRatio) / 2);
2317
2318    // Test double tap on two different divs
2319    // After first zoom, we should go back to minimum page scale with a second double tap.
2320    simulateDoubleTap(webViewHelper.webViewImpl(), topPoint, scale);
2321    EXPECT_FLOAT_EQ(1, scale);
2322    simulateDoubleTap(webViewHelper.webViewImpl(), bottomPoint, scale);
2323    EXPECT_FLOAT_EQ(webViewHelper.webViewImpl()->minimumPageScaleFactor(), scale);
2324
2325    // If the user pinch zooms after double tap, a second double tap should zoom back to the div.
2326    simulateDoubleTap(webViewHelper.webViewImpl(), topPoint, scale);
2327    EXPECT_FLOAT_EQ(1, scale);
2328    webViewHelper.webViewImpl()->applyViewportDeltas(WebSize(), 0.6f, 0);
2329    simulateDoubleTap(webViewHelper.webViewImpl(), bottomPoint, scale);
2330    EXPECT_FLOAT_EQ(1, scale);
2331    simulateDoubleTap(webViewHelper.webViewImpl(), bottomPoint, scale);
2332    EXPECT_FLOAT_EQ(webViewHelper.webViewImpl()->minimumPageScaleFactor(), scale);
2333
2334    // If we didn't yet get an auto-zoom update and a second double-tap arrives, should go back to minimum scale.
2335    webViewHelper.webViewImpl()->applyViewportDeltas(WebSize(), 1.1f, 0);
2336    webViewHelper.webViewImpl()->animateDoubleTapZoom(topPoint);
2337    EXPECT_TRUE(webViewHelper.webViewImpl()->fakeDoubleTapAnimationPendingForTesting());
2338    simulateDoubleTap(webViewHelper.webViewImpl(), bottomPoint, scale);
2339    EXPECT_FLOAT_EQ(webViewHelper.webViewImpl()->minimumPageScaleFactor(), scale);
2340}
2341
2342TEST_F(WebFrameTest, DivAutoZoomScaleBoundsTest)
2343{
2344    registerMockedHttpURLLoad("get_scale_bounds_check_for_auto_zoom_test.html");
2345
2346    int viewportWidth = 320;
2347    int viewportHeight = 480;
2348    float doubleTapZoomAlreadyLegibleRatio = 1.2f;
2349    FrameTestHelpers::WebViewHelper webViewHelper;
2350    webViewHelper.initializeAndLoad(m_baseURL + "get_scale_bounds_check_for_auto_zoom_test.html");
2351    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
2352    webViewHelper.webView()->setDeviceScaleFactor(1.5f);
2353    webViewHelper.webView()->layout();
2354
2355    webViewHelper.webViewImpl()->enableFakePageScaleAnimationForTesting(true);
2356
2357    WebRect div(200, 100, 200, 150);
2358    WebPoint doubleTapPoint(div.x + 50, div.y + 50);
2359    float scale;
2360
2361    // Test double tap scale bounds.
2362    // minimumPageScale < doubleTapZoomAlreadyLegibleScale < 1
2363    webViewHelper.webView()->setPageScaleFactorLimits(0.5f, 4);
2364    webViewHelper.webView()->layout();
2365    float doubleTapZoomAlreadyLegibleScale = webViewHelper.webViewImpl()->minimumPageScaleFactor() * doubleTapZoomAlreadyLegibleRatio;
2366    setScaleAndScrollAndLayout(webViewHelper.webViewImpl(), WebPoint(0, 0), (webViewHelper.webViewImpl()->minimumPageScaleFactor()) * (1 + doubleTapZoomAlreadyLegibleRatio) / 2);
2367    simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale);
2368    EXPECT_FLOAT_EQ(1, scale);
2369    simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale);
2370    EXPECT_FLOAT_EQ(webViewHelper.webViewImpl()->minimumPageScaleFactor(), scale);
2371    simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale);
2372    EXPECT_FLOAT_EQ(1, scale);
2373
2374    // Zoom in to reset double_tap_zoom_in_effect flag.
2375    webViewHelper.webViewImpl()->applyViewportDeltas(WebSize(), 1.1f, 0);
2376    // 1 < minimumPageScale < doubleTapZoomAlreadyLegibleScale
2377    webViewHelper.webView()->setPageScaleFactorLimits(1.1f, 4);
2378    webViewHelper.webView()->layout();
2379    doubleTapZoomAlreadyLegibleScale = webViewHelper.webViewImpl()->minimumPageScaleFactor() * doubleTapZoomAlreadyLegibleRatio;
2380    setScaleAndScrollAndLayout(webViewHelper.webViewImpl(), WebPoint(0, 0), (webViewHelper.webViewImpl()->minimumPageScaleFactor()) * (1 + doubleTapZoomAlreadyLegibleRatio) / 2);
2381    simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale);
2382    EXPECT_FLOAT_EQ(doubleTapZoomAlreadyLegibleScale, scale);
2383    simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale);
2384    EXPECT_FLOAT_EQ(webViewHelper.webViewImpl()->minimumPageScaleFactor(), scale);
2385    simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale);
2386    EXPECT_FLOAT_EQ(doubleTapZoomAlreadyLegibleScale, scale);
2387
2388    // Zoom in to reset double_tap_zoom_in_effect flag.
2389    webViewHelper.webViewImpl()->applyViewportDeltas(WebSize(), 1.1f, 0);
2390    // minimumPageScale < 1 < doubleTapZoomAlreadyLegibleScale
2391    webViewHelper.webView()->setPageScaleFactorLimits(0.95f, 4);
2392    webViewHelper.webView()->layout();
2393    doubleTapZoomAlreadyLegibleScale = webViewHelper.webViewImpl()->minimumPageScaleFactor() * doubleTapZoomAlreadyLegibleRatio;
2394    setScaleAndScrollAndLayout(webViewHelper.webViewImpl(), WebPoint(0, 0), (webViewHelper.webViewImpl()->minimumPageScaleFactor()) * (1 + doubleTapZoomAlreadyLegibleRatio) / 2);
2395    simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale);
2396    EXPECT_FLOAT_EQ(doubleTapZoomAlreadyLegibleScale, scale);
2397    simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale);
2398    EXPECT_FLOAT_EQ(webViewHelper.webViewImpl()->minimumPageScaleFactor(), scale);
2399    simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale);
2400    EXPECT_FLOAT_EQ(doubleTapZoomAlreadyLegibleScale, scale);
2401}
2402
2403TEST_F(WebFrameTest, DivAutoZoomScaleFontScaleFactorTest)
2404{
2405    registerMockedHttpURLLoad("get_scale_bounds_check_for_auto_zoom_test.html");
2406
2407    int viewportWidth = 320;
2408    int viewportHeight = 480;
2409    float doubleTapZoomAlreadyLegibleRatio = 1.2f;
2410    float accessibilityFontScaleFactor = 1.13f;
2411    FrameTestHelpers::WebViewHelper webViewHelper;
2412    webViewHelper.initializeAndLoad(m_baseURL + "get_scale_bounds_check_for_auto_zoom_test.html");
2413    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
2414    webViewHelper.webView()->layout();
2415
2416    webViewHelper.webViewImpl()->enableFakePageScaleAnimationForTesting(true);
2417    webViewHelper.webViewImpl()->page()->settings().setTextAutosizingEnabled(true);
2418    webViewHelper.webViewImpl()->page()->settings().setAccessibilityFontScaleFactor(accessibilityFontScaleFactor);
2419
2420    WebRect div(200, 100, 200, 150);
2421    WebPoint doubleTapPoint(div.x + 50, div.y + 50);
2422    float scale;
2423
2424    // Test double tap scale bounds.
2425    // minimumPageScale < doubleTapZoomAlreadyLegibleScale < 1 < accessibilityFontScaleFactor
2426    float legibleScale = accessibilityFontScaleFactor;
2427    setScaleAndScrollAndLayout(webViewHelper.webViewImpl(), WebPoint(0, 0), (webViewHelper.webViewImpl()->minimumPageScaleFactor()) * (1 + doubleTapZoomAlreadyLegibleRatio) / 2);
2428    float doubleTapZoomAlreadyLegibleScale = webViewHelper.webViewImpl()->minimumPageScaleFactor() * doubleTapZoomAlreadyLegibleRatio;
2429    webViewHelper.webView()->setPageScaleFactorLimits(0.5f, 4);
2430    webViewHelper.webView()->layout();
2431    simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale);
2432    EXPECT_FLOAT_EQ(legibleScale, scale);
2433    simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale);
2434    EXPECT_FLOAT_EQ(webViewHelper.webViewImpl()->minimumPageScaleFactor(), scale);
2435    simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale);
2436    EXPECT_FLOAT_EQ(legibleScale, scale);
2437
2438    // Zoom in to reset double_tap_zoom_in_effect flag.
2439    webViewHelper.webViewImpl()->applyViewportDeltas(WebSize(), 1.1f, 0);
2440    // 1 < accessibilityFontScaleFactor < minimumPageScale < doubleTapZoomAlreadyLegibleScale
2441    webViewHelper.webView()->setPageScaleFactorLimits(1.0f, 4);
2442    webViewHelper.webView()->layout();
2443    doubleTapZoomAlreadyLegibleScale = webViewHelper.webViewImpl()->minimumPageScaleFactor() * doubleTapZoomAlreadyLegibleRatio;
2444    setScaleAndScrollAndLayout(webViewHelper.webViewImpl(), WebPoint(0, 0), (webViewHelper.webViewImpl()->minimumPageScaleFactor()) * (1 + doubleTapZoomAlreadyLegibleRatio) / 2);
2445    simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale);
2446    EXPECT_FLOAT_EQ(doubleTapZoomAlreadyLegibleScale, scale);
2447    simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale);
2448    EXPECT_FLOAT_EQ(webViewHelper.webViewImpl()->minimumPageScaleFactor(), scale);
2449    simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale);
2450    EXPECT_FLOAT_EQ(doubleTapZoomAlreadyLegibleScale, scale);
2451
2452    // Zoom in to reset double_tap_zoom_in_effect flag.
2453    webViewHelper.webViewImpl()->applyViewportDeltas(WebSize(), 1.1f, 0);
2454    // minimumPageScale < 1 < accessibilityFontScaleFactor < doubleTapZoomAlreadyLegibleScale
2455    webViewHelper.webView()->setPageScaleFactorLimits(0.95f, 4);
2456    webViewHelper.webView()->layout();
2457    doubleTapZoomAlreadyLegibleScale = webViewHelper.webViewImpl()->minimumPageScaleFactor() * doubleTapZoomAlreadyLegibleRatio;
2458    setScaleAndScrollAndLayout(webViewHelper.webViewImpl(), WebPoint(0, 0), (webViewHelper.webViewImpl()->minimumPageScaleFactor()) * (1 + doubleTapZoomAlreadyLegibleRatio) / 2);
2459    simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale);
2460    EXPECT_FLOAT_EQ(doubleTapZoomAlreadyLegibleScale, scale);
2461    simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale);
2462    EXPECT_FLOAT_EQ(webViewHelper.webViewImpl()->minimumPageScaleFactor(), scale);
2463    simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale);
2464    EXPECT_FLOAT_EQ(doubleTapZoomAlreadyLegibleScale, scale);
2465
2466    // Zoom in to reset double_tap_zoom_in_effect flag.
2467    webViewHelper.webViewImpl()->applyViewportDeltas(WebSize(), 1.1f, 0);
2468    // minimumPageScale < 1 < doubleTapZoomAlreadyLegibleScale < accessibilityFontScaleFactor
2469    webViewHelper.webView()->setPageScaleFactorLimits(0.9f, 4);
2470    webViewHelper.webView()->layout();
2471    doubleTapZoomAlreadyLegibleScale = webViewHelper.webViewImpl()->minimumPageScaleFactor() * doubleTapZoomAlreadyLegibleRatio;
2472    setScaleAndScrollAndLayout(webViewHelper.webViewImpl(), WebPoint(0, 0), (webViewHelper.webViewImpl()->minimumPageScaleFactor()) * (1 + doubleTapZoomAlreadyLegibleRatio) / 2);
2473    simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale);
2474    EXPECT_FLOAT_EQ(legibleScale, scale);
2475    simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale);
2476    EXPECT_FLOAT_EQ(webViewHelper.webViewImpl()->minimumPageScaleFactor(), scale);
2477    simulateDoubleTap(webViewHelper.webViewImpl(), doubleTapPoint, scale);
2478    EXPECT_FLOAT_EQ(legibleScale, scale);
2479}
2480
2481TEST_F(WebFrameTest, DivMultipleTargetZoomMultipleDivsTest)
2482{
2483    registerMockedHttpURLLoad("get_multiple_divs_for_auto_zoom_test.html");
2484
2485    const float deviceScaleFactor = 2.0f;
2486    int viewportWidth = 640 / deviceScaleFactor;
2487    int viewportHeight = 1280 / deviceScaleFactor;
2488    float doubleTapZoomAlreadyLegibleRatio = 1.2f;
2489    FrameTestHelpers::WebViewHelper webViewHelper;
2490    webViewHelper.initializeAndLoad(m_baseURL + "get_multiple_divs_for_auto_zoom_test.html");
2491    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
2492    webViewHelper.webView()->setPageScaleFactorLimits(0.5f, 4);
2493    webViewHelper.webView()->setDeviceScaleFactor(deviceScaleFactor);
2494    webViewHelper.webView()->setPageScaleFactor(0.5f);
2495    webViewHelper.webView()->layout();
2496
2497    webViewHelper.webViewImpl()->enableFakePageScaleAnimationForTesting(true);
2498
2499    WebRect viewportRect(0, 0, viewportWidth, viewportHeight);
2500    WebRect topDiv(200, 100, 200, 150);
2501    WebRect bottomDiv(200, 300, 200, 150);
2502    float scale;
2503    setScaleAndScrollAndLayout(webViewHelper.webViewImpl(), WebPoint(0, 0), (webViewHelper.webViewImpl()->minimumPageScaleFactor()) * (1 + doubleTapZoomAlreadyLegibleRatio) / 2);
2504
2505    simulateMultiTargetZoom(webViewHelper.webViewImpl(), topDiv, scale);
2506    EXPECT_FLOAT_EQ(1, scale);
2507    simulateMultiTargetZoom(webViewHelper.webViewImpl(), bottomDiv, scale);
2508    EXPECT_FLOAT_EQ(1, scale);
2509    simulateMultiTargetZoom(webViewHelper.webViewImpl(), viewportRect, scale);
2510    EXPECT_FLOAT_EQ(1, scale);
2511    webViewHelper.webViewImpl()->setPageScaleFactor(webViewHelper.webViewImpl()->minimumPageScaleFactor());
2512    simulateMultiTargetZoom(webViewHelper.webViewImpl(), topDiv, scale);
2513    EXPECT_FLOAT_EQ(1, scale);
2514}
2515
2516TEST_F(WebFrameTest, DivScrollIntoEditableTest)
2517{
2518    registerMockedHttpURLLoad("get_scale_for_zoom_into_editable_test.html");
2519
2520    int viewportWidth = 450;
2521    int viewportHeight = 300;
2522    float leftBoxRatio = 0.3f;
2523    int caretPadding = 10;
2524    float minReadableCaretHeight = 18.0f;
2525    FrameTestHelpers::WebViewHelper webViewHelper;
2526    webViewHelper.initializeAndLoad(m_baseURL + "get_scale_for_zoom_into_editable_test.html");
2527    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
2528    webViewHelper.webView()->setPageScaleFactorLimits(1, 4);
2529    webViewHelper.webView()->layout();
2530    webViewHelper.webView()->setDeviceScaleFactor(1.5f);
2531    webViewHelper.webView()->settings()->setAutoZoomFocusedNodeToLegibleScale(true);
2532
2533    webViewHelper.webViewImpl()->enableFakePageScaleAnimationForTesting(true);
2534
2535    WebRect editBoxWithText(200, 200, 250, 20);
2536    WebRect editBoxWithNoText(200, 250, 250, 20);
2537
2538    // Test scrolling the focused node
2539    // The edit box is shorter and narrower than the viewport when legible.
2540    webViewHelper.webView()->advanceFocus(false);
2541    // Set the caret to the end of the input box.
2542    webViewHelper.webView()->mainFrame()->document().getElementById("EditBoxWithText").to<WebInputElement>().setSelectionRange(1000, 1000);
2543    setScaleAndScrollAndLayout(webViewHelper.webView(), WebPoint(0, 0), 1);
2544    WebRect rect, caret;
2545    webViewHelper.webViewImpl()->selectionBounds(caret, rect);
2546
2547    float scale;
2548    IntPoint scroll;
2549    bool needAnimation;
2550    webViewHelper.webViewImpl()->computeScaleAndScrollForFocusedNode(webViewHelper.webViewImpl()->focusedElement(), scale, scroll, needAnimation);
2551    EXPECT_TRUE(needAnimation);
2552    // The edit box should be left aligned with a margin for possible label.
2553    int hScroll = editBoxWithText.x - leftBoxRatio * viewportWidth / scale;
2554    EXPECT_NEAR(hScroll, scroll.x(), 1);
2555    int vScroll = editBoxWithText.y - (viewportHeight / scale - editBoxWithText.height) / 2;
2556    EXPECT_NEAR(vScroll, scroll.y(), 1);
2557    EXPECT_NEAR(minReadableCaretHeight / caret.height, scale, 0.1);
2558
2559    // The edit box is wider than the viewport when legible.
2560    viewportWidth = 200;
2561    viewportHeight = 150;
2562    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
2563    setScaleAndScrollAndLayout(webViewHelper.webView(), WebPoint(0, 0), 1);
2564    webViewHelper.webViewImpl()->selectionBounds(caret, rect);
2565    webViewHelper.webViewImpl()->computeScaleAndScrollForFocusedNode(webViewHelper.webViewImpl()->focusedElement(), scale, scroll, needAnimation);
2566    EXPECT_TRUE(needAnimation);
2567    // The caret should be right aligned since the caret would be offscreen when the edit box is left aligned.
2568    hScroll = caret.x + caret.width + caretPadding - viewportWidth / scale;
2569    EXPECT_NEAR(hScroll, scroll.x(), 1);
2570    EXPECT_NEAR(minReadableCaretHeight / caret.height, scale, 0.1);
2571
2572    setScaleAndScrollAndLayout(webViewHelper.webView(), WebPoint(0, 0), 1);
2573    // Move focus to edit box with text.
2574    webViewHelper.webView()->advanceFocus(false);
2575    webViewHelper.webViewImpl()->selectionBounds(caret, rect);
2576    webViewHelper.webViewImpl()->computeScaleAndScrollForFocusedNode(webViewHelper.webViewImpl()->focusedElement(), scale, scroll, needAnimation);
2577    EXPECT_TRUE(needAnimation);
2578    // The edit box should be left aligned.
2579    hScroll = editBoxWithNoText.x;
2580    EXPECT_NEAR(hScroll, scroll.x(), 1);
2581    vScroll = editBoxWithNoText.y - (viewportHeight / scale - editBoxWithNoText.height) / 2;
2582    EXPECT_NEAR(vScroll, scroll.y(), 1);
2583    EXPECT_NEAR(minReadableCaretHeight / caret.height, scale, 0.1);
2584
2585    setScaleAndScrollAndLayout(webViewHelper.webViewImpl(), scroll, scale);
2586
2587    // Move focus back to the first edit box.
2588    webViewHelper.webView()->advanceFocus(true);
2589    webViewHelper.webViewImpl()->computeScaleAndScrollForFocusedNode(webViewHelper.webViewImpl()->focusedElement(), scale, scroll, needAnimation);
2590    // The position should have stayed the same since this box was already on screen with the right scale.
2591    EXPECT_FALSE(needAnimation);
2592}
2593
2594TEST_F(WebFrameTest, DivScrollIntoEditablePreservePageScaleTest)
2595{
2596    registerMockedHttpURLLoad("get_scale_for_zoom_into_editable_test.html");
2597
2598    const int viewportWidth = 450;
2599    const int viewportHeight = 300;
2600    const float minReadableCaretHeight = 18.0f;
2601    FrameTestHelpers::WebViewHelper webViewHelper;
2602    webViewHelper.initializeAndLoad(m_baseURL + "get_scale_for_zoom_into_editable_test.html");
2603    webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
2604    webViewHelper.webView()->setPageScaleFactorLimits(1, 4);
2605    webViewHelper.webView()->layout();
2606    webViewHelper.webView()->setDeviceScaleFactor(1.5f);
2607    webViewHelper.webView()->settings()->setAutoZoomFocusedNodeToLegibleScale(true);
2608    webViewHelper.webViewImpl()->enableFakePageScaleAnimationForTesting(true);
2609
2610    const WebRect editBoxWithText(200, 200, 250, 20);
2611
2612    webViewHelper.webView()->advanceFocus(false);
2613    // Set the caret to the begining of the input box.
2614    webViewHelper.webView()->mainFrame()->document().getElementById("EditBoxWithText").to<WebInputElement>().setSelectionRange(0, 0);
2615    setScaleAndScrollAndLayout(webViewHelper.webView(), WebPoint(0, 0), 1);
2616    WebRect rect, caret;
2617    webViewHelper.webViewImpl()->selectionBounds(caret, rect);
2618
2619    // Set page scale twice larger then minimal readable scale
2620    float newScale = minReadableCaretHeight / caret.height * 2.0;
2621    setScaleAndScrollAndLayout(webViewHelper.webView(), WebPoint(0, 0), newScale);
2622
2623    float scale;
2624    IntPoint scroll;
2625    bool needAnimation;
2626    webViewHelper.webViewImpl()->computeScaleAndScrollForFocusedNode(webViewHelper.webViewImpl()->focusedElement(), scale, scroll, needAnimation);
2627    EXPECT_TRUE(needAnimation);
2628    // Edit box and caret should be left alinged
2629    int hScroll = editBoxWithText.x;
2630    EXPECT_NEAR(hScroll, scroll.x(), 1);
2631    int vScroll = editBoxWithText.y - (viewportHeight / scale - editBoxWithText.height) / 2;
2632    EXPECT_NEAR(vScroll, scroll.y(), 1);
2633    // Page scale have to be unchanged
2634    EXPECT_EQ(newScale, scale);
2635
2636    // Set page scale and scroll such that edit box will be under the screen
2637    newScale = 3.0;
2638    hScroll = 200;
2639    setScaleAndScrollAndLayout(webViewHelper.webView(), WebPoint(hScroll, 0), newScale);
2640    webViewHelper.webViewImpl()->computeScaleAndScrollForFocusedNode(webViewHelper.webViewImpl()->focusedElement(), scale, scroll, needAnimation);
2641    EXPECT_TRUE(needAnimation);
2642    // Horizontal scroll have to be the same
2643    EXPECT_NEAR(hScroll, scroll.x(), 1);
2644    vScroll = editBoxWithText.y - (viewportHeight / scale - editBoxWithText.height) / 2;
2645    EXPECT_NEAR(vScroll, scroll.y(), 1);
2646    // Page scale have to be unchanged
2647    EXPECT_EQ(newScale, scale);
2648}
2649
2650class TestReloadDoesntRedirectWebFrameClient : public FrameTestHelpers::TestWebFrameClient {
2651public:
2652    virtual WebNavigationPolicy decidePolicyForNavigation(const NavigationPolicyInfo& info) OVERRIDE
2653    {
2654        EXPECT_FALSE(info.isRedirect);
2655        return WebNavigationPolicyCurrentTab;
2656    }
2657};
2658
2659TEST_F(WebFrameTest, ReloadDoesntSetRedirect)
2660{
2661    // Test for case in http://crbug.com/73104. Reloading a frame very quickly
2662    // would sometimes call decidePolicyForNavigation with isRedirect=true
2663    registerMockedHttpURLLoad("form.html");
2664
2665    TestReloadDoesntRedirectWebFrameClient webFrameClient;
2666    FrameTestHelpers::WebViewHelper webViewHelper;
2667    webViewHelper.initializeAndLoad(m_baseURL + "form.html", false, &webFrameClient);
2668
2669    webViewHelper.webView()->mainFrame()->reload(true);
2670    // start another reload before request is delivered.
2671    FrameTestHelpers::reloadFrameIgnoringCache(webViewHelper.webView()->mainFrame());
2672}
2673
2674class ReloadWithOverrideURLTask : public WebThread::Task {
2675public:
2676    ReloadWithOverrideURLTask(WebFrame* frame, const KURL& url, bool ignoreCache)
2677        : m_frame(frame), m_url(url), m_ignoreCache(ignoreCache)
2678    {
2679    }
2680
2681    virtual void run() OVERRIDE
2682    {
2683        m_frame->reloadWithOverrideURL(m_url, m_ignoreCache);
2684    }
2685
2686private:
2687    WebFrame* const m_frame;
2688    const KURL m_url;
2689    const bool m_ignoreCache;
2690};
2691
2692TEST_F(WebFrameTest, ReloadWithOverrideURLPreservesState)
2693{
2694    const std::string firstURL = "find.html";
2695    const std::string secondURL = "form.html";
2696    const std::string thirdURL = "history.html";
2697    const float pageScaleFactor = 1.1684f;
2698    const int pageWidth = 640;
2699    const int pageHeight = 480;
2700
2701    registerMockedHttpURLLoad(firstURL);
2702    registerMockedHttpURLLoad(secondURL);
2703    registerMockedHttpURLLoad(thirdURL);
2704
2705    FrameTestHelpers::WebViewHelper webViewHelper;
2706    webViewHelper.initializeAndLoad(m_baseURL + firstURL, true);
2707    webViewHelper.webViewImpl()->resize(WebSize(pageWidth, pageHeight));
2708    webViewHelper.webViewImpl()->mainFrame()->setScrollOffset(WebSize(pageWidth / 4, pageHeight / 4));
2709    webViewHelper.webViewImpl()->setPageScaleFactor(pageScaleFactor);
2710
2711    WebSize previousOffset = webViewHelper.webViewImpl()->mainFrame()->scrollOffset();
2712    float previousScale = webViewHelper.webViewImpl()->pageScaleFactor();
2713
2714    // Reload the page using the cache.
2715    Platform::current()->currentThread()->postTask(
2716        new ReloadWithOverrideURLTask(webViewHelper.webViewImpl()->mainFrame(), toKURL(m_baseURL + secondURL), false));
2717    FrameTestHelpers::pumpPendingRequestsDoNotUse(webViewHelper.webViewImpl()->mainFrame());
2718    ASSERT_EQ(previousOffset, webViewHelper.webViewImpl()->mainFrame()->scrollOffset());
2719    ASSERT_EQ(previousScale, webViewHelper.webViewImpl()->pageScaleFactor());
2720
2721    // Reload the page while ignoring the cache.
2722    Platform::current()->currentThread()->postTask(
2723        new ReloadWithOverrideURLTask(webViewHelper.webViewImpl()->mainFrame(), toKURL(m_baseURL + thirdURL), true));
2724    FrameTestHelpers::pumpPendingRequestsDoNotUse(webViewHelper.webViewImpl()->mainFrame());
2725    ASSERT_EQ(previousOffset, webViewHelper.webViewImpl()->mainFrame()->scrollOffset());
2726    ASSERT_EQ(previousScale, webViewHelper.webViewImpl()->pageScaleFactor());
2727}
2728
2729TEST_F(WebFrameTest, ReloadWhileProvisional)
2730{
2731    // Test that reloading while the previous load is still pending does not cause the initial
2732    // request to get lost.
2733    registerMockedHttpURLLoad("fixed_layout.html");
2734
2735    FrameTestHelpers::WebViewHelper webViewHelper;
2736    webViewHelper.initialize();
2737    WebURLRequest request;
2738    request.initialize();
2739    request.setURL(toKURL(m_baseURL + "fixed_layout.html"));
2740    webViewHelper.webView()->mainFrame()->loadRequest(request);
2741    // start reload before first request is delivered.
2742    FrameTestHelpers::reloadFrameIgnoringCache(webViewHelper.webView()->mainFrame());
2743
2744    WebDataSource* dataSource = webViewHelper.webView()->mainFrame()->dataSource();
2745    ASSERT_TRUE(dataSource);
2746    EXPECT_EQ(toKURL(m_baseURL + "fixed_layout.html"), toKURL(dataSource->request().url().spec()));
2747}
2748
2749TEST_F(WebFrameTest, AppendRedirects)
2750{
2751    const std::string firstURL = "about:blank";
2752    const std::string secondURL = "http://www.test.com";
2753
2754    FrameTestHelpers::WebViewHelper webViewHelper;
2755    webViewHelper.initializeAndLoad(firstURL, true);
2756
2757    WebDataSource* dataSource = webViewHelper.webView()->mainFrame()->dataSource();
2758    ASSERT_TRUE(dataSource);
2759    dataSource->appendRedirect(toKURL(secondURL));
2760
2761    WebVector<WebURL> redirects;
2762    dataSource->redirectChain(redirects);
2763    ASSERT_EQ(2U, redirects.size());
2764    EXPECT_EQ(toKURL(firstURL), toKURL(redirects[0].spec().data()));
2765    EXPECT_EQ(toKURL(secondURL), toKURL(redirects[1].spec().data()));
2766}
2767
2768TEST_F(WebFrameTest, IframeRedirect)
2769{
2770    registerMockedHttpURLLoad("iframe_redirect.html");
2771    registerMockedHttpURLLoad("visible_iframe.html");
2772
2773    FrameTestHelpers::WebViewHelper webViewHelper;
2774    webViewHelper.initializeAndLoad(m_baseURL + "iframe_redirect.html", true);
2775    // Pump pending requests one more time. The test page loads script that navigates.
2776    FrameTestHelpers::pumpPendingRequestsDoNotUse(webViewHelper.webView()->mainFrame());
2777
2778    WebFrame* iframe = webViewHelper.webView()->findFrameByName(WebString::fromUTF8("ifr"));
2779    ASSERT_TRUE(iframe);
2780    WebDataSource* iframeDataSource = iframe->dataSource();
2781    ASSERT_TRUE(iframeDataSource);
2782    WebVector<WebURL> redirects;
2783    iframeDataSource->redirectChain(redirects);
2784    ASSERT_EQ(2U, redirects.size());
2785    EXPECT_EQ(toKURL("about:blank"), toKURL(redirects[0].spec().data()));
2786    EXPECT_EQ(toKURL("http://www.test.com/visible_iframe.html"), toKURL(redirects[1].spec().data()));
2787}
2788
2789TEST_F(WebFrameTest, ClearFocusedNodeTest)
2790{
2791    registerMockedHttpURLLoad("iframe_clear_focused_node_test.html");
2792    registerMockedHttpURLLoad("autofocus_input_field_iframe.html");
2793
2794    FrameTestHelpers::WebViewHelper webViewHelper;
2795    webViewHelper.initializeAndLoad(m_baseURL + "iframe_clear_focused_node_test.html", true);
2796
2797    // Clear the focused node.
2798    webViewHelper.webView()->clearFocusedElement();
2799
2800    // Now retrieve the FocusedNode and test it should be null.
2801    EXPECT_EQ(0, webViewHelper.webViewImpl()->focusedElement());
2802}
2803
2804// Implementation of WebFrameClient that tracks the v8 contexts that are created
2805// and destroyed for verification.
2806class ContextLifetimeTestWebFrameClient : public FrameTestHelpers::TestWebFrameClient {
2807public:
2808    struct Notification {
2809    public:
2810        Notification(WebLocalFrame* frame, v8::Handle<v8::Context> context, int worldId)
2811            : frame(frame)
2812            , context(context->GetIsolate(), context)
2813            , worldId(worldId)
2814        {
2815        }
2816
2817        ~Notification()
2818        {
2819            context.Reset();
2820        }
2821
2822        bool Equals(Notification* other)
2823        {
2824            return other && frame == other->frame && context == other->context && worldId == other->worldId;
2825        }
2826
2827        WebLocalFrame* frame;
2828        v8::Persistent<v8::Context> context;
2829        int worldId;
2830    };
2831
2832    virtual ~ContextLifetimeTestWebFrameClient()
2833    {
2834        reset();
2835    }
2836
2837    void reset()
2838    {
2839        for (size_t i = 0; i < createNotifications.size(); ++i)
2840            delete createNotifications[i];
2841
2842        for (size_t i = 0; i < releaseNotifications.size(); ++i)
2843            delete releaseNotifications[i];
2844
2845        createNotifications.clear();
2846        releaseNotifications.clear();
2847    }
2848
2849    std::vector<Notification*> createNotifications;
2850    std::vector<Notification*> releaseNotifications;
2851
2852 private:
2853    virtual void didCreateScriptContext(WebLocalFrame* frame, v8::Handle<v8::Context> context, int extensionGroup, int worldId) OVERRIDE
2854    {
2855        createNotifications.push_back(new Notification(frame, context, worldId));
2856    }
2857
2858    virtual void willReleaseScriptContext(WebLocalFrame* frame, v8::Handle<v8::Context> context, int worldId) OVERRIDE
2859    {
2860        releaseNotifications.push_back(new Notification(frame, context, worldId));
2861    }
2862};
2863
2864// TODO(aa): Deflake this test.
2865TEST_F(WebFrameTest, FLAKY_ContextNotificationsLoadUnload)
2866{
2867    v8::HandleScope handleScope(v8::Isolate::GetCurrent());
2868
2869    registerMockedHttpURLLoad("context_notifications_test.html");
2870    registerMockedHttpURLLoad("context_notifications_test_frame.html");
2871
2872    // Load a frame with an iframe, make sure we get the right create notifications.
2873    ContextLifetimeTestWebFrameClient webFrameClient;
2874    FrameTestHelpers::WebViewHelper webViewHelper;
2875    webViewHelper.initializeAndLoad(m_baseURL + "context_notifications_test.html", true, &webFrameClient);
2876
2877    WebFrame* mainFrame = webViewHelper.webView()->mainFrame();
2878    WebFrame* childFrame = mainFrame->firstChild();
2879
2880    ASSERT_EQ(2u, webFrameClient.createNotifications.size());
2881    EXPECT_EQ(0u, webFrameClient.releaseNotifications.size());
2882
2883    ContextLifetimeTestWebFrameClient::Notification* firstCreateNotification = webFrameClient.createNotifications[0];
2884    ContextLifetimeTestWebFrameClient::Notification* secondCreateNotification = webFrameClient.createNotifications[1];
2885
2886    EXPECT_EQ(mainFrame, firstCreateNotification->frame);
2887    EXPECT_EQ(mainFrame->mainWorldScriptContext(), firstCreateNotification->context);
2888    EXPECT_EQ(0, firstCreateNotification->worldId);
2889
2890    EXPECT_EQ(childFrame, secondCreateNotification->frame);
2891    EXPECT_EQ(childFrame->mainWorldScriptContext(), secondCreateNotification->context);
2892    EXPECT_EQ(0, secondCreateNotification->worldId);
2893
2894    // Close the view. We should get two release notifications that are exactly the same as the create ones, in reverse order.
2895    webViewHelper.reset();
2896
2897    ASSERT_EQ(2u, webFrameClient.releaseNotifications.size());
2898    ContextLifetimeTestWebFrameClient::Notification* firstReleaseNotification = webFrameClient.releaseNotifications[0];
2899    ContextLifetimeTestWebFrameClient::Notification* secondReleaseNotification = webFrameClient.releaseNotifications[1];
2900
2901    ASSERT_TRUE(firstCreateNotification->Equals(secondReleaseNotification));
2902    ASSERT_TRUE(secondCreateNotification->Equals(firstReleaseNotification));
2903}
2904
2905TEST_F(WebFrameTest, ContextNotificationsReload)
2906{
2907    v8::HandleScope handleScope(v8::Isolate::GetCurrent());
2908
2909    registerMockedHttpURLLoad("context_notifications_test.html");
2910    registerMockedHttpURLLoad("context_notifications_test_frame.html");
2911
2912    ContextLifetimeTestWebFrameClient webFrameClient;
2913    FrameTestHelpers::WebViewHelper webViewHelper;
2914    webViewHelper.initializeAndLoad(m_baseURL + "context_notifications_test.html", true, &webFrameClient);
2915
2916    // Refresh, we should get two release notifications and two more create notifications.
2917    FrameTestHelpers::reloadFrame(webViewHelper.webView()->mainFrame());
2918    ASSERT_EQ(4u, webFrameClient.createNotifications.size());
2919    ASSERT_EQ(2u, webFrameClient.releaseNotifications.size());
2920
2921    // The two release notifications we got should be exactly the same as the first two create notifications.
2922    for (size_t i = 0; i < webFrameClient.releaseNotifications.size(); ++i) {
2923      EXPECT_TRUE(webFrameClient.releaseNotifications[i]->Equals(
2924          webFrameClient.createNotifications[webFrameClient.createNotifications.size() - 3 - i]));
2925    }
2926
2927    // The last two create notifications should be for the current frames and context.
2928    WebFrame* mainFrame = webViewHelper.webView()->mainFrame();
2929    WebFrame* childFrame = mainFrame->firstChild();
2930    ContextLifetimeTestWebFrameClient::Notification* firstRefreshNotification = webFrameClient.createNotifications[2];
2931    ContextLifetimeTestWebFrameClient::Notification* secondRefreshNotification = webFrameClient.createNotifications[3];
2932
2933    EXPECT_EQ(mainFrame, firstRefreshNotification->frame);
2934    EXPECT_EQ(mainFrame->mainWorldScriptContext(), firstRefreshNotification->context);
2935    EXPECT_EQ(0, firstRefreshNotification->worldId);
2936
2937    EXPECT_EQ(childFrame, secondRefreshNotification->frame);
2938    EXPECT_EQ(childFrame->mainWorldScriptContext(), secondRefreshNotification->context);
2939    EXPECT_EQ(0, secondRefreshNotification->worldId);
2940}
2941
2942TEST_F(WebFrameTest, ContextNotificationsIsolatedWorlds)
2943{
2944    v8::Isolate* isolate = v8::Isolate::GetCurrent();
2945    v8::HandleScope handleScope(isolate);
2946
2947    registerMockedHttpURLLoad("context_notifications_test.html");
2948    registerMockedHttpURLLoad("context_notifications_test_frame.html");
2949
2950    ContextLifetimeTestWebFrameClient webFrameClient;
2951    FrameTestHelpers::WebViewHelper webViewHelper;
2952    webViewHelper.initializeAndLoad(m_baseURL + "context_notifications_test.html", true, &webFrameClient);
2953
2954    // Add an isolated world.
2955    webFrameClient.reset();
2956
2957    int isolatedWorldId = 42;
2958    WebScriptSource scriptSource("hi!");
2959    int numSources = 1;
2960    int extensionGroup = 0;
2961    webViewHelper.webView()->mainFrame()->executeScriptInIsolatedWorld(isolatedWorldId, &scriptSource, numSources, extensionGroup);
2962
2963    // We should now have a new create notification.
2964    ASSERT_EQ(1u, webFrameClient.createNotifications.size());
2965    ContextLifetimeTestWebFrameClient::Notification* notification = webFrameClient.createNotifications[0];
2966    ASSERT_EQ(isolatedWorldId, notification->worldId);
2967    ASSERT_EQ(webViewHelper.webView()->mainFrame(), notification->frame);
2968
2969    // 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.
2970    ASSERT_NE(webViewHelper.webView()->mainFrame()->mainWorldScriptContext(), v8::Local<v8::Context>::New(isolate, notification->context));
2971
2972    webViewHelper.reset();
2973
2974    // We should have gotten three release notifications (one for each of the frames, plus one for the isolated context).
2975    ASSERT_EQ(3u, webFrameClient.releaseNotifications.size());
2976
2977    // And one of them should be exactly the same as the create notification for the isolated context.
2978    int matchCount = 0;
2979    for (size_t i = 0; i < webFrameClient.releaseNotifications.size(); ++i) {
2980      if (webFrameClient.releaseNotifications[i]->Equals(webFrameClient.createNotifications[0]))
2981        ++matchCount;
2982    }
2983    EXPECT_EQ(1, matchCount);
2984}
2985
2986TEST_F(WebFrameTest, FindInPage)
2987{
2988    registerMockedHttpURLLoad("find.html");
2989    FrameTestHelpers::WebViewHelper webViewHelper;
2990    webViewHelper.initializeAndLoad(m_baseURL + "find.html");
2991    WebFrame* frame = webViewHelper.webView()->mainFrame();
2992    const int findIdentifier = 12345;
2993    WebFindOptions options;
2994
2995    // Find in a <div> element.
2996    EXPECT_TRUE(frame->find(findIdentifier, WebString::fromUTF8("bar1"), options, false, 0));
2997    frame->stopFinding(false);
2998    WebRange range = frame->selectionRange();
2999    EXPECT_EQ(5, range.startOffset());
3000    EXPECT_EQ(9, range.endOffset());
3001    EXPECT_TRUE(frame->document().focusedElement().isNull());
3002
3003    // Find in an <input> value.
3004    EXPECT_TRUE(frame->find(findIdentifier, WebString::fromUTF8("bar2"), options, false, 0));
3005    // Confirm stopFinding(false) sets the selection on the found text.
3006    frame->stopFinding(false);
3007    range = frame->selectionRange();
3008    ASSERT_FALSE(range.isNull());
3009    EXPECT_EQ(5, range.startOffset());
3010    EXPECT_EQ(9, range.endOffset());
3011    EXPECT_EQ(WebString::fromUTF8("INPUT"), frame->document().focusedElement().tagName());
3012
3013    // Find in a <textarea> content.
3014    EXPECT_TRUE(frame->find(findIdentifier, WebString::fromUTF8("bar3"), options, false, 0));
3015    // Confirm stopFinding(false) sets the selection on the found text.
3016    frame->stopFinding(false);
3017    range = frame->selectionRange();
3018    ASSERT_FALSE(range.isNull());
3019    EXPECT_EQ(5, range.startOffset());
3020    EXPECT_EQ(9, range.endOffset());
3021    EXPECT_EQ(WebString::fromUTF8("TEXTAREA"), frame->document().focusedElement().tagName());
3022
3023    // Find in a contentEditable element.
3024    EXPECT_TRUE(frame->find(findIdentifier, WebString::fromUTF8("bar4"), options, false, 0));
3025    // Confirm stopFinding(false) sets the selection on the found text.
3026    frame->stopFinding(false);
3027    range = frame->selectionRange();
3028    ASSERT_FALSE(range.isNull());
3029    EXPECT_EQ(0, range.startOffset());
3030    EXPECT_EQ(4, range.endOffset());
3031    // "bar4" is surrounded by <span>, but the focusable node should be the parent <div>.
3032    EXPECT_EQ(WebString::fromUTF8("DIV"), frame->document().focusedElement().tagName());
3033
3034    // Find in <select> content.
3035    EXPECT_FALSE(frame->find(findIdentifier, WebString::fromUTF8("bar5"), options, false, 0));
3036    // If there are any matches, stopFinding will set the selection on the found text.
3037    // However, we do not expect any matches, so check that the selection is null.
3038    frame->stopFinding(false);
3039    range = frame->selectionRange();
3040    ASSERT_TRUE(range.isNull());
3041}
3042
3043TEST_F(WebFrameTest, GetContentAsPlainText)
3044{
3045    FrameTestHelpers::WebViewHelper webViewHelper;
3046    webViewHelper.initializeAndLoad("about:blank", true);
3047    // We set the size because it impacts line wrapping, which changes the
3048    // resulting text value.
3049    webViewHelper.webView()->resize(WebSize(640, 480));
3050    WebFrame* frame = webViewHelper.webView()->mainFrame();
3051
3052    // Generate a simple test case.
3053    const char simpleSource[] = "<div>Foo bar</div><div></div>baz";
3054    KURL testURL = toKURL("about:blank");
3055    FrameTestHelpers::loadHTMLString(frame, simpleSource, testURL);
3056
3057    // Make sure it comes out OK.
3058    const std::string expected("Foo bar\nbaz");
3059    WebString text = frame->contentAsText(std::numeric_limits<size_t>::max());
3060    EXPECT_EQ(expected, text.utf8());
3061
3062    // Try reading the same one with clipping of the text.
3063    const int length = 5;
3064    text = frame->contentAsText(length);
3065    EXPECT_EQ(expected.substr(0, length), text.utf8());
3066
3067    // Now do a new test with a subframe.
3068    const char outerFrameSource[] = "Hello<iframe></iframe> world";
3069    FrameTestHelpers::loadHTMLString(frame, outerFrameSource, testURL);
3070
3071    // Load something into the subframe.
3072    WebFrame* subframe = frame->firstChild();
3073    ASSERT_TRUE(subframe);
3074    FrameTestHelpers::loadHTMLString(subframe, "sub<p>text", testURL);
3075
3076    text = frame->contentAsText(std::numeric_limits<size_t>::max());
3077    EXPECT_EQ("Hello world\n\nsub\ntext", text.utf8());
3078
3079    // Get the frame text where the subframe separator falls on the boundary of
3080    // what we'll take. There used to be a crash in this case.
3081    text = frame->contentAsText(12);
3082    EXPECT_EQ("Hello world", text.utf8());
3083}
3084
3085TEST_F(WebFrameTest, GetFullHtmlOfPage)
3086{
3087    FrameTestHelpers::WebViewHelper webViewHelper;
3088    webViewHelper.initializeAndLoad("about:blank", true);
3089    WebFrame* frame = webViewHelper.webView()->mainFrame();
3090
3091    // Generate a simple test case.
3092    const char simpleSource[] = "<p>Hello</p><p>World</p>";
3093    KURL testURL = toKURL("about:blank");
3094    FrameTestHelpers::loadHTMLString(frame, simpleSource, testURL);
3095
3096    WebString text = frame->contentAsText(std::numeric_limits<size_t>::max());
3097    EXPECT_EQ("Hello\n\nWorld", text.utf8());
3098
3099    const std::string html = frame->contentAsMarkup().utf8();
3100
3101    // Load again with the output html.
3102    FrameTestHelpers::loadHTMLString(frame, html, testURL);
3103
3104    EXPECT_EQ(html, frame->contentAsMarkup().utf8());
3105
3106    text = frame->contentAsText(std::numeric_limits<size_t>::max());
3107    EXPECT_EQ("Hello\n\nWorld", text.utf8());
3108
3109    // Test selection check
3110    EXPECT_FALSE(frame->hasSelection());
3111    frame->executeCommand(WebString::fromUTF8("SelectAll"));
3112    EXPECT_TRUE(frame->hasSelection());
3113    frame->executeCommand(WebString::fromUTF8("Unselect"));
3114    EXPECT_FALSE(frame->hasSelection());
3115    WebString selectionHtml = frame->selectionAsMarkup();
3116    EXPECT_TRUE(selectionHtml.isEmpty());
3117}
3118
3119class TestExecuteScriptDuringDidCreateScriptContext : public FrameTestHelpers::TestWebFrameClient {
3120public:
3121    virtual void didCreateScriptContext(WebLocalFrame* frame, v8::Handle<v8::Context> context, int extensionGroup, int worldId) OVERRIDE
3122    {
3123        frame->executeScript(WebScriptSource("window.history = 'replaced';"));
3124    }
3125};
3126
3127TEST_F(WebFrameTest, ExecuteScriptDuringDidCreateScriptContext)
3128{
3129    registerMockedHttpURLLoad("hello_world.html");
3130
3131    TestExecuteScriptDuringDidCreateScriptContext webFrameClient;
3132    FrameTestHelpers::WebViewHelper webViewHelper;
3133    webViewHelper.initializeAndLoad(m_baseURL + "hello_world.html", true, &webFrameClient);
3134
3135    FrameTestHelpers::reloadFrame(webViewHelper.webView()->mainFrame());
3136}
3137
3138class FindUpdateWebFrameClient : public FrameTestHelpers::TestWebFrameClient {
3139public:
3140    FindUpdateWebFrameClient()
3141        : m_findResultsAreReady(false)
3142        , m_count(-1)
3143    {
3144    }
3145
3146    virtual void reportFindInPageMatchCount(int, int count, bool finalUpdate) OVERRIDE
3147    {
3148        m_count = count;
3149        if (finalUpdate)
3150            m_findResultsAreReady = true;
3151    }
3152
3153    bool findResultsAreReady() const { return m_findResultsAreReady; }
3154    int count() const { return m_count; }
3155
3156private:
3157    bool m_findResultsAreReady;
3158    int m_count;
3159};
3160
3161// This fails on Mac https://bugs.webkit.org/show_bug.cgi?id=108574
3162// Also failing on Android: http://crbug.com/341314
3163#if OS(MACOSX) || OS(ANDROID)
3164TEST_F(WebFrameTest, DISABLED_FindInPageMatchRects)
3165#else
3166TEST_F(WebFrameTest, FindInPageMatchRects)
3167#endif
3168{
3169    registerMockedHttpURLLoad("find_in_page.html");
3170    registerMockedHttpURLLoad("find_in_page_frame.html");
3171
3172    FindUpdateWebFrameClient client;
3173    FrameTestHelpers::WebViewHelper webViewHelper;
3174    webViewHelper.initializeAndLoad(m_baseURL + "find_in_page.html", true, &client);
3175    webViewHelper.webView()->resize(WebSize(640, 480));
3176    webViewHelper.webView()->layout();
3177    runPendingTasks();
3178
3179    // Note that the 'result 19' in the <select> element is not expected to produce a match.
3180    static const char* kFindString = "result";
3181    static const int kFindIdentifier = 12345;
3182    static const int kNumResults = 19;
3183
3184    WebFindOptions options;
3185    WebString searchText = WebString::fromUTF8(kFindString);
3186    WebLocalFrameImpl* mainFrame = toWebLocalFrameImpl(webViewHelper.webView()->mainFrame());
3187    EXPECT_TRUE(mainFrame->find(kFindIdentifier, searchText, options, false, 0));
3188
3189    mainFrame->resetMatchCount();
3190
3191    for (WebFrame* frame = mainFrame; frame; frame = frame->traverseNext(false))
3192        frame->scopeStringMatches(kFindIdentifier, searchText, options, true);
3193
3194    runPendingTasks();
3195    EXPECT_TRUE(client.findResultsAreReady());
3196
3197    WebVector<WebFloatRect> webMatchRects;
3198    mainFrame->findMatchRects(webMatchRects);
3199    ASSERT_EQ(webMatchRects.size(), static_cast<size_t>(kNumResults));
3200    int rectsVersion = mainFrame->findMatchMarkersVersion();
3201
3202    for (int resultIndex = 0; resultIndex < kNumResults; ++resultIndex) {
3203        FloatRect resultRect = static_cast<FloatRect>(webMatchRects[resultIndex]);
3204
3205        // Select the match by the center of its rect.
3206        EXPECT_EQ(mainFrame->selectNearestFindMatch(resultRect.center(), 0), resultIndex + 1);
3207
3208        // Check that the find result ordering matches with our expectations.
3209        Range* result = mainFrame->activeMatchFrame()->activeMatch();
3210        ASSERT_TRUE(result);
3211        result->setEnd(result->endContainer(), result->endOffset() + 3);
3212        EXPECT_EQ(result->text(), String::format("%s %02d", kFindString, resultIndex));
3213
3214        // Verify that the expected match rect also matches the currently active match.
3215        // Compare the enclosing rects to prevent precision issues caused by CSS transforms.
3216        FloatRect activeMatch = mainFrame->activeFindMatchRect();
3217        EXPECT_EQ(enclosingIntRect(activeMatch), enclosingIntRect(resultRect));
3218
3219        // The rects version should not have changed.
3220        EXPECT_EQ(mainFrame->findMatchMarkersVersion(), rectsVersion);
3221    }
3222
3223    // All results after the first two ones should be below between them in find-in-page coordinates.
3224    // This is because results 2 to 9 are inside an iframe located between results 0 and 1. This applies to the fixed div too.
3225    EXPECT_TRUE(webMatchRects[0].y < webMatchRects[1].y);
3226    for (int resultIndex = 2; resultIndex < kNumResults; ++resultIndex) {
3227        EXPECT_TRUE(webMatchRects[0].y < webMatchRects[resultIndex].y);
3228        EXPECT_TRUE(webMatchRects[1].y > webMatchRects[resultIndex].y);
3229    }
3230
3231    // Result 3 should be below both 2 and 4. This is caused by the CSS transform in the containing div.
3232    // If the transform doesn't work then 3 will be between 2 and 4.
3233    EXPECT_TRUE(webMatchRects[3].y > webMatchRects[2].y);
3234    EXPECT_TRUE(webMatchRects[3].y > webMatchRects[4].y);
3235
3236    // Results 6, 7, 8 and 9 should be one below the other in that same order.
3237    // If overflow:scroll is not properly handled then result 8 would be below result 9 or
3238    // result 7 above result 6 depending on the scroll.
3239    EXPECT_TRUE(webMatchRects[6].y < webMatchRects[7].y);
3240    EXPECT_TRUE(webMatchRects[7].y < webMatchRects[8].y);
3241    EXPECT_TRUE(webMatchRects[8].y < webMatchRects[9].y);
3242
3243    // Results 11, 12, 13 and 14 should be between results 10 and 15, as they are inside the table.
3244    EXPECT_TRUE(webMatchRects[11].y > webMatchRects[10].y);
3245    EXPECT_TRUE(webMatchRects[12].y > webMatchRects[10].y);
3246    EXPECT_TRUE(webMatchRects[13].y > webMatchRects[10].y);
3247    EXPECT_TRUE(webMatchRects[14].y > webMatchRects[10].y);
3248    EXPECT_TRUE(webMatchRects[11].y < webMatchRects[15].y);
3249    EXPECT_TRUE(webMatchRects[12].y < webMatchRects[15].y);
3250    EXPECT_TRUE(webMatchRects[13].y < webMatchRects[15].y);
3251    EXPECT_TRUE(webMatchRects[14].y < webMatchRects[15].y);
3252
3253    // Result 11 should be above 12, 13 and 14 as it's in the table header.
3254    EXPECT_TRUE(webMatchRects[11].y < webMatchRects[12].y);
3255    EXPECT_TRUE(webMatchRects[11].y < webMatchRects[13].y);
3256    EXPECT_TRUE(webMatchRects[11].y < webMatchRects[14].y);
3257
3258    // Result 11 should also be right to 12, 13 and 14 because of the colspan.
3259    EXPECT_TRUE(webMatchRects[11].x > webMatchRects[12].x);
3260    EXPECT_TRUE(webMatchRects[11].x > webMatchRects[13].x);
3261    EXPECT_TRUE(webMatchRects[11].x > webMatchRects[14].x);
3262
3263    // Result 12 should be left to results 11, 13 and 14 in the table layout.
3264    EXPECT_TRUE(webMatchRects[12].x < webMatchRects[11].x);
3265    EXPECT_TRUE(webMatchRects[12].x < webMatchRects[13].x);
3266    EXPECT_TRUE(webMatchRects[12].x < webMatchRects[14].x);
3267
3268    // Results 13, 12 and 14 should be one above the other in that order because of the rowspan
3269    // and vertical-align: middle by default.
3270    EXPECT_TRUE(webMatchRects[13].y < webMatchRects[12].y);
3271    EXPECT_TRUE(webMatchRects[12].y < webMatchRects[14].y);
3272
3273    // Result 16 should be below result 15.
3274    EXPECT_TRUE(webMatchRects[15].y > webMatchRects[14].y);
3275
3276    // Result 18 should be normalized with respect to the position:relative div, and not it's
3277    // immediate containing div. Consequently, result 18 should be above result 17.
3278    EXPECT_TRUE(webMatchRects[17].y > webMatchRects[18].y);
3279
3280    // Resizing should update the rects version.
3281    webViewHelper.webView()->resize(WebSize(800, 600));
3282    runPendingTasks();
3283    EXPECT_TRUE(mainFrame->findMatchMarkersVersion() != rectsVersion);
3284}
3285
3286TEST_F(WebFrameTest, FindInPageSkipsHiddenFrames)
3287{
3288    registerMockedHttpURLLoad("find_in_hidden_frame.html");
3289
3290    FindUpdateWebFrameClient client;
3291    FrameTestHelpers::WebViewHelper webViewHelper;
3292    webViewHelper.initializeAndLoad(m_baseURL + "find_in_hidden_frame.html", true, &client);
3293    webViewHelper.webView()->resize(WebSize(640, 480));
3294    webViewHelper.webView()->layout();
3295    runPendingTasks();
3296
3297    static const char* kFindString = "hello";
3298    static const int kFindIdentifier = 12345;
3299    static const int kNumResults = 1;
3300
3301    WebFindOptions options;
3302    WebString searchText = WebString::fromUTF8(kFindString);
3303    WebLocalFrameImpl* mainFrame = toWebLocalFrameImpl(webViewHelper.webView()->mainFrame());
3304    EXPECT_TRUE(mainFrame->find(kFindIdentifier, searchText, options, false, 0));
3305
3306    mainFrame->resetMatchCount();
3307
3308    for (WebFrame* frame = mainFrame; frame; frame = frame->traverseNext(false))
3309        frame->scopeStringMatches(kFindIdentifier, searchText, options, true);
3310
3311    runPendingTasks();
3312    EXPECT_TRUE(client.findResultsAreReady());
3313    EXPECT_EQ(kNumResults, client.count());
3314}
3315
3316TEST_F(WebFrameTest, FindOnDetachedFrame)
3317{
3318    registerMockedHttpURLLoad("find_in_page.html");
3319    registerMockedHttpURLLoad("find_in_page_frame.html");
3320
3321    FindUpdateWebFrameClient client;
3322    FrameTestHelpers::WebViewHelper webViewHelper;
3323    webViewHelper.initializeAndLoad(m_baseURL + "find_in_page.html", true, &client);
3324    webViewHelper.webView()->resize(WebSize(640, 480));
3325    webViewHelper.webView()->layout();
3326    runPendingTasks();
3327
3328    static const char* kFindString = "result";
3329    static const int kFindIdentifier = 12345;
3330
3331    WebFindOptions options;
3332    WebString searchText = WebString::fromUTF8(kFindString);
3333    WebLocalFrameImpl* mainFrame = toWebLocalFrameImpl(webViewHelper.webView()->mainFrame());
3334    RefPtrWillBeRawPtr<WebLocalFrameImpl> secondFrame = toWebLocalFrameImpl(mainFrame->traverseNext(false));
3335    RefPtrWillBeRawPtr<LocalFrame> holdSecondFrame(secondFrame->frame());
3336
3337    // Detach the frame before finding.
3338    EXPECT_TRUE(mainFrame->document().getElementById("frame").remove());
3339
3340    EXPECT_TRUE(mainFrame->find(kFindIdentifier, searchText, options, false, 0));
3341    EXPECT_FALSE(secondFrame->find(kFindIdentifier, searchText, options, false, 0));
3342
3343    runPendingTasks();
3344    EXPECT_FALSE(client.findResultsAreReady());
3345
3346    mainFrame->resetMatchCount();
3347
3348    for (WebFrame* frame = mainFrame; frame; frame = frame->traverseNext(false))
3349        frame->scopeStringMatches(kFindIdentifier, searchText, options, true);
3350
3351    runPendingTasks();
3352    EXPECT_TRUE(client.findResultsAreReady());
3353}
3354
3355TEST_F(WebFrameTest, FindDetachFrameBeforeScopeStrings)
3356{
3357    registerMockedHttpURLLoad("find_in_page.html");
3358    registerMockedHttpURLLoad("find_in_page_frame.html");
3359
3360    FindUpdateWebFrameClient client;
3361    FrameTestHelpers::WebViewHelper webViewHelper;
3362    webViewHelper.initializeAndLoad(m_baseURL + "find_in_page.html", true, &client);
3363    webViewHelper.webView()->resize(WebSize(640, 480));
3364    webViewHelper.webView()->layout();
3365    runPendingTasks();
3366
3367    static const char* kFindString = "result";
3368    static const int kFindIdentifier = 12345;
3369
3370    WebFindOptions options;
3371    WebString searchText = WebString::fromUTF8(kFindString);
3372    WebLocalFrameImpl* mainFrame = toWebLocalFrameImpl(webViewHelper.webView()->mainFrame());
3373    WebLocalFrameImpl* secondFrame = toWebLocalFrameImpl(mainFrame->traverseNext(false));
3374    RefPtrWillBeRawPtr<LocalFrame> holdSecondFrame(secondFrame->frame());
3375
3376    for (WebFrame* frame = mainFrame; frame; frame = frame->traverseNext(false))
3377        EXPECT_TRUE(frame->find(kFindIdentifier, searchText, options, false, 0));
3378
3379    runPendingTasks();
3380    EXPECT_FALSE(client.findResultsAreReady());
3381
3382    // Detach the frame between finding and scoping.
3383    EXPECT_TRUE(mainFrame->document().getElementById("frame").remove());
3384
3385    mainFrame->resetMatchCount();
3386
3387    for (WebFrame* frame = mainFrame; frame; frame = frame->traverseNext(false))
3388        frame->scopeStringMatches(kFindIdentifier, searchText, options, true);
3389
3390    runPendingTasks();
3391    EXPECT_TRUE(client.findResultsAreReady());
3392}
3393
3394TEST_F(WebFrameTest, FindDetachFrameWhileScopingStrings)
3395{
3396    registerMockedHttpURLLoad("find_in_page.html");
3397    registerMockedHttpURLLoad("find_in_page_frame.html");
3398
3399    FindUpdateWebFrameClient client;
3400    FrameTestHelpers::WebViewHelper webViewHelper;
3401    webViewHelper.initializeAndLoad(m_baseURL + "find_in_page.html", true, &client);
3402    webViewHelper.webView()->resize(WebSize(640, 480));
3403    webViewHelper.webView()->layout();
3404    runPendingTasks();
3405
3406    static const char* kFindString = "result";
3407    static const int kFindIdentifier = 12345;
3408
3409    WebFindOptions options;
3410    WebString searchText = WebString::fromUTF8(kFindString);
3411    WebLocalFrameImpl* mainFrame = toWebLocalFrameImpl(webViewHelper.webView()->mainFrame());
3412    WebLocalFrameImpl* secondFrame = toWebLocalFrameImpl(mainFrame->traverseNext(false));
3413    RefPtrWillBeRawPtr<LocalFrame> holdSecondFrame(secondFrame->frame());
3414
3415    for (WebFrame* frame = mainFrame; frame; frame = frame->traverseNext(false))
3416        EXPECT_TRUE(frame->find(kFindIdentifier, searchText, options, false, 0));
3417
3418    runPendingTasks();
3419    EXPECT_FALSE(client.findResultsAreReady());
3420
3421    mainFrame->resetMatchCount();
3422
3423    for (WebFrame* frame = mainFrame; frame; frame = frame->traverseNext(false))
3424        frame->scopeStringMatches(kFindIdentifier, searchText, options, true);
3425
3426    // The first scopeStringMatches will have reset the state. Detach before it actually scopes.
3427    EXPECT_TRUE(mainFrame->document().getElementById("frame").remove());
3428
3429    runPendingTasks();
3430    EXPECT_TRUE(client.findResultsAreReady());
3431}
3432
3433TEST_F(WebFrameTest, ResetMatchCount)
3434{
3435    registerMockedHttpURLLoad("find_in_generated_frame.html");
3436
3437    FindUpdateWebFrameClient client;
3438    FrameTestHelpers::WebViewHelper webViewHelper;
3439    webViewHelper.initializeAndLoad(m_baseURL + "find_in_generated_frame.html", true, &client);
3440    webViewHelper.webView()->resize(WebSize(640, 480));
3441    webViewHelper.webView()->layout();
3442    runPendingTasks();
3443
3444    static const char* kFindString = "result";
3445    static const int kFindIdentifier = 12345;
3446
3447    WebFindOptions options;
3448    WebString searchText = WebString::fromUTF8(kFindString);
3449    WebLocalFrameImpl* mainFrame = toWebLocalFrameImpl(webViewHelper.webView()->mainFrame());
3450
3451    // Check that child frame exists.
3452    EXPECT_TRUE(!!mainFrame->traverseNext(false));
3453
3454    for (WebFrame* frame = mainFrame; frame; frame = frame->traverseNext(false)) {
3455        EXPECT_FALSE(frame->find(kFindIdentifier, searchText, options, false, 0));
3456    }
3457
3458    runPendingTasks();
3459    EXPECT_FALSE(client.findResultsAreReady());
3460
3461    mainFrame->resetMatchCount();
3462}
3463
3464TEST_F(WebFrameTest, SetTickmarks)
3465{
3466    registerMockedHttpURLLoad("find.html");
3467
3468    FindUpdateWebFrameClient client;
3469    FrameTestHelpers::WebViewHelper webViewHelper;
3470    webViewHelper.initializeAndLoad(m_baseURL + "find.html", true, &client);
3471    webViewHelper.webView()->resize(WebSize(640, 480));
3472    webViewHelper.webView()->layout();
3473    runPendingTasks();
3474
3475    static const char* kFindString = "foo";
3476    static const int kFindIdentifier = 12345;
3477
3478    WebFindOptions options;
3479    WebString searchText = WebString::fromUTF8(kFindString);
3480    WebLocalFrameImpl* mainFrame = toWebLocalFrameImpl(webViewHelper.webView()->mainFrame());
3481    EXPECT_TRUE(mainFrame->find(kFindIdentifier, searchText, options, false, 0));
3482
3483    mainFrame->resetMatchCount();
3484    mainFrame->scopeStringMatches(kFindIdentifier, searchText, options, true);
3485
3486    runPendingTasks();
3487    EXPECT_TRUE(client.findResultsAreReady());
3488
3489    // Get the tickmarks for the original find request.
3490    FrameView* frameView = webViewHelper.webViewImpl()->mainFrameImpl()->frameView();
3491    RefPtr<Scrollbar> scrollbar = frameView->createScrollbar(HorizontalScrollbar);
3492    Vector<IntRect> originalTickmarks;
3493    scrollbar->getTickmarks(originalTickmarks);
3494    EXPECT_EQ(4u, originalTickmarks.size());
3495
3496    // Override the tickmarks.
3497    Vector<IntRect> overridingTickmarksExpected;
3498    overridingTickmarksExpected.append(IntRect(0, 0, 100, 100));
3499    overridingTickmarksExpected.append(IntRect(0, 20, 100, 100));
3500    overridingTickmarksExpected.append(IntRect(0, 30, 100, 100));
3501    mainFrame->setTickmarks(overridingTickmarksExpected);
3502
3503    // Check the tickmarks are overriden correctly.
3504    Vector<IntRect> overridingTickmarksActual;
3505    scrollbar->getTickmarks(overridingTickmarksActual);
3506    EXPECT_EQ(overridingTickmarksExpected, overridingTickmarksActual);
3507
3508    // Reset the tickmark behavior.
3509    Vector<IntRect> resetTickmarks;
3510    mainFrame->setTickmarks(resetTickmarks);
3511
3512    // Check that the original tickmarks are returned
3513    Vector<IntRect> originalTickmarksAfterReset;
3514    scrollbar->getTickmarks(originalTickmarksAfterReset);
3515    EXPECT_EQ(originalTickmarks, originalTickmarksAfterReset);
3516}
3517
3518static WebPoint topLeft(const WebRect& rect)
3519{
3520    return WebPoint(rect.x, rect.y);
3521}
3522
3523static WebPoint bottomRightMinusOne(const WebRect& rect)
3524{
3525    // FIXME: If we don't subtract 1 from the x- and y-coordinates of the
3526    // selection bounds, selectRange() will select the *next* element. That's
3527    // strictly correct, as hit-testing checks the pixel to the lower-right of
3528    // the input coordinate, but it's a wart on the API.
3529    return WebPoint(rect.x + rect.width - 1, rect.y + rect.height - 1);
3530}
3531
3532static WebRect elementBounds(WebFrame* frame, const WebString& id)
3533{
3534    return frame->document().getElementById(id).boundsInViewportSpace();
3535}
3536
3537static std::string selectionAsString(WebFrame* frame)
3538{
3539    return frame->selectionAsText().utf8();
3540}
3541
3542TEST_F(WebFrameTest, SelectRange)
3543{
3544    WebFrame* frame;
3545    WebRect startWebRect;
3546    WebRect endWebRect;
3547
3548    registerMockedHttpURLLoad("select_range_basic.html");
3549    registerMockedHttpURLLoad("select_range_scroll.html");
3550
3551    FrameTestHelpers::WebViewHelper webViewHelper;
3552    initializeTextSelectionWebView(m_baseURL + "select_range_basic.html", &webViewHelper);
3553    frame = webViewHelper.webView()->mainFrame();
3554    EXPECT_EQ("Some test text for testing.", selectionAsString(frame));
3555    webViewHelper.webView()->selectionBounds(startWebRect, endWebRect);
3556    frame->executeCommand(WebString::fromUTF8("Unselect"));
3557    EXPECT_EQ("", selectionAsString(frame));
3558    frame->selectRange(topLeft(startWebRect), bottomRightMinusOne(endWebRect));
3559    EXPECT_EQ("Some test text for testing.", selectionAsString(frame));
3560
3561    initializeTextSelectionWebView(m_baseURL + "select_range_scroll.html", &webViewHelper);
3562    frame = webViewHelper.webView()->mainFrame();
3563    EXPECT_EQ("Some offscreen test text for testing.", selectionAsString(frame));
3564    webViewHelper.webView()->selectionBounds(startWebRect, endWebRect);
3565    frame->executeCommand(WebString::fromUTF8("Unselect"));
3566    EXPECT_EQ("", selectionAsString(frame));
3567    frame->selectRange(topLeft(startWebRect), bottomRightMinusOne(endWebRect));
3568    EXPECT_EQ("Some offscreen test text for testing.", selectionAsString(frame));
3569}
3570
3571TEST_F(WebFrameTest, SelectRangeInIframe)
3572{
3573    WebFrame* frame;
3574    WebRect startWebRect;
3575    WebRect endWebRect;
3576
3577    registerMockedHttpURLLoad("select_range_iframe.html");
3578    registerMockedHttpURLLoad("select_range_basic.html");
3579
3580    FrameTestHelpers::WebViewHelper webViewHelper;
3581    initializeTextSelectionWebView(m_baseURL + "select_range_iframe.html", &webViewHelper);
3582    frame = webViewHelper.webView()->mainFrame();
3583    WebFrame* subframe = frame->firstChild();
3584    EXPECT_EQ("Some test text for testing.", selectionAsString(subframe));
3585    webViewHelper.webView()->selectionBounds(startWebRect, endWebRect);
3586    subframe->executeCommand(WebString::fromUTF8("Unselect"));
3587    EXPECT_EQ("", selectionAsString(subframe));
3588    subframe->selectRange(topLeft(startWebRect), bottomRightMinusOne(endWebRect));
3589    EXPECT_EQ("Some test text for testing.", selectionAsString(subframe));
3590}
3591
3592TEST_F(WebFrameTest, SelectRangeDivContentEditable)
3593{
3594    WebFrame* frame;
3595    WebRect startWebRect;
3596    WebRect endWebRect;
3597
3598    registerMockedHttpURLLoad("select_range_div_editable.html");
3599
3600    // Select the middle of an editable element, then try to extend the selection to the top of the document.
3601    // The selection range should be clipped to the bounds of the editable element.
3602    FrameTestHelpers::WebViewHelper webViewHelper;
3603    initializeTextSelectionWebView(m_baseURL + "select_range_div_editable.html", &webViewHelper);
3604    frame = webViewHelper.webView()->mainFrame();
3605    EXPECT_EQ("This text is initially selected.", selectionAsString(frame));
3606    webViewHelper.webView()->selectionBounds(startWebRect, endWebRect);
3607
3608    frame->selectRange(bottomRightMinusOne(endWebRect), WebPoint(0, 0));
3609    EXPECT_EQ("16-char header. This text is initially selected.", selectionAsString(frame));
3610
3611    // As above, but extending the selection to the bottom of the document.
3612    initializeTextSelectionWebView(m_baseURL + "select_range_div_editable.html", &webViewHelper);
3613    frame = webViewHelper.webView()->mainFrame();
3614
3615    webViewHelper.webView()->selectionBounds(startWebRect, endWebRect);
3616    frame->selectRange(topLeft(startWebRect), bottomRightMinusOne(endWebRect));
3617    EXPECT_EQ("This text is initially selected.", selectionAsString(frame));
3618    webViewHelper.webView()->selectionBounds(startWebRect, endWebRect);
3619
3620    webViewHelper.webView()->selectionBounds(startWebRect, endWebRect);
3621    frame->selectRange(topLeft(startWebRect), WebPoint(640, 480));
3622    EXPECT_EQ("This text is initially selected. 16-char footer.", selectionAsString(frame));
3623}
3624
3625// positionForPoint returns the wrong values for contenteditable spans. See
3626// http://crbug.com/238334.
3627TEST_F(WebFrameTest, DISABLED_SelectRangeSpanContentEditable)
3628{
3629    WebFrame* frame;
3630    WebRect startWebRect;
3631    WebRect endWebRect;
3632
3633    registerMockedHttpURLLoad("select_range_span_editable.html");
3634
3635    // Select the middle of an editable element, then try to extend the selection to the top of the document.
3636    // The selection range should be clipped to the bounds of the editable element.
3637    FrameTestHelpers::WebViewHelper webViewHelper;
3638    initializeTextSelectionWebView(m_baseURL + "select_range_span_editable.html", &webViewHelper);
3639    frame = webViewHelper.webView()->mainFrame();
3640    EXPECT_EQ("This text is initially selected.", selectionAsString(frame));
3641    webViewHelper.webView()->selectionBounds(startWebRect, endWebRect);
3642
3643    frame->selectRange(bottomRightMinusOne(endWebRect), WebPoint(0, 0));
3644    EXPECT_EQ("16-char header. This text is initially selected.", selectionAsString(frame));
3645
3646    // As above, but extending the selection to the bottom of the document.
3647    initializeTextSelectionWebView(m_baseURL + "select_range_span_editable.html", &webViewHelper);
3648    frame = webViewHelper.webView()->mainFrame();
3649
3650    webViewHelper.webView()->selectionBounds(startWebRect, endWebRect);
3651    frame->selectRange(topLeft(startWebRect), bottomRightMinusOne(endWebRect));
3652    EXPECT_EQ("This text is initially selected.", selectionAsString(frame));
3653    webViewHelper.webView()->selectionBounds(startWebRect, endWebRect);
3654
3655    EXPECT_EQ("This text is initially selected.", selectionAsString(frame));
3656    webViewHelper.webView()->selectionBounds(startWebRect, endWebRect);
3657    frame->selectRange(topLeft(startWebRect), WebPoint(640, 480));
3658    EXPECT_EQ("This text is initially selected. 16-char footer.", selectionAsString(frame));
3659}
3660
3661TEST_F(WebFrameTest, SelectRangeCanMoveSelectionStart)
3662{
3663    registerMockedHttpURLLoad("text_selection.html");
3664    FrameTestHelpers::WebViewHelper webViewHelper;
3665    initializeTextSelectionWebView(m_baseURL + "text_selection.html", &webViewHelper);
3666    WebFrame* frame = webViewHelper.webView()->mainFrame();
3667
3668    // Select second span. We can move the start to include the first span.
3669    frame->executeScript(WebScriptSource("selectElement('header_2');"));
3670    EXPECT_EQ("Header 2.", selectionAsString(frame));
3671    frame->selectRange(bottomRightMinusOne(elementBounds(frame, "header_2")), topLeft(elementBounds(frame, "header_1")));
3672    EXPECT_EQ("Header 1. Header 2.", selectionAsString(frame));
3673
3674    // We can move the start and end together.
3675    frame->executeScript(WebScriptSource("selectElement('header_1');"));
3676    EXPECT_EQ("Header 1.", selectionAsString(frame));
3677    frame->selectRange(bottomRightMinusOne(elementBounds(frame, "header_1")), bottomRightMinusOne(elementBounds(frame, "header_1")));
3678    EXPECT_EQ("", selectionAsString(frame));
3679    // Selection is a caret, not empty.
3680    EXPECT_FALSE(frame->selectionRange().isNull());
3681
3682    // We can move the start across the end.
3683    frame->executeScript(WebScriptSource("selectElement('header_1');"));
3684    EXPECT_EQ("Header 1.", selectionAsString(frame));
3685    frame->selectRange(bottomRightMinusOne(elementBounds(frame, "header_1")), bottomRightMinusOne(elementBounds(frame, "header_2")));
3686    EXPECT_EQ(" Header 2.", selectionAsString(frame));
3687
3688    // Can't extend the selection part-way into an editable element.
3689    frame->executeScript(WebScriptSource("selectElement('footer_2');"));
3690    EXPECT_EQ("Footer 2.", selectionAsString(frame));
3691    frame->selectRange(bottomRightMinusOne(elementBounds(frame, "footer_2")), topLeft(elementBounds(frame, "editable_2")));
3692    EXPECT_EQ(" [ Footer 1. Footer 2.", selectionAsString(frame));
3693
3694    // Can extend the selection completely across editable elements.
3695    frame->executeScript(WebScriptSource("selectElement('footer_2');"));
3696    EXPECT_EQ("Footer 2.", selectionAsString(frame));
3697    frame->selectRange(bottomRightMinusOne(elementBounds(frame, "footer_2")), topLeft(elementBounds(frame, "header_2")));
3698    EXPECT_EQ("Header 2. ] [ Editable 1. Editable 2. ] [ Footer 1. Footer 2.", selectionAsString(frame));
3699
3700    // If the selection is editable text, we can't extend it into non-editable text.
3701    frame->executeScript(WebScriptSource("selectElement('editable_2');"));
3702    EXPECT_EQ("Editable 2.", selectionAsString(frame));
3703    frame->selectRange(bottomRightMinusOne(elementBounds(frame, "editable_2")), topLeft(elementBounds(frame, "header_2")));
3704    // positionForPoint returns the wrong values for contenteditable spans. See
3705    // http://crbug.com/238334.
3706    // EXPECT_EQ("[ Editable 1. Editable 2.", selectionAsString(frame));
3707}
3708
3709TEST_F(WebFrameTest, SelectRangeCanMoveSelectionEnd)
3710{
3711    registerMockedHttpURLLoad("text_selection.html");
3712    FrameTestHelpers::WebViewHelper webViewHelper;
3713    initializeTextSelectionWebView(m_baseURL + "text_selection.html", &webViewHelper);
3714    WebFrame* frame = webViewHelper.webView()->mainFrame();
3715
3716    // Select first span. We can move the end to include the second span.
3717    frame->executeScript(WebScriptSource("selectElement('header_1');"));
3718    EXPECT_EQ("Header 1.", selectionAsString(frame));
3719    frame->selectRange(topLeft(elementBounds(frame, "header_1")), bottomRightMinusOne(elementBounds(frame, "header_2")));
3720    EXPECT_EQ("Header 1. Header 2.", selectionAsString(frame));
3721
3722    // We can move the start and end together.
3723    frame->executeScript(WebScriptSource("selectElement('header_2');"));
3724    EXPECT_EQ("Header 2.", selectionAsString(frame));
3725    frame->selectRange(topLeft(elementBounds(frame, "header_2")), topLeft(elementBounds(frame, "header_2")));
3726    EXPECT_EQ("", selectionAsString(frame));
3727    // Selection is a caret, not empty.
3728    EXPECT_FALSE(frame->selectionRange().isNull());
3729
3730    // We can move the end across the start.
3731    frame->executeScript(WebScriptSource("selectElement('header_2');"));
3732    EXPECT_EQ("Header 2.", selectionAsString(frame));
3733    frame->selectRange(topLeft(elementBounds(frame, "header_2")), topLeft(elementBounds(frame, "header_1")));
3734    EXPECT_EQ("Header 1. ", selectionAsString(frame));
3735
3736    // Can't extend the selection part-way into an editable element.
3737    frame->executeScript(WebScriptSource("selectElement('header_1');"));
3738    EXPECT_EQ("Header 1.", selectionAsString(frame));
3739    frame->selectRange(topLeft(elementBounds(frame, "header_1")), bottomRightMinusOne(elementBounds(frame, "editable_1")));
3740    EXPECT_EQ("Header 1. Header 2. ] ", selectionAsString(frame));
3741
3742    // Can extend the selection completely across editable elements.
3743    frame->executeScript(WebScriptSource("selectElement('header_1');"));
3744    EXPECT_EQ("Header 1.", selectionAsString(frame));
3745    frame->selectRange(topLeft(elementBounds(frame, "header_1")), bottomRightMinusOne(elementBounds(frame, "footer_1")));
3746    EXPECT_EQ("Header 1. Header 2. ] [ Editable 1. Editable 2. ] [ Footer 1.", selectionAsString(frame));
3747
3748    // If the selection is editable text, we can't extend it into non-editable text.
3749    frame->executeScript(WebScriptSource("selectElement('editable_1');"));
3750    EXPECT_EQ("Editable 1.", selectionAsString(frame));
3751    frame->selectRange(topLeft(elementBounds(frame, "editable_1")), bottomRightMinusOne(elementBounds(frame, "footer_1")));
3752    // positionForPoint returns the wrong values for contenteditable spans. See
3753    // http://crbug.com/238334.
3754    // EXPECT_EQ("Editable 1. Editable 2. ]", selectionAsString(frame));
3755}
3756
3757static int computeOffset(RenderObject* renderer, int x, int y)
3758{
3759    return VisiblePosition(renderer->positionForPoint(LayoutPoint(x, y))).deepEquivalent().computeOffsetInContainerNode();
3760}
3761
3762// positionForPoint returns the wrong values for contenteditable spans. See
3763// http://crbug.com/238334.
3764TEST_F(WebFrameTest, DISABLED_PositionForPointTest)
3765{
3766    registerMockedHttpURLLoad("select_range_span_editable.html");
3767    FrameTestHelpers::WebViewHelper webViewHelper;
3768    initializeTextSelectionWebView(m_baseURL + "select_range_span_editable.html", &webViewHelper);
3769    WebLocalFrameImpl* mainFrame = toWebLocalFrameImpl(webViewHelper.webView()->mainFrame());
3770    RenderObject* renderer = mainFrame->frame()->selection().rootEditableElement()->renderer();
3771    EXPECT_EQ(0, computeOffset(renderer, -1, -1));
3772    EXPECT_EQ(64, computeOffset(renderer, 1000, 1000));
3773
3774    registerMockedHttpURLLoad("select_range_div_editable.html");
3775    initializeTextSelectionWebView(m_baseURL + "select_range_div_editable.html", &webViewHelper);
3776    mainFrame = toWebLocalFrameImpl(webViewHelper.webView()->mainFrame());
3777    renderer = mainFrame->frame()->selection().rootEditableElement()->renderer();
3778    EXPECT_EQ(0, computeOffset(renderer, -1, -1));
3779    EXPECT_EQ(64, computeOffset(renderer, 1000, 1000));
3780}
3781
3782#if !OS(MACOSX) && !OS(LINUX)
3783TEST_F(WebFrameTest, SelectRangeStaysHorizontallyAlignedWhenMoved)
3784{
3785    registerMockedHttpURLLoad("move_caret.html");
3786
3787    FrameTestHelpers::WebViewHelper webViewHelper;
3788    initializeTextSelectionWebView(m_baseURL + "move_caret.html", &webViewHelper);
3789    WebLocalFrameImpl* frame = toWebLocalFrameImpl(webViewHelper.webView()->mainFrame());
3790
3791    WebRect initialStartRect;
3792    WebRect initialEndRect;
3793    WebRect startRect;
3794    WebRect endRect;
3795
3796    frame->executeScript(WebScriptSource("selectRange();"));
3797    webViewHelper.webView()->selectionBounds(initialStartRect, initialEndRect);
3798    WebPoint movedStart(topLeft(initialStartRect));
3799
3800    movedStart.y += 40;
3801    frame->selectRange(movedStart, bottomRightMinusOne(initialEndRect));
3802    webViewHelper.webView()->selectionBounds(startRect, endRect);
3803    EXPECT_EQ(startRect, initialStartRect);
3804    EXPECT_EQ(endRect, initialEndRect);
3805
3806    movedStart.y -= 80;
3807    frame->selectRange(movedStart, bottomRightMinusOne(initialEndRect));
3808    webViewHelper.webView()->selectionBounds(startRect, endRect);
3809    EXPECT_EQ(startRect, initialStartRect);
3810    EXPECT_EQ(endRect, initialEndRect);
3811
3812    WebPoint movedEnd(bottomRightMinusOne(initialEndRect));
3813
3814    movedEnd.y += 40;
3815    frame->selectRange(topLeft(initialStartRect), movedEnd);
3816    webViewHelper.webView()->selectionBounds(startRect, endRect);
3817    EXPECT_EQ(startRect, initialStartRect);
3818    EXPECT_EQ(endRect, initialEndRect);
3819
3820    movedEnd.y -= 80;
3821    frame->selectRange(topLeft(initialStartRect), movedEnd);
3822    webViewHelper.webView()->selectionBounds(startRect, endRect);
3823    EXPECT_EQ(startRect, initialStartRect);
3824    EXPECT_EQ(endRect, initialEndRect);
3825}
3826
3827TEST_F(WebFrameTest, MoveCaretStaysHorizontallyAlignedWhenMoved)
3828{
3829    WebLocalFrameImpl* frame;
3830    registerMockedHttpURLLoad("move_caret.html");
3831
3832    FrameTestHelpers::WebViewHelper webViewHelper;
3833    initializeTextSelectionWebView(m_baseURL + "move_caret.html", &webViewHelper);
3834    frame = (WebLocalFrameImpl*)webViewHelper.webView()->mainFrame();
3835
3836    WebRect initialStartRect;
3837    WebRect initialEndRect;
3838    WebRect startRect;
3839    WebRect endRect;
3840
3841    frame->executeScript(WebScriptSource("selectCaret();"));
3842    webViewHelper.webView()->selectionBounds(initialStartRect, initialEndRect);
3843    WebPoint moveTo(topLeft(initialStartRect));
3844
3845    moveTo.y += 40;
3846    frame->moveCaretSelection(moveTo);
3847    webViewHelper.webView()->selectionBounds(startRect, endRect);
3848    EXPECT_EQ(startRect, initialStartRect);
3849    EXPECT_EQ(endRect, initialEndRect);
3850
3851    moveTo.y -= 80;
3852    frame->moveCaretSelection(moveTo);
3853    webViewHelper.webView()->selectionBounds(startRect, endRect);
3854    EXPECT_EQ(startRect, initialStartRect);
3855    EXPECT_EQ(endRect, initialEndRect);
3856}
3857#endif
3858
3859class CompositedSelectionBoundsTestLayerTreeView : public WebLayerTreeView {
3860public:
3861    CompositedSelectionBoundsTestLayerTreeView() : m_selectionCleared(false) { }
3862    virtual ~CompositedSelectionBoundsTestLayerTreeView() { }
3863
3864    virtual void setSurfaceReady()  OVERRIDE { }
3865    virtual void setRootLayer(const WebLayer&)  OVERRIDE { }
3866    virtual void clearRootLayer()  OVERRIDE { }
3867    virtual void setViewportSize(const WebSize& deviceViewportSize)  OVERRIDE { }
3868    virtual WebSize deviceViewportSize() const  OVERRIDE { return WebSize(); }
3869    virtual void setDeviceScaleFactor(float) OVERRIDE { }
3870    virtual float deviceScaleFactor() const  OVERRIDE { return 1.f; }
3871    virtual void setBackgroundColor(WebColor)  OVERRIDE { }
3872    virtual void setHasTransparentBackground(bool)  OVERRIDE { }
3873    virtual void setVisible(bool)  OVERRIDE { }
3874    virtual void setPageScaleFactorAndLimits(float pageScaleFactor, float minimum, float maximum)  OVERRIDE { }
3875    virtual void startPageScaleAnimation(const WebPoint& destination, bool useAnchor, float newPageScale, double durationSec)  OVERRIDE { }
3876    virtual void setNeedsAnimate()  OVERRIDE { }
3877    virtual bool commitRequested() const  OVERRIDE { return false; }
3878    virtual void finishAllRendering()  OVERRIDE { }
3879    virtual void registerSelection(const WebSelectionBound& start, const WebSelectionBound& end) OVERRIDE
3880    {
3881        m_start = adoptPtr(new WebSelectionBound(start));
3882        m_end = adoptPtr(new WebSelectionBound(end));
3883    }
3884    virtual void clearSelection() OVERRIDE
3885    {
3886        m_selectionCleared = true;
3887        m_start.clear();
3888        m_end.clear();
3889    }
3890
3891    bool getAndResetSelectionCleared()
3892    {
3893        bool selectionCleared  = m_selectionCleared;
3894        m_selectionCleared = false;
3895        return selectionCleared;
3896    }
3897
3898    const WebSelectionBound* start() const { return m_start.get(); }
3899    const WebSelectionBound* end() const { return m_end.get(); }
3900
3901private:
3902    bool m_selectionCleared;
3903    OwnPtr<WebSelectionBound> m_start;
3904    OwnPtr<WebSelectionBound> m_end;
3905};
3906
3907class CompositedSelectionBoundsTestWebViewClient : public FrameTestHelpers::TestWebViewClient {
3908public:
3909    virtual ~CompositedSelectionBoundsTestWebViewClient() { }
3910    virtual WebLayerTreeView* layerTreeView() OVERRIDE { return &m_testLayerTreeView; }
3911
3912    CompositedSelectionBoundsTestLayerTreeView& selectionLayerTreeView() { return m_testLayerTreeView; }
3913
3914private:
3915    CompositedSelectionBoundsTestLayerTreeView m_testLayerTreeView;
3916};
3917
3918class CompositedSelectionBoundsTest : public WebFrameTest {
3919protected:
3920    CompositedSelectionBoundsTest()
3921        : m_fakeSelectionLayerTreeView(m_fakeSelectionWebViewClient.selectionLayerTreeView())
3922    {
3923        blink::RuntimeEnabledFeatures::setCompositedSelectionUpdateEnabled(true);
3924        registerMockedHttpURLLoad("Ahem.ttf");
3925
3926        m_webViewHelper.initialize(true, 0, &m_fakeSelectionWebViewClient);
3927        m_webViewHelper.webView()->settings()->setDefaultFontSize(12);
3928        m_webViewHelper.webView()->setPageScaleFactorLimits(1, 1);
3929        m_webViewHelper.webView()->resize(WebSize(640, 480));
3930    }
3931
3932    void runTest(const char* testFile)
3933    {
3934        registerMockedHttpURLLoad(testFile);
3935        FrameTestHelpers::loadFrame(m_webViewHelper.webView()->mainFrame(), m_baseURL + testFile);
3936        m_webViewHelper.webView()->layout();
3937
3938        const WebSelectionBound* selectStart = m_fakeSelectionLayerTreeView.start();
3939        const WebSelectionBound* selectEnd = m_fakeSelectionLayerTreeView.end();
3940
3941        v8::HandleScope handleScope(v8::Isolate::GetCurrent());
3942        v8::Handle<v8::Value> result = m_webViewHelper.webView()->mainFrame()->toWebLocalFrame()->executeScriptAndReturnValueForTests(WebScriptSource("expectedResult"));
3943        if (result.IsEmpty() || (*result)->IsUndefined()) {
3944            EXPECT_FALSE(selectStart);
3945            EXPECT_FALSE(selectEnd);
3946            return;
3947        }
3948
3949        ASSERT_TRUE(selectStart);
3950        ASSERT_TRUE(selectEnd);
3951
3952        ASSERT_TRUE((*result)->IsArray());
3953        v8::Array& expectedResult = *v8::Array::Cast(*result);
3954        ASSERT_EQ(10u, expectedResult.Length());
3955
3956        blink::Node* layerOwnerNodeForStart = blink::V8Node::toImplWithTypeCheck(v8::Isolate::GetCurrent(), expectedResult.Get(0));
3957        ASSERT_TRUE(layerOwnerNodeForStart);
3958        EXPECT_EQ(layerOwnerNodeForStart->renderer()->enclosingLayer()->enclosingLayerForPaintInvalidation()->graphicsLayerBacking()->platformLayer()->id(), selectStart->layerId);
3959        EXPECT_EQ(expectedResult.Get(1)->Int32Value(), selectStart->edgeTopInLayer.x);
3960        EXPECT_EQ(expectedResult.Get(2)->Int32Value(), selectStart->edgeTopInLayer.y);
3961        EXPECT_EQ(expectedResult.Get(3)->Int32Value(), selectStart->edgeBottomInLayer.x);
3962        EXPECT_EQ(expectedResult.Get(4)->Int32Value(), selectStart->edgeBottomInLayer.y);
3963
3964        blink::Node* layerOwnerNodeForEnd = blink::V8Node::toImplWithTypeCheck(v8::Isolate::GetCurrent(), expectedResult.Get(5));
3965        ASSERT_TRUE(layerOwnerNodeForEnd);
3966        EXPECT_EQ(layerOwnerNodeForEnd->renderer()->enclosingLayer()->enclosingLayerForPaintInvalidation()->graphicsLayerBacking()->platformLayer()->id(), selectEnd->layerId);
3967        EXPECT_EQ(expectedResult.Get(6)->Int32Value(), selectEnd->edgeTopInLayer.x);
3968        EXPECT_EQ(expectedResult.Get(7)->Int32Value(), selectEnd->edgeTopInLayer.y);
3969        EXPECT_EQ(expectedResult.Get(8)->Int32Value(), selectEnd->edgeBottomInLayer.x);
3970        EXPECT_EQ(expectedResult.Get(9)->Int32Value(), selectEnd->edgeBottomInLayer.y);
3971    }
3972
3973    void runTestWithMultipleFiles(const char* testFile, ...)
3974    {
3975        va_list auxFiles;
3976        va_start(auxFiles, testFile);
3977        while (const char* auxFile = va_arg(auxFiles, const char*))
3978            registerMockedHttpURLLoad(auxFile);
3979        va_end(auxFiles);
3980
3981        runTest(testFile);
3982    }
3983
3984    CompositedSelectionBoundsTestWebViewClient m_fakeSelectionWebViewClient;
3985    CompositedSelectionBoundsTestLayerTreeView& m_fakeSelectionLayerTreeView;
3986    FrameTestHelpers::WebViewHelper m_webViewHelper;
3987};
3988
3989TEST_F(CompositedSelectionBoundsTest, None) { runTest("composited_selection_bounds_none.html"); }
3990TEST_F(CompositedSelectionBoundsTest, Basic) { runTest("composited_selection_bounds_basic.html"); }
3991TEST_F(CompositedSelectionBoundsTest, Transformed) { runTest("composited_selection_bounds_transformed.html"); }
3992TEST_F(CompositedSelectionBoundsTest, SplitLayer) { runTest("composited_selection_bounds_split_layer.html"); }
3993TEST_F(CompositedSelectionBoundsTest, EmptyLayer) { runTest("composited_selection_bounds_empty_layer.html"); }
3994TEST_F(CompositedSelectionBoundsTest, Iframe) { runTestWithMultipleFiles("composited_selection_bounds_iframe.html", "composited_selection_bounds_basic.html", nullptr); }
3995
3996TEST_F(WebFrameTest, CompositedSelectionBoundsCleared)
3997{
3998    RuntimeEnabledFeatures::setCompositedSelectionUpdateEnabled(true);
3999
4000    registerMockedHttpURLLoad("select_range_basic.html");
4001    registerMockedHttpURLLoad("select_range_scroll.html");
4002
4003    int viewWidth = 500;
4004    int viewHeight = 500;
4005
4006    CompositedSelectionBoundsTestWebViewClient fakeSelectionWebViewClient;
4007    CompositedSelectionBoundsTestLayerTreeView& fakeSelectionLayerTreeView = fakeSelectionWebViewClient.selectionLayerTreeView();
4008
4009    FrameTestHelpers::WebViewHelper webViewHelper;
4010    webViewHelper.initialize(true, 0, &fakeSelectionWebViewClient);
4011    webViewHelper.webView()->settings()->setDefaultFontSize(12);
4012    webViewHelper.webView()->setPageScaleFactorLimits(1, 1);
4013    webViewHelper.webView()->resize(WebSize(viewWidth, viewHeight));
4014    FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), m_baseURL + "select_range_basic.html");
4015
4016    // The frame starts with a non-empty selection.
4017    WebFrame* frame = webViewHelper.webView()->mainFrame();
4018    ASSERT_TRUE(frame->hasSelection());
4019    EXPECT_FALSE(fakeSelectionLayerTreeView.getAndResetSelectionCleared());
4020
4021    // The selection cleared notification should be triggered upon layout.
4022    frame->executeCommand(WebString::fromUTF8("Unselect"));
4023    ASSERT_FALSE(frame->hasSelection());
4024    EXPECT_FALSE(fakeSelectionLayerTreeView.getAndResetSelectionCleared());
4025    webViewHelper.webView()->layout();
4026    EXPECT_TRUE(fakeSelectionLayerTreeView.getAndResetSelectionCleared());
4027
4028    frame->executeCommand(WebString::fromUTF8("SelectAll"));
4029    webViewHelper.webView()->layout();
4030    ASSERT_TRUE(frame->hasSelection());
4031    EXPECT_FALSE(fakeSelectionLayerTreeView.getAndResetSelectionCleared());
4032
4033    FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), m_baseURL + "select_range_scroll.html");
4034    ASSERT_TRUE(frame->hasSelection());
4035    EXPECT_FALSE(fakeSelectionLayerTreeView.getAndResetSelectionCleared());
4036
4037    // Transitions between non-empty selections should not trigger a clearing.
4038    WebRect startWebRect;
4039    WebRect endWebRect;
4040    webViewHelper.webViewImpl()->selectionBounds(startWebRect, endWebRect);
4041    WebPoint movedEnd(bottomRightMinusOne(endWebRect));
4042    endWebRect.x -= 20;
4043    frame->selectRange(topLeft(startWebRect), movedEnd);
4044    webViewHelper.webView()->layout();
4045    ASSERT_TRUE(frame->hasSelection());
4046    EXPECT_FALSE(fakeSelectionLayerTreeView.getAndResetSelectionCleared());
4047
4048    frame = webViewHelper.webView()->mainFrame();
4049    frame->executeCommand(WebString::fromUTF8("Unselect"));
4050    webViewHelper.webView()->layout();
4051    ASSERT_FALSE(frame->