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