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 { 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->setAcceleratedCompositingForFixedPositionEnabled(true); 147 settings->setAcceleratedCompositingForOverflowScrollEnabled(true); 148 settings->setCompositedScrollingForFramesEnabled(true); 149 settings->setPinchVirtualViewportEnabled(true); 150 } 151 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. 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 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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