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 70using namespace blink; 71 72using ::testing::_; 73using ::testing::PrintToString; 74using ::testing::Mock; 75 76namespace 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 85namespace { 86 87class PinchViewportTest : public testing::Test { 88public: 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 159protected: 160 std::string m_baseURL; 161 FrameTestHelpers::TestWebViewClient m_mockWebViewClient; 162 163private: 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. 169TEST_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. 199TEST_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. 259TEST_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 316static 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. 326TEST_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. 341TEST_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. 392TEST_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. 437TEST_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. 489TEST_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. 544TEST_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. 562TEST_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. 580TEST_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. 596TEST_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. 612TEST_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. 646TEST_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. 670TEST_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. 694TEST_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. 721TEST_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. 755TEST_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. 800TEST_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 817class MockWebFrameClient : public WebFrameClient { 818public: 819 MOCK_METHOD1(showContextMenu, void(const WebContextMenuData&)); 820}; 821 822MATCHER_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. 832TEST_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. 886TEST_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