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