• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "config.h"
6 
7 #include "core/frame/PinchViewport.h"
8 
9 #include "core/frame/FrameHost.h"
10 #include "core/frame/LocalFrame.h"
11 #include "core/rendering/RenderObject.h"
12 #include "core/rendering/RenderView.h"
13 #include "core/rendering/compositing/CompositedLayerMapping.h"
14 #include "core/rendering/compositing/RenderLayerCompositor.h"
15 #include "public/platform/Platform.h"
16 #include "public/platform/WebLayerTreeView.h"
17 #include "public/platform/WebUnitTestSupport.h"
18 #include "public/web/WebContextMenuData.h"
19 #include "public/web/WebFrameClient.h"
20 #include "public/web/WebInputEvent.h"
21 #include "public/web/WebScriptSource.h"
22 #include "public/web/WebSettings.h"
23 #include "public/web/WebViewClient.h"
24 #include "web/WebLocalFrameImpl.h"
25 #include "web/tests/FrameTestHelpers.h"
26 #include "web/tests/URLTestHelpers.h"
27 #include <gmock/gmock.h>
28 #include <gtest/gtest.h>
29 
30 #define EXPECT_POINT_EQ(expected, actual) \
31     do { \
32         EXPECT_EQ((expected).x(), (actual).x()); \
33         EXPECT_EQ((expected).y(), (actual).y()); \
34     } while (false)
35 
36 #define EXPECT_FLOAT_POINT_EQ(expected, actual) \
37     do { \
38         EXPECT_FLOAT_EQ((expected).x(), (actual).x()); \
39         EXPECT_FLOAT_EQ((expected).y(), (actual).y()); \
40     } while (false)
41 
42 #define EXPECT_POINT_EQ(expected, actual) \
43     do { \
44         EXPECT_EQ((expected).x(), (actual).x()); \
45         EXPECT_EQ((expected).y(), (actual).y()); \
46     } while (false)
47 
48 #define EXPECT_SIZE_EQ(expected, actual) \
49     do { \
50         EXPECT_EQ((expected).width(), (actual).width()); \
51         EXPECT_EQ((expected).height(), (actual).height()); \
52     } while (false)
53 
54 #define EXPECT_FLOAT_SIZE_EQ(expected, actual) \
55     do { \
56         EXPECT_FLOAT_EQ((expected).width(), (actual).width()); \
57         EXPECT_FLOAT_EQ((expected).height(), (actual).height()); \
58     } while (false)
59 
60 #define EXPECT_FLOAT_RECT_EQ(expected, actual) \
61     do { \
62         EXPECT_FLOAT_EQ((expected).x(), (actual).x()); \
63         EXPECT_FLOAT_EQ((expected).y(), (actual).y()); \
64         EXPECT_FLOAT_EQ((expected).width(), (actual).width()); \
65         EXPECT_FLOAT_EQ((expected).height(), (actual).height()); \
66     } while (false)
67 
68 
69 using namespace WebCore;
70 using namespace blink;
71 
72 using ::testing::_;
73 using ::testing::PrintToString;
74 using ::testing::Mock;
75 
76 namespace blink {
operator <<(::std::ostream & os,const WebContextMenuData & data)77 ::std::ostream& operator<<(::std::ostream& os, const WebContextMenuData& data)
78 {
79     return os << "Context menu location: ["
80         << data.mousePosition.x << ", " << data.mousePosition.y << "]";
81 }
82 }
83 
84 
85 namespace {
86 
87 class PinchViewportTest : public testing::Test {
88 public:
PinchViewportTest()89     PinchViewportTest()
90         : m_baseURL("http://www.test.com/")
91     {
92     }
93 
initializeWithDesktopSettings(void (* overrideSettingsFunc)(WebSettings *)=0)94     void initializeWithDesktopSettings(void (*overrideSettingsFunc)(WebSettings*) = 0)
95     {
96         if (!overrideSettingsFunc)
97             overrideSettingsFunc = &configureSettings;
98         m_helper.initialize(true, 0, &m_mockWebViewClient, overrideSettingsFunc);
99         webViewImpl()->setPageScaleFactorLimits(1, 4);
100     }
101 
initializeWithAndroidSettings(void (* overrideSettingsFunc)(WebSettings *)=0)102     void initializeWithAndroidSettings(void (*overrideSettingsFunc)(WebSettings*) = 0)
103     {
104         if (!overrideSettingsFunc)
105             overrideSettingsFunc = &configureAndroidSettings;
106         m_helper.initialize(true, 0, &m_mockWebViewClient, overrideSettingsFunc);
107     }
108 
~PinchViewportTest()109     virtual ~PinchViewportTest()
110     {
111         Platform::current()->unitTestSupport()->unregisterAllMockedURLs();
112     }
113 
navigateTo(const std::string & url)114     void navigateTo(const std::string& url)
115     {
116         FrameTestHelpers::loadFrame(webViewImpl()->mainFrame(), url);
117     }
118 
forceFullCompositingUpdate()119     void forceFullCompositingUpdate()
120     {
121         webViewImpl()->layout();
122     }
123 
registerMockedHttpURLLoad(const std::string & fileName)124     void registerMockedHttpURLLoad(const std::string& fileName)
125     {
126         URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8(fileName.c_str()));
127     }
128 
getRootScrollLayer()129     WebLayer* getRootScrollLayer()
130     {
131         RenderLayerCompositor* compositor = frame()->contentRenderer()->compositor();
132         ASSERT(compositor);
133         ASSERT(compositor->scrollLayer());
134 
135         WebLayer* webScrollLayer = compositor->scrollLayer()->platformLayer();
136         return webScrollLayer;
137     }
138 
webViewImpl() const139     WebViewImpl* webViewImpl() const { return m_helper.webViewImpl(); }
frame() const140     LocalFrame* frame() const { return m_helper.webViewImpl()->mainFrameImpl()->frame(); }
141 
configureSettings(WebSettings * settings)142     static void configureSettings(WebSettings* settings)
143     {
144         settings->setJavaScriptEnabled(true);
145         settings->setAcceleratedCompositingEnabled(true);
146         settings->setAcceleratedCompositingForFixedPositionEnabled(true);
147         settings->setAcceleratedCompositingForOverflowScrollEnabled(true);
148         settings->setCompositedScrollingForFramesEnabled(true);
149         settings->setPinchVirtualViewportEnabled(true);
150     }
151 
configureAndroidSettings(WebSettings * settings)152     static void configureAndroidSettings(WebSettings* settings)
153     {
154         configureSettings(settings);
155         settings->setViewportEnabled(true);
156         settings->setViewportMetaEnabled(true);
157         settings->setShrinksViewportContentToFit(true);
158     }
159 
160 protected:
161     std::string m_baseURL;
162     FrameTestHelpers::TestWebViewClient m_mockWebViewClient;
163 
164 private:
165     FrameTestHelpers::WebViewHelper m_helper;
166 };
167 
168 // Test that resizing the PinchViewport works as expected and that resizing the
169 // WebView resizes the PinchViewport.
TEST_F(PinchViewportTest,TestResize)170 TEST_F(PinchViewportTest, TestResize)
171 {
172     initializeWithDesktopSettings();
173     webViewImpl()->resize(IntSize(320, 240));
174 
175     navigateTo("about:blank");
176     forceFullCompositingUpdate();
177 
178     PinchViewport& pinchViewport = frame()->page()->frameHost().pinchViewport();
179 
180     IntSize webViewSize = webViewImpl()->size();
181 
182     // Make sure the pinch viewport was initialized.
183     EXPECT_SIZE_EQ(webViewSize, pinchViewport.size());
184 
185     // Resizing the WebView should change the PinchViewport.
186     webViewSize = IntSize(640, 480);
187     webViewImpl()->resize(webViewSize);
188     EXPECT_SIZE_EQ(webViewSize, IntSize(webViewImpl()->size()));
189     EXPECT_SIZE_EQ(webViewSize, pinchViewport.size());
190 
191     // Resizing the pinch viewport shouldn't affect the WebView.
192     IntSize newViewportSize = IntSize(320, 200);
193     pinchViewport.setSize(newViewportSize);
194     EXPECT_SIZE_EQ(webViewSize, IntSize(webViewImpl()->size()));
195     EXPECT_SIZE_EQ(newViewportSize, pinchViewport.size());
196 }
197 
disableAcceleratedCompositing(WebSettings * settings)198 static void disableAcceleratedCompositing(WebSettings* settings)
199 {
200     PinchViewportTest::configureSettings(settings);
201     // FIXME: This setting is being removed, so this test needs to be rewritten to
202     // do something else. crbug.com/173949
203     settings->setAcceleratedCompositingEnabled(false);
204 }
205 
206 // Test that the container layer gets sized properly if the WebView is resized
207 // prior to the PinchViewport being attached to the layer tree.
TEST_F(PinchViewportTest,TestWebViewResizedBeforeAttachment)208 TEST_F(PinchViewportTest, TestWebViewResizedBeforeAttachment)
209 {
210     initializeWithDesktopSettings(disableAcceleratedCompositing);
211     webViewImpl()->resize(IntSize(320, 240));
212 
213     navigateTo("about:blank");
214     forceFullCompositingUpdate();
215     webViewImpl()->settings()->setAcceleratedCompositingEnabled(true);
216     webViewImpl()->layout();
217 
218     PinchViewport& pinchViewport = frame()->page()->frameHost().pinchViewport();
219     EXPECT_FLOAT_SIZE_EQ(FloatSize(320, 240), pinchViewport.containerLayer()->size());
220 }
221 // Make sure that the visibleRect method acurately reflects the scale and scroll location
222 // of the viewport.
TEST_F(PinchViewportTest,TestVisibleRect)223 TEST_F(PinchViewportTest, TestVisibleRect)
224 {
225     initializeWithDesktopSettings();
226     webViewImpl()->resize(IntSize(320, 240));
227 
228     navigateTo("about:blank");
229     forceFullCompositingUpdate();
230 
231     PinchViewport& pinchViewport = frame()->page()->frameHost().pinchViewport();
232 
233     // Initial visible rect should be the whole frame.
234     EXPECT_SIZE_EQ(IntSize(webViewImpl()->size()), pinchViewport.size());
235 
236     // Viewport is whole frame.
237     IntSize size = IntSize(400, 200);
238     webViewImpl()->resize(size);
239     webViewImpl()->layout();
240     pinchViewport.setSize(size);
241 
242     // Scale the viewport to 2X; size should not change.
243     FloatRect expectedRect(FloatPoint(0, 0), size);
244     expectedRect.scale(0.5);
245     pinchViewport.setScale(2);
246     EXPECT_EQ(2, pinchViewport.scale());
247     EXPECT_SIZE_EQ(size, pinchViewport.size());
248     EXPECT_FLOAT_RECT_EQ(expectedRect, pinchViewport.visibleRect());
249 
250     // Move the viewport.
251     expectedRect.setLocation(FloatPoint(5, 7));
252     pinchViewport.setLocation(expectedRect.location());
253     EXPECT_FLOAT_RECT_EQ(expectedRect, pinchViewport.visibleRect());
254 
255     expectedRect.setLocation(FloatPoint(200, 100));
256     pinchViewport.setLocation(expectedRect.location());
257     EXPECT_FLOAT_RECT_EQ(expectedRect, pinchViewport.visibleRect());
258 
259     // Scale the viewport to 3X to introduce some non-int values.
260     FloatPoint oldLocation = expectedRect.location();
261     expectedRect = FloatRect(FloatPoint(), size);
262     expectedRect.scale(1 / 3.0f);
263     expectedRect.setLocation(oldLocation);
264     pinchViewport.setScale(3);
265     EXPECT_FLOAT_RECT_EQ(expectedRect, pinchViewport.visibleRect());
266 
267     expectedRect.setLocation(FloatPoint(0.25f, 0.333f));
268     pinchViewport.setLocation(expectedRect.location());
269     EXPECT_FLOAT_RECT_EQ(expectedRect, pinchViewport.visibleRect());
270 }
271 
272 // Test that the viewport's scroll offset is always appropriately bounded such that the
273 // pinch viewport always stays within the bounds of the main frame.
TEST_F(PinchViewportTest,TestOffsetClamping)274 TEST_F(PinchViewportTest, TestOffsetClamping)
275 {
276     initializeWithDesktopSettings();
277     webViewImpl()->resize(IntSize(320, 240));
278 
279     navigateTo("about:blank");
280     forceFullCompositingUpdate();
281 
282     // Pinch viewport should be initialized to same size as frame so no scrolling possible.
283     PinchViewport& pinchViewport = frame()->page()->frameHost().pinchViewport();
284     EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), pinchViewport.visibleRect().location());
285 
286     pinchViewport.setLocation(FloatPoint(-1, -2));
287     EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), pinchViewport.visibleRect().location());
288 
289     pinchViewport.setLocation(FloatPoint(100, 200));
290     EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), pinchViewport.visibleRect().location());
291 
292     pinchViewport.setLocation(FloatPoint(-5, 10));
293     EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), pinchViewport.visibleRect().location());
294 
295     // Scale by 2x. The viewport's visible rect should now have a size of 160x120.
296     pinchViewport.setScale(2);
297     FloatPoint location(10, 50);
298     pinchViewport.setLocation(location);
299     EXPECT_FLOAT_POINT_EQ(location, pinchViewport.visibleRect().location());
300 
301     pinchViewport.setLocation(FloatPoint(1000, 2000));
302     EXPECT_FLOAT_POINT_EQ(FloatPoint(160, 120), pinchViewport.visibleRect().location());
303 
304     pinchViewport.setLocation(FloatPoint(-1000, -2000));
305     EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), pinchViewport.visibleRect().location());
306 
307     // Make sure offset gets clamped on scale out. Scale to 1.25 so the viewport is 256x192.
308     pinchViewport.setLocation(FloatPoint(160, 120));
309     pinchViewport.setScale(1.25);
310     EXPECT_FLOAT_POINT_EQ(FloatPoint(64, 48), pinchViewport.visibleRect().location());
311 
312     // Scale out smaller than 1.
313     pinchViewport.setScale(0.25);
314     EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), pinchViewport.visibleRect().location());
315 }
316 
317 // Test that the viewport can be scrolled around only within the main frame in the presence
318 // of viewport resizes, as would be the case if the on screen keyboard came up.
TEST_F(PinchViewportTest,TestOffsetClampingWithResize)319 TEST_F(PinchViewportTest, TestOffsetClampingWithResize)
320 {
321     initializeWithDesktopSettings();
322     webViewImpl()->resize(IntSize(320, 240));
323 
324     navigateTo("about:blank");
325     forceFullCompositingUpdate();
326 
327     // Pinch viewport should be initialized to same size as frame so no scrolling possible.
328     PinchViewport& pinchViewport = frame()->page()->frameHost().pinchViewport();
329     EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), pinchViewport.visibleRect().location());
330 
331     // Shrink the viewport vertically. The resize shouldn't affect the location, but it
332     // should allow vertical scrolling.
333     pinchViewport.setSize(IntSize(320, 200));
334     EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), pinchViewport.visibleRect().location());
335     pinchViewport.setLocation(FloatPoint(10, 20));
336     EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 20), pinchViewport.visibleRect().location());
337     pinchViewport.setLocation(FloatPoint(0, 100));
338     EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 40), pinchViewport.visibleRect().location());
339     pinchViewport.setLocation(FloatPoint(0, 10));
340     EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 10), pinchViewport.visibleRect().location());
341     pinchViewport.setLocation(FloatPoint(0, -100));
342     EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), pinchViewport.visibleRect().location());
343 
344     // Repeat the above but for horizontal dimension.
345     pinchViewport.setSize(IntSize(280, 240));
346     EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), pinchViewport.visibleRect().location());
347     pinchViewport.setLocation(FloatPoint(10, 20));
348     EXPECT_FLOAT_POINT_EQ(FloatPoint(10, 0), pinchViewport.visibleRect().location());
349     pinchViewport.setLocation(FloatPoint(100, 0));
350     EXPECT_FLOAT_POINT_EQ(FloatPoint(40, 0), pinchViewport.visibleRect().location());
351     pinchViewport.setLocation(FloatPoint(10, 0));
352     EXPECT_FLOAT_POINT_EQ(FloatPoint(10, 0), pinchViewport.visibleRect().location());
353     pinchViewport.setLocation(FloatPoint(-100, 0));
354     EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), pinchViewport.visibleRect().location());
355 
356     // Now with both dimensions.
357     pinchViewport.setSize(IntSize(280, 200));
358     EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), pinchViewport.visibleRect().location());
359     pinchViewport.setLocation(FloatPoint(10, 20));
360     EXPECT_FLOAT_POINT_EQ(FloatPoint(10, 20), pinchViewport.visibleRect().location());
361     pinchViewport.setLocation(FloatPoint(100, 100));
362     EXPECT_FLOAT_POINT_EQ(FloatPoint(40, 40), pinchViewport.visibleRect().location());
363     pinchViewport.setLocation(FloatPoint(10, 3));
364     EXPECT_FLOAT_POINT_EQ(FloatPoint(10, 3), pinchViewport.visibleRect().location());
365     pinchViewport.setLocation(FloatPoint(-10, -4));
366     EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), pinchViewport.visibleRect().location());
367 }
368 
369 // Test that the viewport is scrollable but bounded appropriately within the main frame
370 // when we apply both scaling and resizes.
TEST_F(PinchViewportTest,TestOffsetClampingWithResizeAndScale)371 TEST_F(PinchViewportTest, TestOffsetClampingWithResizeAndScale)
372 {
373     initializeWithDesktopSettings();
374     webViewImpl()->resize(IntSize(320, 240));
375 
376     navigateTo("about:blank");
377     forceFullCompositingUpdate();
378 
379     // Pinch viewport should be initialized to same size as WebView so no scrolling possible.
380     PinchViewport& pinchViewport = frame()->page()->frameHost().pinchViewport();
381     EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0), pinchViewport.visibleRect().location());
382 
383     // Zoom in to 2X so we can scroll the viewport to 160x120.
384     pinchViewport.setScale(2);
385     pinchViewport.setLocation(FloatPoint(200, 200));
386     EXPECT_FLOAT_POINT_EQ(FloatPoint(160, 120), pinchViewport.visibleRect().location());
387 
388     // Now resize the viewport to make it 10px smaller. Since we're zoomed in by 2X it should
389     // allow us to scroll by 5px more.
390     pinchViewport.setSize(IntSize(310, 230));
391     pinchViewport.setLocation(FloatPoint(200, 200));
392     EXPECT_FLOAT_POINT_EQ(FloatPoint(165, 125), pinchViewport.visibleRect().location());
393 
394     // The viewport can be larger than the main frame (currently 320, 240) though typically
395     // the scale will be clamped to prevent it from actually being larger. Make sure size
396     // changes clamp the offset so the inner remains within the outer.
397     pinchViewport.setSize(IntSize(330, 250));
398     EXPECT_SIZE_EQ(IntSize(330, 250), pinchViewport.size());
399     EXPECT_FLOAT_POINT_EQ(FloatPoint(155, 115), pinchViewport.visibleRect().location());
400     pinchViewport.setLocation(FloatPoint(200, 200));
401     EXPECT_FLOAT_POINT_EQ(FloatPoint(155, 115), pinchViewport.visibleRect().location());
402 
403     // Resize both the viewport and the frame to be larger.
404     webViewImpl()->resize(IntSize(640, 480));
405     webViewImpl()->layout();
406     EXPECT_SIZE_EQ(IntSize(webViewImpl()->size()), pinchViewport.size());
407     EXPECT_SIZE_EQ(IntSize(webViewImpl()->size()), frame()->view()->frameRect().size());
408     pinchViewport.setLocation(FloatPoint(1000, 1000));
409     EXPECT_FLOAT_POINT_EQ(FloatPoint(320, 240), pinchViewport.visibleRect().location());
410 
411     // Make sure resizing the viewport doesn't change its offset if the resize doesn't make
412     // the viewport go out of bounds.
413     pinchViewport.setLocation(FloatPoint(200, 200));
414     pinchViewport.setSize(IntSize(880, 560));
415     EXPECT_FLOAT_POINT_EQ(FloatPoint(200, 200), pinchViewport.visibleRect().location());
416 
417     // Resizing the viewport such that the viewport is out of bounds should move the
418     // viewport.
419     pinchViewport.setSize(IntSize(920, 640));
420     EXPECT_FLOAT_POINT_EQ(FloatPoint(180, 160), pinchViewport.visibleRect().location());
421 }
422 
423 // The main FrameView's size should be set such that its the size of the pinch viewport
424 // at minimum scale. If there's no explicit minimum scale set, the FrameView should be
425 // set to the content width and height derived by the aspect ratio.
TEST_F(PinchViewportTest,TestFrameViewSizedToContent)426 TEST_F(PinchViewportTest, TestFrameViewSizedToContent)
427 {
428     initializeWithAndroidSettings();
429     webViewImpl()->resize(IntSize(320, 240));
430 
431     registerMockedHttpURLLoad("200-by-300-viewport.html");
432     navigateTo(m_baseURL + "200-by-300-viewport.html");
433 
434     webViewImpl()->resize(IntSize(600, 800));
435     webViewImpl()->layout();
436 
437     EXPECT_SIZE_EQ(IntSize(200, 266),
438         webViewImpl()->mainFrameImpl()->frameView()->frameRect().size());
439 }
440 
441 // The main FrameView's size should be set such that its the size of the pinch viewport
442 // at minimum scale. On Desktop, the minimum scale is set at 1 so make sure the FrameView
443 // is sized to the viewport.
TEST_F(PinchViewportTest,TestFrameViewSizedToMinimumScale)444 TEST_F(PinchViewportTest, TestFrameViewSizedToMinimumScale)
445 {
446     initializeWithDesktopSettings();
447     webViewImpl()->resize(IntSize(320, 240));
448 
449     registerMockedHttpURLLoad("200-by-300.html");
450     navigateTo(m_baseURL + "200-by-300.html");
451 
452     webViewImpl()->resize(IntSize(100, 160));
453     webViewImpl()->layout();
454 
455     EXPECT_SIZE_EQ(IntSize(100, 160),
456         webViewImpl()->mainFrameImpl()->frameView()->frameRect().size());
457 }
458 
459 // The main FrameView's size should be set such that its the size of the pinch viewport
460 // at minimum scale. Test that the FrameView is appropriately sized in the presence
461 // of a viewport <meta> tag.
TEST_F(PinchViewportTest,TestFrameViewSizedToViewportMetaMinimumScale)462 TEST_F(PinchViewportTest, TestFrameViewSizedToViewportMetaMinimumScale)
463 {
464     initializeWithAndroidSettings();
465     webViewImpl()->resize(IntSize(320, 240));
466 
467     registerMockedHttpURLLoad("200-by-300-min-scale-2.html");
468     navigateTo(m_baseURL + "200-by-300-min-scale-2.html");
469 
470     webViewImpl()->resize(IntSize(100, 160));
471     webViewImpl()->layout();
472 
473     EXPECT_SIZE_EQ(IntSize(50, 80),
474         webViewImpl()->mainFrameImpl()->frameView()->frameRect().size());
475 }
476 
477 // Test that the pinch viewport still gets sized in AutoSize/AutoResize mode.
TEST_F(PinchViewportTest,TestPinchViewportGetsSizeInAutoSizeMode)478 TEST_F(PinchViewportTest, TestPinchViewportGetsSizeInAutoSizeMode)
479 {
480     initializeWithDesktopSettings();
481 
482     EXPECT_SIZE_EQ(IntSize(0, 0), IntSize(webViewImpl()->size()));
483     EXPECT_SIZE_EQ(IntSize(0, 0), frame()->page()->frameHost().pinchViewport().size());
484 
485     webViewImpl()->enableAutoResizeMode(WebSize(10, 10), WebSize(1000, 1000));
486 
487     registerMockedHttpURLLoad("200-by-300.html");
488     navigateTo(m_baseURL + "200-by-300.html");
489 
490     EXPECT_SIZE_EQ(IntSize(200, 300), frame()->page()->frameHost().pinchViewport().size());
491 }
492 
493 // Test that the text selection handle's position accounts for the pinch viewport.
TEST_F(PinchViewportTest,TestTextSelectionHandles)494 TEST_F(PinchViewportTest, TestTextSelectionHandles)
495 {
496     initializeWithDesktopSettings();
497     webViewImpl()->resize(IntSize(500, 800));
498 
499     registerMockedHttpURLLoad("pinch-viewport-input-field.html");
500     navigateTo(m_baseURL + "pinch-viewport-input-field.html");
501 
502     PinchViewport& pinchViewport = frame()->page()->frameHost().pinchViewport();
503     webViewImpl()->setInitialFocus(false);
504 
505     WebRect originalAnchor;
506     WebRect originalFocus;
507     webViewImpl()->selectionBounds(originalAnchor, originalFocus);
508 
509     webViewImpl()->setPageScaleFactor(2);
510     pinchViewport.setLocation(FloatPoint(100, 400));
511 
512     WebRect anchor;
513     WebRect focus;
514     webViewImpl()->selectionBounds(anchor, focus);
515 
516     IntPoint expected(IntRect(originalAnchor).location());
517     expected.moveBy(-flooredIntPoint(pinchViewport.visibleRect().location()));
518     expected.scale(pinchViewport.scale(), pinchViewport.scale());
519 
520     EXPECT_POINT_EQ(expected, IntRect(anchor).location());
521     EXPECT_POINT_EQ(expected, IntRect(focus).location());
522 
523     // FIXME(bokan) - http://crbug.com/364154 - Figure out how to test text selection
524     // as well rather than just carret.
525 }
526 
527 // Test that the HistoryItem for the page stores the pinch viewport's offset and scale.
TEST_F(PinchViewportTest,TestSavedToHistoryItem)528 TEST_F(PinchViewportTest, TestSavedToHistoryItem)
529 {
530     initializeWithDesktopSettings();
531     webViewImpl()->resize(IntSize(200, 300));
532     webViewImpl()->layout();
533 
534     registerMockedHttpURLLoad("200-by-300.html");
535     navigateTo(m_baseURL + "200-by-300.html");
536 
537     EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 0),
538         toLocalFrame(webViewImpl()->page()->mainFrame())->loader().currentItem()->pinchViewportScrollPoint());
539 
540     PinchViewport& pinchViewport = frame()->page()->frameHost().pinchViewport();
541     pinchViewport.setScale(2);
542 
543     EXPECT_EQ(2, toLocalFrame(webViewImpl()->page()->mainFrame())->loader().currentItem()->pageScaleFactor());
544 
545     pinchViewport.setLocation(FloatPoint(10, 20));
546 
547     EXPECT_FLOAT_POINT_EQ(FloatPoint(10, 20),
548         toLocalFrame(webViewImpl()->page()->mainFrame())->loader().currentItem()->pinchViewportScrollPoint());
549 }
550 
551 // Test restoring a HistoryItem properly restores the pinch viewport's state.
TEST_F(PinchViewportTest,TestRestoredFromHistoryItem)552 TEST_F(PinchViewportTest, TestRestoredFromHistoryItem)
553 {
554     initializeWithDesktopSettings();
555     webViewImpl()->resize(IntSize(200, 300));
556 
557     registerMockedHttpURLLoad("200-by-300.html");
558 
559     WebHistoryItem item;
560     item.initialize();
561     WebURL destinationURL(blink::URLTestHelpers::toKURL(m_baseURL + "200-by-300.html"));
562     item.setURLString(destinationURL.string());
563     item.setPinchViewportScrollOffset(WebFloatPoint(100, 120));
564     item.setPageScaleFactor(2);
565 
566     FrameTestHelpers::loadHistoryItem(webViewImpl()->mainFrame(), item, WebHistoryDifferentDocumentLoad, WebURLRequest::UseProtocolCachePolicy);
567 
568     PinchViewport& pinchViewport = frame()->page()->frameHost().pinchViewport();
569     EXPECT_EQ(2, pinchViewport.scale());
570 
571     EXPECT_FLOAT_POINT_EQ(FloatPoint(100, 120), pinchViewport.visibleRect().location());
572 }
573 
574 // Test restoring a HistoryItem without the pinch viewport offset falls back to distributing
575 // the scroll offset between the main frame and the pinch viewport.
TEST_F(PinchViewportTest,TestRestoredFromLegacyHistoryItem)576 TEST_F(PinchViewportTest, TestRestoredFromLegacyHistoryItem)
577 {
578     initializeWithDesktopSettings();
579     webViewImpl()->resize(IntSize(100, 150));
580 
581     registerMockedHttpURLLoad("200-by-300-viewport.html");
582 
583     WebHistoryItem item;
584     item.initialize();
585     WebURL destinationURL(blink::URLTestHelpers::toKURL(m_baseURL + "200-by-300-viewport.html"));
586     item.setURLString(destinationURL.string());
587     // (-1, -1) will be used if the HistoryItem is an older version prior to having
588     // pinch viewport scroll offset.
589     item.setPinchViewportScrollOffset(WebFloatPoint(-1, -1));
590     item.setScrollOffset(WebPoint(120, 180));
591     item.setPageScaleFactor(2);
592 
593     FrameTestHelpers::loadHistoryItem(webViewImpl()->mainFrame(), item, WebHistoryDifferentDocumentLoad, WebURLRequest::UseProtocolCachePolicy);
594 
595     PinchViewport& pinchViewport = frame()->page()->frameHost().pinchViewport();
596     EXPECT_EQ(2, pinchViewport.scale());
597     EXPECT_POINT_EQ(IntPoint(100, 150), frame()->view()->scrollPosition());
598     EXPECT_FLOAT_POINT_EQ(FloatPoint(20, 30), pinchViewport.visibleRect().location());
599 }
600 
601 // Test that the coordinates sent into moveRangeSelection are offset by the
602 // pinch viewport's location.
TEST_F(PinchViewportTest,TestWebFrameRangeAccountsForPinchViewportScroll)603 TEST_F(PinchViewportTest, TestWebFrameRangeAccountsForPinchViewportScroll)
604 {
605     initializeWithDesktopSettings();
606     webViewImpl()->settings()->setDefaultFontSize(12);
607     webViewImpl()->resize(WebSize(640, 480));
608     registerMockedHttpURLLoad("move_range.html");
609     navigateTo(m_baseURL + "move_range.html");
610 
611     WebRect baseRect;
612     WebRect extentRect;
613 
614     webViewImpl()->setPageScaleFactor(2);
615     WebFrame* mainFrame = webViewImpl()->mainFrame();
616 
617     // Select some text and get the base and extent rects (that's the start of
618     // the range and its end). Do a sanity check that the expected text is
619     // selected
620     mainFrame->executeScript(WebScriptSource("selectRange();"));
621     EXPECT_EQ("ir", mainFrame->selectionAsText().utf8());
622 
623     webViewImpl()->selectionBounds(baseRect, extentRect);
624     WebPoint initialPoint(baseRect.x, baseRect.y);
625     WebPoint endPoint(extentRect.x, extentRect.y);
626 
627     // Move the pinch viewport over and make the selection in the same
628     // screen-space location. The selection should change to two characters to
629     // the right and down one line.
630     PinchViewport& pinchViewport = frame()->page()->frameHost().pinchViewport();
631     pinchViewport.move(FloatPoint(60, 25));
632     mainFrame->moveRangeSelection(initialPoint, endPoint);
633     EXPECT_EQ("t ", mainFrame->selectionAsText().utf8());
634 }
635 
636 // Test that the scrollFocusedNodeIntoRect method works with the pinch viewport.
TEST_F(PinchViewportTest,TestScrollFocusedNodeIntoRect)637 TEST_F(PinchViewportTest, TestScrollFocusedNodeIntoRect)
638 {
639     initializeWithDesktopSettings();
640     webViewImpl()->resize(IntSize(500, 300));
641 
642     registerMockedHttpURLLoad("pinch-viewport-input-field.html");
643     navigateTo(m_baseURL + "pinch-viewport-input-field.html");
644 
645     PinchViewport& pinchViewport = frame()->page()->frameHost().pinchViewport();
646     webViewImpl()->resizePinchViewport(IntSize(200, 100));
647     webViewImpl()->setInitialFocus(false);
648     pinchViewport.setLocation(FloatPoint());
649     webViewImpl()->scrollFocusedNodeIntoRect(IntRect(0, 0, 500, 200));
650 
651     EXPECT_POINT_EQ(IntPoint(0, frame()->view()->maximumScrollPosition().y()),
652         frame()->view()->scrollPosition());
653     EXPECT_FLOAT_POINT_EQ(FloatPoint(150, 200), pinchViewport.visibleRect().location());
654 
655     // Try it again but with the page zoomed in
656     frame()->view()->notifyScrollPositionChanged(IntPoint(0, 0));
657     webViewImpl()->resizePinchViewport(IntSize(500, 300));
658     pinchViewport.setLocation(FloatPoint(0, 0));
659 
660     webViewImpl()->setPageScaleFactor(2);
661     webViewImpl()->scrollFocusedNodeIntoRect(IntRect(0, 0, 500, 200));
662     EXPECT_POINT_EQ(IntPoint(0, frame()->view()->maximumScrollPosition().y()),
663         frame()->view()->scrollPosition());
664     EXPECT_FLOAT_POINT_EQ(FloatPoint(125, 150), pinchViewport.visibleRect().location());
665 
666     // Once more but make sure that we don't move the pinch viewport unless necessary.
667     registerMockedHttpURLLoad("pinch-viewport-input-field-long-and-wide.html");
668     navigateTo(m_baseURL + "pinch-viewport-input-field-long-and-wide.html");
669     webViewImpl()->setInitialFocus(false);
670     pinchViewport.setLocation(FloatPoint());
671     frame()->view()->notifyScrollPositionChanged(IntPoint(0, 0));
672     webViewImpl()->resizePinchViewport(IntSize(500, 300));
673     pinchViewport.setLocation(FloatPoint(30, 50));
674 
675     webViewImpl()->setPageScaleFactor(2);
676     webViewImpl()->scrollFocusedNodeIntoRect(IntRect(0, 0, 500, 200));
677     EXPECT_POINT_EQ(IntPoint(200-30-75, 600-50-65), frame()->view()->scrollPosition());
678     EXPECT_FLOAT_POINT_EQ(FloatPoint(30, 50), pinchViewport.visibleRect().location());
679 }
680 
681 // Test that resizing the WebView causes ViewportConstrained objects to relayout.
TEST_F(PinchViewportTest,TestWebViewResizeCausesViewportConstrainedLayout)682 TEST_F(PinchViewportTest, TestWebViewResizeCausesViewportConstrainedLayout)
683 {
684     initializeWithDesktopSettings();
685     webViewImpl()->resize(IntSize(500, 300));
686 
687     registerMockedHttpURLLoad("pinch-viewport-fixed-pos.html");
688     navigateTo(m_baseURL + "pinch-viewport-fixed-pos.html");
689 
690     RenderObject* navbar = frame()->document()->getElementById("navbar")->renderer();
691 
692     EXPECT_FALSE(navbar->needsLayout());
693 
694     frame()->view()->resize(IntSize(500, 200));
695 
696     EXPECT_TRUE(navbar->needsLayout());
697 }
698 
699 class MockWebFrameClient : public blink::WebFrameClient {
700 public:
701     MOCK_METHOD1(showContextMenu, void(const WebContextMenuData&));
702 };
703 
704 MATCHER_P2(ContextMenuAtLocation, x, y,
705     std::string(negation ? "is" : "isn't")
706     + " at expected location ["
707     + PrintToString(x) + ", " + PrintToString(y) + "]")
708 {
709     return arg.mousePosition.x == x && arg.mousePosition.y == y;
710 }
711 
712 // Test that the context menu's location is correct in the presence of pinch
713 // viewport offset.
TEST_F(PinchViewportTest,TestContextMenuShownInCorrectLocation)714 TEST_F(PinchViewportTest, TestContextMenuShownInCorrectLocation)
715 {
716     initializeWithDesktopSettings();
717     webViewImpl()->resize(IntSize(200, 300));
718 
719     registerMockedHttpURLLoad("200-by-300.html");
720     navigateTo(m_baseURL + "200-by-300.html");
721 
722     WebMouseEvent mouseDownEvent;
723     mouseDownEvent.type = WebInputEvent::MouseDown;
724     mouseDownEvent.x = 10;
725     mouseDownEvent.y = 10;
726     mouseDownEvent.windowX = 10;
727     mouseDownEvent.windowY = 10;
728     mouseDownEvent.globalX = 110;
729     mouseDownEvent.globalY = 210;
730     mouseDownEvent.clickCount = 1;
731     mouseDownEvent.button = WebMouseEvent::ButtonRight;
732 
733     // Corresponding release event (Windows shows context menu on release).
734     WebMouseEvent mouseUpEvent(mouseDownEvent);
735     mouseUpEvent.type = WebInputEvent::MouseUp;
736 
737     WebFrameClient* oldClient = webViewImpl()->mainFrameImpl()->client();
738     MockWebFrameClient mockWebFrameClient;
739     EXPECT_CALL(mockWebFrameClient, showContextMenu(ContextMenuAtLocation(mouseDownEvent.x, mouseDownEvent.y)));
740 
741     // Do a sanity check with no scale applied.
742     webViewImpl()->mainFrameImpl()->setClient(&mockWebFrameClient);
743     webViewImpl()->handleInputEvent(mouseDownEvent);
744     webViewImpl()->handleInputEvent(mouseUpEvent);
745 
746     Mock::VerifyAndClearExpectations(&mockWebFrameClient);
747     mouseDownEvent.button = WebMouseEvent::ButtonLeft;
748     webViewImpl()->handleInputEvent(mouseDownEvent);
749 
750     // Now pinch zoom into the page and move the pinch viewport. The context
751     // menu should still appear at the location of the event, relative to the
752     // WebView.
753     PinchViewport& pinchViewport = frame()->page()->frameHost().pinchViewport();
754     webViewImpl()->setPageScaleFactor(2);
755     pinchViewport.setLocation(FloatPoint(60, 80));
756     EXPECT_CALL(mockWebFrameClient, showContextMenu(ContextMenuAtLocation(mouseDownEvent.x, mouseDownEvent.y)));
757 
758     mouseDownEvent.button = WebMouseEvent::ButtonRight;
759     webViewImpl()->handleInputEvent(mouseDownEvent);
760     webViewImpl()->handleInputEvent(mouseUpEvent);
761 
762     // Reset the old client so destruction can occur naturally.
763     webViewImpl()->mainFrameImpl()->setClient(oldClient);
764 }
765 
766 // Test that the scrollIntoView correctly scrolls the main frame
767 // and pinch viewports such that the given rect is centered in the viewport.
TEST_F(PinchViewportTest,TestScrollingDocumentRegionIntoView)768 TEST_F(PinchViewportTest, TestScrollingDocumentRegionIntoView)
769 {
770     initializeWithDesktopSettings();
771     webViewImpl()->resize(IntSize(100, 150));
772 
773     registerMockedHttpURLLoad("200-by-300-viewport.html");
774     navigateTo(m_baseURL + "200-by-300-viewport.html");
775 
776     PinchViewport& pinchViewport = frame()->page()->frameHost().pinchViewport();
777 
778     // Test that the pinch viewport is scrolled if the viewport has been
779     // resized (as is the case when the ChromeOS keyboard comes up) but not
780     // scaled.
781     webViewImpl()->resizePinchViewport(WebSize(100, 100));
782     pinchViewport.scrollIntoView(FloatRect(100, 250, 50, 50));
783     EXPECT_POINT_EQ(IntPoint(75, 150), frame()->view()->scrollPosition());
784     EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 50), pinchViewport.visibleRect().location());
785 
786     pinchViewport.scrollIntoView(FloatRect(25, 75, 50, 50));
787     EXPECT_POINT_EQ(IntPoint(0, 0), frame()->view()->scrollPosition());
788     EXPECT_FLOAT_POINT_EQ(FloatPoint(0, 50), pinchViewport.visibleRect().location());
789 
790     // Reset the pinch viewport's size, scale the page and repeat the test
791     webViewImpl()->resizePinchViewport(IntSize(100, 150));
792     webViewImpl()->setPageScaleFactor(2);
793     pinchViewport.setLocation(FloatPoint());
794 
795     pinchViewport.scrollIntoView(FloatRect(50, 75, 50, 75));
796     EXPECT_POINT_EQ(IntPoint(50, 75), frame()->view()->scrollPosition());
797     EXPECT_FLOAT_POINT_EQ(FloatPoint(), pinchViewport.visibleRect().location());
798 
799     pinchViewport.scrollIntoView(FloatRect(190, 290, 10, 10));
800     EXPECT_POINT_EQ(IntPoint(100, 150), frame()->view()->scrollPosition());
801     EXPECT_FLOAT_POINT_EQ(FloatPoint(50, 75), pinchViewport.visibleRect().location());
802 }
803 
804 } // namespace
805