1/* 2 * Copyright (C) 2012 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY 17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 */ 24 25#include "config.h" 26 27#include "core/page/scrolling/ScrollingCoordinator.h" 28 29#include "core/page/Page.h" 30#include "core/rendering/RenderView.h" 31#include "core/rendering/RenderWidget.h" 32#include "core/rendering/compositing/CompositedLayerMapping.h" 33#include "core/rendering/compositing/RenderLayerCompositor.h" 34#include "core/testing/URLTestHelpers.h" 35#include "platform/graphics/GraphicsLayer.h" 36#include "public/platform/Platform.h" 37#include "public/platform/WebLayer.h" 38#include "public/platform/WebLayerPositionConstraint.h" 39#include "public/platform/WebLayerTreeView.h" 40#include "public/platform/WebUnitTestSupport.h" 41#include "public/web/WebSettings.h" 42#include "public/web/WebViewClient.h" 43#include "web/WebLocalFrameImpl.h" 44#include "web/WebViewImpl.h" 45#include "web/tests/FrameTestHelpers.h" 46#include <gtest/gtest.h> 47 48using namespace blink; 49 50namespace { 51 52class ScrollingCoordinatorChromiumTest : public testing::Test { 53public: 54 ScrollingCoordinatorChromiumTest() 55 : m_baseURL("http://www.test.com/") 56 { 57 m_helper.initialize(true, 0, &m_mockWebViewClient, &configureSettings); 58 webViewImpl()->resize(IntSize(320, 240)); 59 } 60 61 virtual ~ScrollingCoordinatorChromiumTest() 62 { 63 Platform::current()->unitTestSupport()->unregisterAllMockedURLs(); 64 } 65 66 void navigateTo(const std::string& url) 67 { 68 FrameTestHelpers::loadFrame(webViewImpl()->mainFrame(), url); 69 } 70 71 void forceFullCompositingUpdate() 72 { 73 webViewImpl()->layout(); 74 } 75 76 void registerMockedHttpURLLoad(const std::string& fileName) 77 { 78 URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8(fileName.c_str())); 79 } 80 81 WebLayer* getRootScrollLayer() 82 { 83 RenderLayerCompositor* compositor = frame()->contentRenderer()->compositor(); 84 ASSERT(compositor); 85 ASSERT(compositor->scrollLayer()); 86 87 WebLayer* webScrollLayer = compositor->scrollLayer()->platformLayer(); 88 return webScrollLayer; 89 } 90 91 WebViewImpl* webViewImpl() const { return m_helper.webViewImpl(); } 92 LocalFrame* frame() const { return m_helper.webViewImpl()->mainFrameImpl()->frame(); } 93 94protected: 95 std::string m_baseURL; 96 FrameTestHelpers::TestWebViewClient m_mockWebViewClient; 97 98private: 99 static void configureSettings(WebSettings* settings) 100 { 101 settings->setJavaScriptEnabled(true); 102 settings->setAcceleratedCompositingEnabled(true); 103 settings->setPreferCompositingToLCDTextEnabled(true); 104 } 105 106 FrameTestHelpers::WebViewHelper m_helper; 107}; 108 109TEST_F(ScrollingCoordinatorChromiumTest, fastScrollingByDefault) 110{ 111 navigateTo("about:blank"); 112 forceFullCompositingUpdate(); 113 114 // Make sure the scrolling coordinator is active. 115 FrameView* frameView = frame()->view(); 116 Page* page = frame()->page(); 117 ASSERT_TRUE(page->scrollingCoordinator()); 118 ASSERT_TRUE(page->scrollingCoordinator()->coordinatesScrollingForFrameView(frameView)); 119 120 // Fast scrolling should be enabled by default. 121 WebLayer* rootScrollLayer = getRootScrollLayer(); 122 ASSERT_TRUE(rootScrollLayer->scrollable()); 123 ASSERT_FALSE(rootScrollLayer->shouldScrollOnMainThread()); 124 ASSERT_FALSE(rootScrollLayer->haveWheelEventHandlers()); 125} 126 127TEST_F(ScrollingCoordinatorChromiumTest, fastScrollingCanBeDisabledWithSetting) 128{ 129 navigateTo("about:blank"); 130 webViewImpl()->settings()->setThreadedScrollingEnabled(false); 131 forceFullCompositingUpdate(); 132 133 // Make sure the scrolling coordinator is active. 134 FrameView* frameView = frame()->view(); 135 Page* page = frame()->page(); 136 ASSERT_TRUE(page->scrollingCoordinator()); 137 ASSERT_TRUE(page->scrollingCoordinator()->coordinatesScrollingForFrameView(frameView)); 138 139 // Main scrolling should be enabled with the setting override. 140 WebLayer* rootScrollLayer = getRootScrollLayer(); 141 ASSERT_TRUE(rootScrollLayer->scrollable()); 142 ASSERT_TRUE(rootScrollLayer->shouldScrollOnMainThread()); 143} 144 145static WebLayer* webLayerFromElement(Element* element) 146{ 147 if (!element) 148 return 0; 149 RenderObject* renderer = element->renderer(); 150 if (!renderer || !renderer->isBoxModelObject()) 151 return 0; 152 RenderLayer* layer = toRenderBoxModelObject(renderer)->layer(); 153 if (!layer) 154 return 0; 155 if (!layer->hasCompositedLayerMapping()) 156 return 0; 157 CompositedLayerMapping* compositedLayerMapping = layer->compositedLayerMapping(); 158 GraphicsLayer* graphicsLayer = compositedLayerMapping->mainGraphicsLayer(); 159 if (!graphicsLayer) 160 return 0; 161 return graphicsLayer->platformLayer(); 162} 163 164TEST_F(ScrollingCoordinatorChromiumTest, fastScrollingForFixedPosition) 165{ 166 registerMockedHttpURLLoad("fixed-position.html"); 167 navigateTo(m_baseURL + "fixed-position.html"); 168 forceFullCompositingUpdate(); 169 170 // Fixed position should not fall back to main thread scrolling. 171 WebLayer* rootScrollLayer = getRootScrollLayer(); 172 ASSERT_FALSE(rootScrollLayer->shouldScrollOnMainThread()); 173 174 Document* document = frame()->document(); 175 { 176 Element* element = document->getElementById("div-tl"); 177 ASSERT_TRUE(element); 178 WebLayer* layer = webLayerFromElement(element); 179 ASSERT_TRUE(layer); 180 WebLayerPositionConstraint constraint = layer->positionConstraint(); 181 ASSERT_TRUE(constraint.isFixedPosition); 182 ASSERT_TRUE(!constraint.isFixedToRightEdge && !constraint.isFixedToBottomEdge); 183 } 184 { 185 Element* element = document->getElementById("div-tr"); 186 ASSERT_TRUE(element); 187 WebLayer* layer = webLayerFromElement(element); 188 ASSERT_TRUE(layer); 189 WebLayerPositionConstraint constraint = layer->positionConstraint(); 190 ASSERT_TRUE(constraint.isFixedPosition); 191 ASSERT_TRUE(constraint.isFixedToRightEdge && !constraint.isFixedToBottomEdge); 192 } 193 { 194 Element* element = document->getElementById("div-bl"); 195 ASSERT_TRUE(element); 196 WebLayer* layer = webLayerFromElement(element); 197 ASSERT_TRUE(layer); 198 WebLayerPositionConstraint constraint = layer->positionConstraint(); 199 ASSERT_TRUE(constraint.isFixedPosition); 200 ASSERT_TRUE(!constraint.isFixedToRightEdge && constraint.isFixedToBottomEdge); 201 } 202 { 203 Element* element = document->getElementById("div-br"); 204 ASSERT_TRUE(element); 205 WebLayer* layer = webLayerFromElement(element); 206 ASSERT_TRUE(layer); 207 WebLayerPositionConstraint constraint = layer->positionConstraint(); 208 ASSERT_TRUE(constraint.isFixedPosition); 209 ASSERT_TRUE(constraint.isFixedToRightEdge && constraint.isFixedToBottomEdge); 210 } 211 { 212 Element* element = document->getElementById("span-tl"); 213 ASSERT_TRUE(element); 214 WebLayer* layer = webLayerFromElement(element); 215 ASSERT_TRUE(layer); 216 WebLayerPositionConstraint constraint = layer->positionConstraint(); 217 ASSERT_TRUE(constraint.isFixedPosition); 218 ASSERT_TRUE(!constraint.isFixedToRightEdge && !constraint.isFixedToBottomEdge); 219 } 220 { 221 Element* element = document->getElementById("span-tr"); 222 ASSERT_TRUE(element); 223 WebLayer* layer = webLayerFromElement(element); 224 ASSERT_TRUE(layer); 225 WebLayerPositionConstraint constraint = layer->positionConstraint(); 226 ASSERT_TRUE(constraint.isFixedPosition); 227 ASSERT_TRUE(constraint.isFixedToRightEdge && !constraint.isFixedToBottomEdge); 228 } 229 { 230 Element* element = document->getElementById("span-bl"); 231 ASSERT_TRUE(element); 232 WebLayer* layer = webLayerFromElement(element); 233 ASSERT_TRUE(layer); 234 WebLayerPositionConstraint constraint = layer->positionConstraint(); 235 ASSERT_TRUE(constraint.isFixedPosition); 236 ASSERT_TRUE(!constraint.isFixedToRightEdge && constraint.isFixedToBottomEdge); 237 } 238 { 239 Element* element = document->getElementById("span-br"); 240 ASSERT_TRUE(element); 241 WebLayer* layer = webLayerFromElement(element); 242 ASSERT_TRUE(layer); 243 WebLayerPositionConstraint constraint = layer->positionConstraint(); 244 ASSERT_TRUE(constraint.isFixedPosition); 245 ASSERT_TRUE(constraint.isFixedToRightEdge && constraint.isFixedToBottomEdge); 246 } 247} 248 249TEST_F(ScrollingCoordinatorChromiumTest, wheelEventHandler) 250{ 251 registerMockedHttpURLLoad("wheel-event-handler.html"); 252 navigateTo(m_baseURL + "wheel-event-handler.html"); 253 forceFullCompositingUpdate(); 254 255 WebLayer* rootScrollLayer = getRootScrollLayer(); 256 ASSERT_TRUE(rootScrollLayer->haveWheelEventHandlers()); 257} 258 259TEST_F(ScrollingCoordinatorChromiumTest, scrollEventHandler) 260{ 261 registerMockedHttpURLLoad("scroll-event-handler.html"); 262 navigateTo(m_baseURL + "scroll-event-handler.html"); 263 forceFullCompositingUpdate(); 264 265 WebLayer* rootScrollLayer = getRootScrollLayer(); 266 ASSERT_TRUE(rootScrollLayer->haveScrollEventHandlers()); 267} 268 269TEST_F(ScrollingCoordinatorChromiumTest, updateEventHandlersDuringTeardown) 270{ 271 registerMockedHttpURLLoad("scroll-event-handler-window.html"); 272 navigateTo(m_baseURL + "scroll-event-handler-window.html"); 273 forceFullCompositingUpdate(); 274 275 // Simulate detaching the document from its DOM window. This should not 276 // cause a crash when the WebViewImpl is closed by the test runner. 277 frame()->document()->prepareForDestruction(); 278} 279 280TEST_F(ScrollingCoordinatorChromiumTest, clippedBodyTest) 281{ 282 registerMockedHttpURLLoad("clipped-body.html"); 283 navigateTo(m_baseURL + "clipped-body.html"); 284 forceFullCompositingUpdate(); 285 286 WebLayer* rootScrollLayer = getRootScrollLayer(); 287 ASSERT_EQ(0u, rootScrollLayer->nonFastScrollableRegion().size()); 288} 289 290TEST_F(ScrollingCoordinatorChromiumTest, overflowScrolling) 291{ 292 registerMockedHttpURLLoad("overflow-scrolling.html"); 293 navigateTo(m_baseURL + "overflow-scrolling.html"); 294 forceFullCompositingUpdate(); 295 296 // Verify the properties of the accelerated scrolling element starting from the RenderObject 297 // all the way to the WebLayer. 298 Element* scrollableElement = frame()->document()->getElementById("scrollable"); 299 ASSERT(scrollableElement); 300 301 RenderObject* renderer = scrollableElement->renderer(); 302 ASSERT_TRUE(renderer->isBox()); 303 ASSERT_TRUE(renderer->hasLayer()); 304 305 RenderBox* box = toRenderBox(renderer); 306 ASSERT_TRUE(box->usesCompositedScrolling()); 307 ASSERT_EQ(PaintsIntoOwnBacking, box->layer()->compositingState()); 308 309 CompositedLayerMapping* compositedLayerMapping = box->layer()->compositedLayerMapping(); 310 ASSERT_TRUE(compositedLayerMapping->hasScrollingLayer()); 311 ASSERT(compositedLayerMapping->scrollingContentsLayer()); 312 313 GraphicsLayer* graphicsLayer = compositedLayerMapping->scrollingContentsLayer(); 314 ASSERT_EQ(box->layer()->scrollableArea(), graphicsLayer->scrollableArea()); 315 316 WebLayer* webScrollLayer = compositedLayerMapping->scrollingContentsLayer()->platformLayer(); 317 ASSERT_TRUE(webScrollLayer->scrollable()); 318 ASSERT_TRUE(webScrollLayer->userScrollableHorizontal()); 319 ASSERT_TRUE(webScrollLayer->userScrollableVertical()); 320 321#if OS(ANDROID) 322 // Now verify we've attached impl-side scrollbars onto the scrollbar layers 323 ASSERT_TRUE(compositedLayerMapping->layerForHorizontalScrollbar()); 324 ASSERT_TRUE(compositedLayerMapping->layerForHorizontalScrollbar()->hasContentsLayer()); 325 ASSERT_TRUE(compositedLayerMapping->layerForVerticalScrollbar()); 326 ASSERT_TRUE(compositedLayerMapping->layerForVerticalScrollbar()->hasContentsLayer()); 327#endif 328} 329 330TEST_F(ScrollingCoordinatorChromiumTest, overflowHidden) 331{ 332 registerMockedHttpURLLoad("overflow-hidden.html"); 333 navigateTo(m_baseURL + "overflow-hidden.html"); 334 forceFullCompositingUpdate(); 335 336 // Verify the properties of the accelerated scrolling element starting from the RenderObject 337 // all the way to the WebLayer. 338 Element* overflowElement = frame()->document()->getElementById("unscrollable-y"); 339 ASSERT(overflowElement); 340 341 RenderObject* renderer = overflowElement->renderer(); 342 ASSERT_TRUE(renderer->isBox()); 343 ASSERT_TRUE(renderer->hasLayer()); 344 345 RenderBox* box = toRenderBox(renderer); 346 ASSERT_TRUE(box->usesCompositedScrolling()); 347 ASSERT_EQ(PaintsIntoOwnBacking, box->layer()->compositingState()); 348 349 CompositedLayerMapping* compositedLayerMapping = box->layer()->compositedLayerMapping(); 350 ASSERT_TRUE(compositedLayerMapping->hasScrollingLayer()); 351 ASSERT(compositedLayerMapping->scrollingContentsLayer()); 352 353 GraphicsLayer* graphicsLayer = compositedLayerMapping->scrollingContentsLayer(); 354 ASSERT_EQ(box->layer()->scrollableArea(), graphicsLayer->scrollableArea()); 355 356 WebLayer* webScrollLayer = compositedLayerMapping->scrollingContentsLayer()->platformLayer(); 357 ASSERT_TRUE(webScrollLayer->scrollable()); 358 ASSERT_TRUE(webScrollLayer->userScrollableHorizontal()); 359 ASSERT_FALSE(webScrollLayer->userScrollableVertical()); 360 361 overflowElement = frame()->document()->getElementById("unscrollable-x"); 362 ASSERT(overflowElement); 363 364 renderer = overflowElement->renderer(); 365 ASSERT_TRUE(renderer->isBox()); 366 ASSERT_TRUE(renderer->hasLayer()); 367 368 box = toRenderBox(renderer); 369 ASSERT_TRUE(box->scrollableArea()->usesCompositedScrolling()); 370 ASSERT_EQ(PaintsIntoOwnBacking, box->layer()->compositingState()); 371 372 compositedLayerMapping = box->layer()->compositedLayerMapping(); 373 ASSERT_TRUE(compositedLayerMapping->hasScrollingLayer()); 374 ASSERT(compositedLayerMapping->scrollingContentsLayer()); 375 376 graphicsLayer = compositedLayerMapping->scrollingContentsLayer(); 377 ASSERT_EQ(box->layer()->scrollableArea(), graphicsLayer->scrollableArea()); 378 379 webScrollLayer = compositedLayerMapping->scrollingContentsLayer()->platformLayer(); 380 ASSERT_TRUE(webScrollLayer->scrollable()); 381 ASSERT_FALSE(webScrollLayer->userScrollableHorizontal()); 382 ASSERT_TRUE(webScrollLayer->userScrollableVertical()); 383} 384 385TEST_F(ScrollingCoordinatorChromiumTest, iframeScrolling) 386{ 387 registerMockedHttpURLLoad("iframe-scrolling.html"); 388 registerMockedHttpURLLoad("iframe-scrolling-inner.html"); 389 navigateTo(m_baseURL + "iframe-scrolling.html"); 390 forceFullCompositingUpdate(); 391 392 // Verify the properties of the accelerated scrolling element starting from the RenderObject 393 // all the way to the WebLayer. 394 Element* scrollableFrame = frame()->document()->getElementById("scrollable"); 395 ASSERT_TRUE(scrollableFrame); 396 397 RenderObject* renderer = scrollableFrame->renderer(); 398 ASSERT_TRUE(renderer); 399 ASSERT_TRUE(renderer->isWidget()); 400 401 RenderWidget* renderWidget = toRenderWidget(renderer); 402 ASSERT_TRUE(renderWidget); 403 ASSERT_TRUE(renderWidget->widget()); 404 ASSERT_TRUE(renderWidget->widget()->isFrameView()); 405 406 FrameView* innerFrameView = toFrameView(renderWidget->widget()); 407 RenderView* innerRenderView = innerFrameView->renderView(); 408 ASSERT_TRUE(innerRenderView); 409 410 RenderLayerCompositor* innerCompositor = innerRenderView->compositor(); 411 ASSERT_TRUE(innerCompositor->inCompositingMode()); 412 ASSERT_TRUE(innerCompositor->scrollLayer()); 413 414 GraphicsLayer* scrollLayer = innerCompositor->scrollLayer(); 415 ASSERT_EQ(innerFrameView, scrollLayer->scrollableArea()); 416 417 WebLayer* webScrollLayer = scrollLayer->platformLayer(); 418 ASSERT_TRUE(webScrollLayer->scrollable()); 419 420#if OS(ANDROID) 421 // Now verify we've attached impl-side scrollbars onto the scrollbar layers 422 ASSERT_TRUE(innerCompositor->layerForHorizontalScrollbar()); 423 ASSERT_TRUE(innerCompositor->layerForHorizontalScrollbar()->hasContentsLayer()); 424 ASSERT_TRUE(innerCompositor->layerForVerticalScrollbar()); 425 ASSERT_TRUE(innerCompositor->layerForVerticalScrollbar()->hasContentsLayer()); 426#endif 427} 428 429TEST_F(ScrollingCoordinatorChromiumTest, rtlIframe) 430{ 431 registerMockedHttpURLLoad("rtl-iframe.html"); 432 registerMockedHttpURLLoad("rtl-iframe-inner.html"); 433 navigateTo(m_baseURL + "rtl-iframe.html"); 434 forceFullCompositingUpdate(); 435 436 // Verify the properties of the accelerated scrolling element starting from the RenderObject 437 // all the way to the WebLayer. 438 Element* scrollableFrame = frame()->document()->getElementById("scrollable"); 439 ASSERT_TRUE(scrollableFrame); 440 441 RenderObject* renderer = scrollableFrame->renderer(); 442 ASSERT_TRUE(renderer); 443 ASSERT_TRUE(renderer->isWidget()); 444 445 RenderWidget* renderWidget = toRenderWidget(renderer); 446 ASSERT_TRUE(renderWidget); 447 ASSERT_TRUE(renderWidget->widget()); 448 ASSERT_TRUE(renderWidget->widget()->isFrameView()); 449 450 FrameView* innerFrameView = toFrameView(renderWidget->widget()); 451 RenderView* innerRenderView = innerFrameView->renderView(); 452 ASSERT_TRUE(innerRenderView); 453 454 RenderLayerCompositor* innerCompositor = innerRenderView->compositor(); 455 ASSERT_TRUE(innerCompositor->inCompositingMode()); 456 ASSERT_TRUE(innerCompositor->scrollLayer()); 457 458 GraphicsLayer* scrollLayer = innerCompositor->scrollLayer(); 459 ASSERT_EQ(innerFrameView, scrollLayer->scrollableArea()); 460 461 WebLayer* webScrollLayer = scrollLayer->platformLayer(); 462 ASSERT_TRUE(webScrollLayer->scrollable()); 463 464 int expectedScrollPosition = 958 + (innerFrameView->verticalScrollbar()->isOverlayScrollbar() ? 0 : 15); 465 ASSERT_EQ(expectedScrollPosition, webScrollLayer->scrollPosition().x); 466} 467 468TEST_F(ScrollingCoordinatorChromiumTest, setupScrollbarLayerShouldNotCrash) 469{ 470 registerMockedHttpURLLoad("setup_scrollbar_layer_crash.html"); 471 navigateTo(m_baseURL + "setup_scrollbar_layer_crash.html"); 472 forceFullCompositingUpdate(); 473 // This test document setup an iframe with scrollbars, then switch to 474 // an empty document by javascript. 475} 476 477} // namespace 478