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