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 <gtest/gtest.h> 30 #include "FrameTestHelpers.h" 31 #include "URLTestHelpers.h" 32 #include "WebFrameClient.h" 33 #include "WebFrameImpl.h" 34 #include "WebSettings.h" 35 #include "WebViewClient.h" 36 #include "WebViewImpl.h" 37 #include "core/rendering/CompositedLayerMapping.h" 38 #include "core/rendering/RenderLayerCompositor.h" 39 #include "core/rendering/RenderView.h" 40 #include "platform/graphics/GraphicsLayer.h" 41 #include "public/platform/Platform.h" 42 #include "public/platform/WebLayer.h" 43 #include "public/platform/WebLayerPositionConstraint.h" 44 #include "public/platform/WebLayerTreeView.h" 45 #include "public/platform/WebUnitTestSupport.h" 46 47 using namespace WebCore; 48 using namespace blink; 49 50 namespace { 51 52 class FakeWebViewClient : public WebViewClient { 53 public: 54 virtual void initializeLayerTreeView() 55 { 56 m_layerTreeView = adoptPtr(Platform::current()->unitTestSupport()->createLayerTreeViewForTesting(WebUnitTestSupport::TestViewTypeUnitTest)); 57 ASSERT(m_layerTreeView); 58 } 59 60 virtual WebLayerTreeView* layerTreeView() 61 { 62 return m_layerTreeView.get(); 63 } 64 65 private: 66 OwnPtr<WebLayerTreeView> m_layerTreeView; 67 }; 68 69 class MockWebFrameClient : public WebFrameClient { 70 }; 71 72 class ScrollingCoordinatorChromiumTest : public testing::Test { 73 public: 74 ScrollingCoordinatorChromiumTest() 75 : m_baseURL("http://www.test.com/") 76 { 77 // We cannot reuse FrameTestHelpers::createWebViewAndLoad here because the compositing 78 // settings need to be set before the page is loaded. 79 m_mainFrame = WebFrame::create(&m_mockWebFrameClient); 80 m_webViewImpl = toWebViewImpl(WebView::create(&m_mockWebViewClient)); 81 m_webViewImpl->settings()->setJavaScriptEnabled(true); 82 m_webViewImpl->settings()->setForceCompositingMode(true); 83 m_webViewImpl->settings()->setAcceleratedCompositingEnabled(true); 84 m_webViewImpl->settings()->setAcceleratedCompositingForFixedPositionEnabled(true); 85 m_webViewImpl->settings()->setAcceleratedCompositingForOverflowScrollEnabled(true); 86 m_webViewImpl->settings()->setAcceleratedCompositingForScrollableFramesEnabled(true); 87 m_webViewImpl->settings()->setCompositedScrollingForFramesEnabled(true); 88 m_webViewImpl->settings()->setFixedPositionCreatesStackingContext(true); 89 m_webViewImpl->setMainFrame(m_mainFrame); 90 m_webViewImpl->resize(IntSize(320, 240)); 91 } 92 93 virtual ~ScrollingCoordinatorChromiumTest() 94 { 95 Platform::current()->unitTestSupport()->unregisterAllMockedURLs(); 96 m_webViewImpl->close(); 97 m_mainFrame->close(); 98 } 99 100 void navigateTo(const std::string& url) 101 { 102 FrameTestHelpers::loadFrame(m_webViewImpl->mainFrame(), url); 103 Platform::current()->unitTestSupport()->serveAsynchronousMockedRequests(); 104 } 105 106 void forceFullCompositingUpdate() 107 { 108 RenderLayerCompositor* compositor = m_webViewImpl->mainFrameImpl()->frame()->contentRenderer()->compositor(); 109 compositor->updateCompositingLayers(CompositingUpdateFinishAllDeferredWork); 110 } 111 112 void registerMockedHttpURLLoad(const std::string& fileName) 113 { 114 URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8(fileName.c_str())); 115 } 116 117 WebLayer* getRootScrollLayer() 118 { 119 RenderLayerCompositor* compositor = m_webViewImpl->mainFrameImpl()->frame()->contentRenderer()->compositor(); 120 ASSERT(compositor); 121 ASSERT(compositor->scrollLayer()); 122 123 WebLayer* webScrollLayer = compositor->scrollLayer()->platformLayer(); 124 return webScrollLayer; 125 } 126 127 protected: 128 std::string m_baseURL; 129 MockWebFrameClient m_mockWebFrameClient; 130 FakeWebViewClient m_mockWebViewClient; 131 WebViewImpl* m_webViewImpl; 132 WebFrame* m_mainFrame; 133 }; 134 135 TEST_F(ScrollingCoordinatorChromiumTest, fastScrollingByDefault) 136 { 137 navigateTo("about:blank"); 138 forceFullCompositingUpdate(); 139 140 // Make sure the scrolling coordinator is active. 141 FrameView* frameView = m_webViewImpl->mainFrameImpl()->frameView(); 142 Page* page = m_webViewImpl->mainFrameImpl()->frame()->page(); 143 ASSERT_TRUE(page->scrollingCoordinator()); 144 ASSERT_TRUE(page->scrollingCoordinator()->coordinatesScrollingForFrameView(frameView)); 145 146 // Fast scrolling should be enabled by default. 147 WebLayer* rootScrollLayer = getRootScrollLayer(); 148 ASSERT_TRUE(rootScrollLayer->scrollable()); 149 ASSERT_FALSE(rootScrollLayer->shouldScrollOnMainThread()); 150 ASSERT_FALSE(rootScrollLayer->haveWheelEventHandlers()); 151 } 152 153 static WebLayer* webLayerFromElement(Element* element) 154 { 155 if (!element) 156 return 0; 157 RenderObject* renderer = element->renderer(); 158 if (!renderer || !renderer->isBoxModelObject()) 159 return 0; 160 RenderLayer* layer = toRenderBoxModelObject(renderer)->layer(); 161 if (!layer) 162 return 0; 163 if (!layer->hasCompositedLayerMapping()) 164 return 0; 165 CompositedLayerMappingPtr compositedLayerMapping = layer->compositedLayerMapping(); 166 GraphicsLayer* graphicsLayer = compositedLayerMapping->mainGraphicsLayer(); 167 if (!graphicsLayer) 168 return 0; 169 return graphicsLayer->platformLayer(); 170 } 171 172 TEST_F(ScrollingCoordinatorChromiumTest, fastScrollingForFixedPosition) 173 { 174 registerMockedHttpURLLoad("fixed-position.html"); 175 navigateTo(m_baseURL + "fixed-position.html"); 176 forceFullCompositingUpdate(); 177 178 // Fixed position should not fall back to main thread scrolling. 179 WebLayer* rootScrollLayer = getRootScrollLayer(); 180 ASSERT_FALSE(rootScrollLayer->shouldScrollOnMainThread()); 181 182 Document* document = m_webViewImpl->mainFrameImpl()->frame()->document(); 183 { 184 Element* element = document->getElementById("div-tl"); 185 ASSERT_TRUE(element); 186 WebLayer* layer = webLayerFromElement(element); 187 ASSERT_TRUE(layer); 188 WebLayerPositionConstraint constraint = layer->positionConstraint(); 189 ASSERT_TRUE(constraint.isFixedPosition); 190 ASSERT_TRUE(!constraint.isFixedToRightEdge && !constraint.isFixedToBottomEdge); 191 } 192 { 193 Element* element = document->getElementById("div-tr"); 194 ASSERT_TRUE(element); 195 WebLayer* layer = webLayerFromElement(element); 196 ASSERT_TRUE(layer); 197 WebLayerPositionConstraint constraint = layer->positionConstraint(); 198 ASSERT_TRUE(constraint.isFixedPosition); 199 ASSERT_TRUE(constraint.isFixedToRightEdge && !constraint.isFixedToBottomEdge); 200 } 201 { 202 Element* element = document->getElementById("div-bl"); 203 ASSERT_TRUE(element); 204 WebLayer* layer = webLayerFromElement(element); 205 ASSERT_TRUE(layer); 206 WebLayerPositionConstraint constraint = layer->positionConstraint(); 207 ASSERT_TRUE(constraint.isFixedPosition); 208 ASSERT_TRUE(!constraint.isFixedToRightEdge && constraint.isFixedToBottomEdge); 209 } 210 { 211 Element* element = document->getElementById("div-br"); 212 ASSERT_TRUE(element); 213 WebLayer* layer = webLayerFromElement(element); 214 ASSERT_TRUE(layer); 215 WebLayerPositionConstraint constraint = layer->positionConstraint(); 216 ASSERT_TRUE(constraint.isFixedPosition); 217 ASSERT_TRUE(constraint.isFixedToRightEdge && constraint.isFixedToBottomEdge); 218 } 219 { 220 Element* element = document->getElementById("span-tl"); 221 ASSERT_TRUE(element); 222 WebLayer* layer = webLayerFromElement(element); 223 ASSERT_TRUE(layer); 224 WebLayerPositionConstraint constraint = layer->positionConstraint(); 225 ASSERT_TRUE(constraint.isFixedPosition); 226 ASSERT_TRUE(!constraint.isFixedToRightEdge && !constraint.isFixedToBottomEdge); 227 } 228 { 229 Element* element = document->getElementById("span-tr"); 230 ASSERT_TRUE(element); 231 WebLayer* layer = webLayerFromElement(element); 232 ASSERT_TRUE(layer); 233 WebLayerPositionConstraint constraint = layer->positionConstraint(); 234 ASSERT_TRUE(constraint.isFixedPosition); 235 ASSERT_TRUE(constraint.isFixedToRightEdge && !constraint.isFixedToBottomEdge); 236 } 237 { 238 Element* element = document->getElementById("span-bl"); 239 ASSERT_TRUE(element); 240 WebLayer* layer = webLayerFromElement(element); 241 ASSERT_TRUE(layer); 242 WebLayerPositionConstraint constraint = layer->positionConstraint(); 243 ASSERT_TRUE(constraint.isFixedPosition); 244 ASSERT_TRUE(!constraint.isFixedToRightEdge && constraint.isFixedToBottomEdge); 245 } 246 { 247 Element* element = document->getElementById("span-br"); 248 ASSERT_TRUE(element); 249 WebLayer* layer = webLayerFromElement(element); 250 ASSERT_TRUE(layer); 251 WebLayerPositionConstraint constraint = layer->positionConstraint(); 252 ASSERT_TRUE(constraint.isFixedPosition); 253 ASSERT_TRUE(constraint.isFixedToRightEdge && constraint.isFixedToBottomEdge); 254 } 255 } 256 257 TEST_F(ScrollingCoordinatorChromiumTest, nonFastScrollableRegion) 258 { 259 registerMockedHttpURLLoad("non-fast-scrollable.html"); 260 navigateTo(m_baseURL + "non-fast-scrollable.html"); 261 forceFullCompositingUpdate(); 262 263 WebLayer* rootScrollLayer = getRootScrollLayer(); 264 WebVector<WebRect> nonFastScrollableRegion = rootScrollLayer->nonFastScrollableRegion(); 265 266 ASSERT_EQ(1u, nonFastScrollableRegion.size()); 267 ASSERT_EQ(WebRect(8, 8, 10, 10), nonFastScrollableRegion[0]); 268 } 269 270 TEST_F(ScrollingCoordinatorChromiumTest, wheelEventHandler) 271 { 272 registerMockedHttpURLLoad("wheel-event-handler.html"); 273 navigateTo(m_baseURL + "wheel-event-handler.html"); 274 forceFullCompositingUpdate(); 275 276 WebLayer* rootScrollLayer = getRootScrollLayer(); 277 ASSERT_TRUE(rootScrollLayer->haveWheelEventHandlers()); 278 } 279 280 TEST_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 290 TEST_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 = m_webViewImpl->mainFrameImpl()->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 CompositedLayerMappingPtr 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 330 TEST_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 = m_webViewImpl->mainFrameImpl()->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 CompositedLayerMappingPtr 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 = m_webViewImpl->mainFrameImpl()->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 385 TEST_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 = m_webViewImpl->mainFrameImpl()->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 429 TEST_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 = m_webViewImpl->mainFrameImpl()->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 ASSERT_EQ(expectedScrollPosition, webScrollLayer->maxScrollPosition().width); 467 } 468 469 TEST_F(ScrollingCoordinatorChromiumTest, setupScrollbarLayerShouldNotCrash) 470 { 471 registerMockedHttpURLLoad("setup_scrollbar_layer_crash.html"); 472 navigateTo(m_baseURL + "setup_scrollbar_layer_crash.html"); 473 forceFullCompositingUpdate(); 474 // This test document setup an iframe with scrollbars, then switch to 475 // an empty document by javascript. 476 } 477 478 } // namespace 479