1 /* 2 * Copyright (C) 2011 Apple 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'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 28 #include "core/page/scrolling/ScrollingCoordinator.h" 29 30 #include "core/dom/Document.h" 31 #include "core/dom/Fullscreen.h" 32 #include "core/dom/Node.h" 33 #include "core/frame/EventHandlerRegistry.h" 34 #include "core/frame/FrameView.h" 35 #include "core/frame/LocalFrame.h" 36 #include "core/frame/Settings.h" 37 #include "core/html/HTMLElement.h" 38 #include "core/page/Page.h" 39 #include "core/plugins/PluginView.h" 40 #include "core/rendering/RenderGeometryMap.h" 41 #include "core/rendering/RenderPart.h" 42 #include "core/rendering/RenderView.h" 43 #include "core/rendering/compositing/CompositedLayerMapping.h" 44 #include "core/rendering/compositing/RenderLayerCompositor.h" 45 #include "platform/RuntimeEnabledFeatures.h" 46 #include "platform/TraceEvent.h" 47 #include "platform/exported/WebScrollbarImpl.h" 48 #include "platform/exported/WebScrollbarThemeGeometryNative.h" 49 #include "platform/geometry/Region.h" 50 #include "platform/geometry/TransformState.h" 51 #include "platform/graphics/GraphicsLayer.h" 52 #if OS(MACOSX) 53 #include "platform/mac/ScrollAnimatorMac.h" 54 #endif 55 #include "platform/scroll/ScrollAnimator.h" 56 #include "platform/scroll/ScrollbarTheme.h" 57 #include "public/platform/Platform.h" 58 #include "public/platform/WebCompositorSupport.h" 59 #include "public/platform/WebLayerPositionConstraint.h" 60 #include "public/platform/WebScrollbarLayer.h" 61 #include "public/platform/WebScrollbarThemeGeometry.h" 62 #include "public/platform/WebScrollbarThemePainter.h" 63 #include "wtf/text/StringBuilder.h" 64 65 using blink::WebLayer; 66 using blink::WebLayerPositionConstraint; 67 using blink::WebRect; 68 using blink::WebScrollbarLayer; 69 using blink::WebVector; 70 71 namespace { 72 73 WebLayer* toWebLayer(blink::GraphicsLayer* layer) 74 { 75 return layer ? layer->platformLayer() : 0; 76 } 77 78 } // namespace 79 80 namespace blink { 81 82 PassOwnPtr<ScrollingCoordinator> ScrollingCoordinator::create(Page* page) 83 { 84 return adoptPtr(new ScrollingCoordinator(page)); 85 } 86 87 ScrollingCoordinator::ScrollingCoordinator(Page* page) 88 : m_page(page) 89 , m_scrollGestureRegionIsDirty(false) 90 , m_touchEventTargetRectsAreDirty(false) 91 , m_shouldScrollOnMainThreadDirty(false) 92 , m_wasFrameScrollable(false) 93 , m_lastMainThreadScrollingReasons(0) 94 { 95 } 96 97 ScrollingCoordinator::~ScrollingCoordinator() 98 { 99 } 100 101 void ScrollingCoordinator::setShouldHandleScrollGestureOnMainThreadRegion(const Region& region) 102 { 103 if (!m_page->mainFrame()->isLocalFrame()) 104 return; 105 if (WebLayer* scrollLayer = toWebLayer(m_page->deprecatedLocalMainFrame()->view()->layerForScrolling())) { 106 Vector<IntRect> rects = region.rects(); 107 WebVector<WebRect> webRects(rects.size()); 108 for (size_t i = 0; i < rects.size(); ++i) 109 webRects[i] = rects[i]; 110 scrollLayer->setNonFastScrollableRegion(webRects); 111 } 112 } 113 114 void ScrollingCoordinator::notifyLayoutUpdated() 115 { 116 m_scrollGestureRegionIsDirty = true; 117 m_touchEventTargetRectsAreDirty = true; 118 m_shouldScrollOnMainThreadDirty = true; 119 } 120 121 void ScrollingCoordinator::updateAfterCompositingChangeIfNeeded() 122 { 123 if (!m_page->mainFrame()->isLocalFrame()) 124 return; 125 126 if (!shouldUpdateAfterCompositingChange()) 127 return; 128 129 TRACE_EVENT0("input", "ScrollingCoordinator::updateAfterCompositingChangeIfNeeded"); 130 131 if (m_scrollGestureRegionIsDirty) { 132 // Compute the region of the page where we can't handle scroll gestures and mousewheel events 133 // on the impl thread. This currently includes: 134 // 1. All scrollable areas, such as subframes, overflow divs and list boxes, whose composited 135 // scrolling are not enabled. We need to do this even if the frame view whose layout was updated 136 // is not the main frame. 137 // 2. Resize control areas, e.g. the small rect at the right bottom of div/textarea/iframe when 138 // CSS property "resize" is enabled. 139 // 3. Plugin areas. 140 Region shouldHandleScrollGestureOnMainThreadRegion = computeShouldHandleScrollGestureOnMainThreadRegion(m_page->deprecatedLocalMainFrame(), IntPoint()); 141 setShouldHandleScrollGestureOnMainThreadRegion(shouldHandleScrollGestureOnMainThreadRegion); 142 m_scrollGestureRegionIsDirty = false; 143 } 144 145 if (m_touchEventTargetRectsAreDirty) { 146 updateTouchEventTargetRectsIfNeeded(); 147 m_touchEventTargetRectsAreDirty = false; 148 } 149 150 FrameView* frameView = m_page->deprecatedLocalMainFrame()->view(); 151 bool frameIsScrollable = frameView && frameView->isScrollable(); 152 if (m_shouldScrollOnMainThreadDirty || m_wasFrameScrollable != frameIsScrollable) { 153 setShouldUpdateScrollLayerPositionOnMainThread(mainThreadScrollingReasons()); 154 m_shouldScrollOnMainThreadDirty = false; 155 } 156 m_wasFrameScrollable = frameIsScrollable; 157 158 // The mainFrame view doesn't get included in the FrameTree below, so we 159 // update its size separately. 160 if (WebLayer* scrollingWebLayer = frameView ? toWebLayer(frameView->layerForScrolling()) : 0) { 161 // If there is a fullscreen element, set the scroll bounds to empty so the main frame won't scroll. 162 Document* mainFrameDocument = m_page->deprecatedLocalMainFrame()->document(); 163 Element* fullscreenElement = Fullscreen::fullscreenElementFrom(*mainFrameDocument); 164 if (fullscreenElement && fullscreenElement != mainFrameDocument->documentElement()) 165 scrollingWebLayer->setBounds(IntSize()); 166 else 167 scrollingWebLayer->setBounds(frameView->contentsSize()); 168 } 169 170 const FrameTree& tree = m_page->mainFrame()->tree(); 171 for (const Frame* child = tree.firstChild(); child; child = child->tree().nextSibling()) { 172 if (!child->isLocalFrame()) 173 continue; 174 if (WebLayer* scrollLayer = toWebLayer(toLocalFrame(child)->view()->layerForScrolling())) 175 scrollLayer->setBounds(toLocalFrame(child)->view()->contentsSize()); 176 } 177 } 178 179 void ScrollingCoordinator::setLayerIsContainerForFixedPositionLayers(GraphicsLayer* layer, bool enable) 180 { 181 if (WebLayer* scrollableLayer = toWebLayer(layer)) 182 scrollableLayer->setIsContainerForFixedPositionLayers(enable); 183 } 184 185 static void clearPositionConstraintExceptForLayer(GraphicsLayer* layer, GraphicsLayer* except) 186 { 187 if (layer && layer != except && toWebLayer(layer)) 188 toWebLayer(layer)->setPositionConstraint(WebLayerPositionConstraint()); 189 } 190 191 static WebLayerPositionConstraint computePositionConstraint(const RenderLayer* layer) 192 { 193 ASSERT(layer->hasCompositedLayerMapping()); 194 do { 195 if (layer->renderer()->style()->position() == FixedPosition) { 196 const RenderObject* fixedPositionObject = layer->renderer(); 197 bool fixedToRight = !fixedPositionObject->style()->right().isAuto(); 198 bool fixedToBottom = !fixedPositionObject->style()->bottom().isAuto(); 199 return WebLayerPositionConstraint::fixedPosition(fixedToRight, fixedToBottom); 200 } 201 202 layer = layer->parent(); 203 204 // Composited layers that inherit a fixed position state will be positioned with respect to the nearest compositedLayerMapping's GraphicsLayer. 205 // So, once we find a layer that has its own compositedLayerMapping, we can stop searching for a fixed position RenderObject. 206 } while (layer && !layer->hasCompositedLayerMapping()); 207 return WebLayerPositionConstraint(); 208 } 209 210 void ScrollingCoordinator::updateLayerPositionConstraint(RenderLayer* layer) 211 { 212 ASSERT(layer->hasCompositedLayerMapping()); 213 CompositedLayerMapping* compositedLayerMapping = layer->compositedLayerMapping(); 214 GraphicsLayer* mainLayer = compositedLayerMapping->childForSuperlayers(); 215 216 // Avoid unnecessary commits 217 clearPositionConstraintExceptForLayer(compositedLayerMapping->squashingContainmentLayer(), mainLayer); 218 clearPositionConstraintExceptForLayer(compositedLayerMapping->ancestorClippingLayer(), mainLayer); 219 clearPositionConstraintExceptForLayer(compositedLayerMapping->mainGraphicsLayer(), mainLayer); 220 221 if (WebLayer* scrollableLayer = toWebLayer(mainLayer)) 222 scrollableLayer->setPositionConstraint(computePositionConstraint(layer)); 223 } 224 225 void ScrollingCoordinator::willDestroyScrollableArea(ScrollableArea* scrollableArea) 226 { 227 removeWebScrollbarLayer(scrollableArea, HorizontalScrollbar); 228 removeWebScrollbarLayer(scrollableArea, VerticalScrollbar); 229 } 230 231 void ScrollingCoordinator::removeWebScrollbarLayer(ScrollableArea* scrollableArea, ScrollbarOrientation orientation) 232 { 233 ScrollbarMap& scrollbars = orientation == HorizontalScrollbar ? m_horizontalScrollbars : m_verticalScrollbars; 234 if (OwnPtr<WebScrollbarLayer> scrollbarLayer = scrollbars.take(scrollableArea)) 235 GraphicsLayer::unregisterContentsLayer(scrollbarLayer->layer()); 236 } 237 238 static PassOwnPtr<WebScrollbarLayer> createScrollbarLayer(Scrollbar* scrollbar) 239 { 240 ScrollbarTheme* theme = scrollbar->theme(); 241 blink::WebScrollbarThemePainter painter(theme, scrollbar); 242 OwnPtr<blink::WebScrollbarThemeGeometry> geometry(blink::WebScrollbarThemeGeometryNative::create(theme)); 243 244 OwnPtr<WebScrollbarLayer> scrollbarLayer = adoptPtr(blink::Platform::current()->compositorSupport()->createScrollbarLayer(new blink::WebScrollbarImpl(scrollbar), painter, geometry.leakPtr())); 245 GraphicsLayer::registerContentsLayer(scrollbarLayer->layer()); 246 return scrollbarLayer.release(); 247 } 248 249 PassOwnPtr<WebScrollbarLayer> ScrollingCoordinator::createSolidColorScrollbarLayer(ScrollbarOrientation orientation, int thumbThickness, int trackStart, bool isLeftSideVerticalScrollbar) 250 { 251 blink::WebScrollbar::Orientation webOrientation = (orientation == HorizontalScrollbar) ? blink::WebScrollbar::Horizontal : blink::WebScrollbar::Vertical; 252 OwnPtr<WebScrollbarLayer> scrollbarLayer = adoptPtr(blink::Platform::current()->compositorSupport()->createSolidColorScrollbarLayer(webOrientation, thumbThickness, trackStart, isLeftSideVerticalScrollbar)); 253 GraphicsLayer::registerContentsLayer(scrollbarLayer->layer()); 254 return scrollbarLayer.release(); 255 } 256 257 static void detachScrollbarLayer(GraphicsLayer* scrollbarGraphicsLayer) 258 { 259 ASSERT(scrollbarGraphicsLayer); 260 261 scrollbarGraphicsLayer->setContentsToPlatformLayer(0); 262 scrollbarGraphicsLayer->setDrawsContent(true); 263 } 264 265 static void setupScrollbarLayer(GraphicsLayer* scrollbarGraphicsLayer, WebScrollbarLayer* scrollbarLayer, WebLayer* scrollLayer, WebLayer* containerLayer) 266 { 267 ASSERT(scrollbarGraphicsLayer); 268 ASSERT(scrollbarLayer); 269 270 if (!scrollLayer) { 271 detachScrollbarLayer(scrollbarGraphicsLayer); 272 return; 273 } 274 scrollbarLayer->setScrollLayer(scrollLayer); 275 scrollbarLayer->setClipLayer(containerLayer); 276 scrollbarGraphicsLayer->setContentsToPlatformLayer(scrollbarLayer->layer()); 277 scrollbarGraphicsLayer->setDrawsContent(false); 278 } 279 280 WebScrollbarLayer* ScrollingCoordinator::addWebScrollbarLayer(ScrollableArea* scrollableArea, ScrollbarOrientation orientation, PassOwnPtr<blink::WebScrollbarLayer> scrollbarLayer) 281 { 282 ScrollbarMap& scrollbars = orientation == HorizontalScrollbar ? m_horizontalScrollbars : m_verticalScrollbars; 283 return scrollbars.add(scrollableArea, scrollbarLayer).storedValue->value.get(); 284 } 285 286 WebScrollbarLayer* ScrollingCoordinator::getWebScrollbarLayer(ScrollableArea* scrollableArea, ScrollbarOrientation orientation) 287 { 288 ScrollbarMap& scrollbars = orientation == HorizontalScrollbar ? m_horizontalScrollbars : m_verticalScrollbars; 289 return scrollbars.get(scrollableArea); 290 } 291 292 void ScrollingCoordinator::scrollableAreaScrollbarLayerDidChange(ScrollableArea* scrollableArea, ScrollbarOrientation orientation) 293 { 294 // FIXME: Instead of hardcode here, we should make a setting flag. 295 #if OS(MACOSX) 296 static const bool platformSupportsCoordinatedScrollbar = ScrollAnimatorMac::canUseCoordinatedScrollbar(); 297 static const bool platformSupportsMainFrameOnly = false; // Don't care. 298 #elif OS(ANDROID) 299 static const bool platformSupportsCoordinatedScrollbar = true; 300 static const bool platformSupportsMainFrameOnly = false; 301 #else 302 static const bool platformSupportsCoordinatedScrollbar = true; 303 static const bool platformSupportsMainFrameOnly = true; 304 #endif 305 if (!platformSupportsCoordinatedScrollbar) 306 return; 307 308 bool isMainFrame = isForMainFrame(scrollableArea); 309 if (!isMainFrame && platformSupportsMainFrameOnly) 310 return; 311 312 GraphicsLayer* scrollbarGraphicsLayer = orientation == HorizontalScrollbar 313 ? scrollableArea->layerForHorizontalScrollbar() 314 : scrollableArea->layerForVerticalScrollbar(); 315 316 if (scrollbarGraphicsLayer) { 317 Scrollbar* scrollbar = orientation == HorizontalScrollbar ? scrollableArea->horizontalScrollbar() : scrollableArea->verticalScrollbar(); 318 if (scrollbar->isCustomScrollbar()) { 319 detachScrollbarLayer(scrollbarGraphicsLayer); 320 return; 321 } 322 323 WebScrollbarLayer* scrollbarLayer = getWebScrollbarLayer(scrollableArea, orientation); 324 if (!scrollbarLayer) { 325 Settings* settings = m_page->mainFrame()->settings(); 326 327 OwnPtr<WebScrollbarLayer> webScrollbarLayer; 328 if (settings->useSolidColorScrollbars()) { 329 ASSERT(RuntimeEnabledFeatures::overlayScrollbarsEnabled()); 330 webScrollbarLayer = createSolidColorScrollbarLayer(orientation, scrollbar->theme()->thumbThickness(scrollbar), scrollbar->theme()->trackPosition(scrollbar), scrollableArea->shouldPlaceVerticalScrollbarOnLeft()); 331 } else { 332 webScrollbarLayer = createScrollbarLayer(scrollbar); 333 } 334 scrollbarLayer = addWebScrollbarLayer(scrollableArea, orientation, webScrollbarLayer.release()); 335 } 336 337 // Root layer non-overlay scrollbars should be marked opaque to disable 338 // blending. 339 bool isOpaqueScrollbar = !scrollbar->isOverlayScrollbar(); 340 scrollbarGraphicsLayer->setContentsOpaque(isMainFrame && isOpaqueScrollbar); 341 342 WebLayer* scrollLayer = toWebLayer(scrollableArea->layerForScrolling()); 343 WebLayer* containerLayer = toWebLayer(scrollableArea->layerForContainer()); 344 setupScrollbarLayer(scrollbarGraphicsLayer, scrollbarLayer, scrollLayer, containerLayer); 345 } else 346 removeWebScrollbarLayer(scrollableArea, orientation); 347 } 348 349 bool ScrollingCoordinator::scrollableAreaScrollLayerDidChange(ScrollableArea* scrollableArea) 350 { 351 GraphicsLayer* scrollLayer = scrollableArea->layerForScrolling(); 352 353 if (scrollLayer) { 354 ASSERT(m_page); 355 // With pinch virtual viewport we no longer need to special case the main frame. 356 bool pinchVirtualViewportEnabled = m_page->settings().pinchVirtualViewportEnabled(); 357 bool layerScrollShouldFireGraphicsLayerDidScroll = isForMainFrame(scrollableArea) && !pinchVirtualViewportEnabled; 358 scrollLayer->setScrollableArea(scrollableArea, layerScrollShouldFireGraphicsLayerDidScroll); 359 } 360 361 WebLayer* webLayer = toWebLayer(scrollableArea->layerForScrolling()); 362 WebLayer* containerLayer = toWebLayer(scrollableArea->layerForContainer()); 363 if (webLayer) { 364 webLayer->setScrollClipLayer(containerLayer); 365 webLayer->setScrollPosition(IntPoint(scrollableArea->scrollPosition() - scrollableArea->minimumScrollPosition())); 366 webLayer->setBounds(scrollableArea->contentsSize()); 367 bool canScrollX = scrollableArea->userInputScrollable(HorizontalScrollbar); 368 bool canScrollY = scrollableArea->userInputScrollable(VerticalScrollbar); 369 webLayer->setUserScrollable(canScrollX, canScrollY); 370 } 371 if (WebScrollbarLayer* scrollbarLayer = getWebScrollbarLayer(scrollableArea, HorizontalScrollbar)) { 372 GraphicsLayer* horizontalScrollbarLayer = scrollableArea->layerForHorizontalScrollbar(); 373 if (horizontalScrollbarLayer) 374 setupScrollbarLayer(horizontalScrollbarLayer, scrollbarLayer, webLayer, containerLayer); 375 } 376 if (WebScrollbarLayer* scrollbarLayer = getWebScrollbarLayer(scrollableArea, VerticalScrollbar)) { 377 GraphicsLayer* verticalScrollbarLayer = scrollableArea->layerForVerticalScrollbar(); 378 if (verticalScrollbarLayer) 379 setupScrollbarLayer(verticalScrollbarLayer, scrollbarLayer, webLayer, containerLayer); 380 } 381 382 return !!webLayer; 383 } 384 385 typedef WTF::HashMap<const GraphicsLayer*, Vector<LayoutRect> > GraphicsLayerHitTestRects; 386 387 // In order to do a DFS cross-frame walk of the RenderLayer tree, we need to know which 388 // RenderLayers have child frames inside of them. This computes a mapping for the 389 // current frame which we can consult while walking the layers of that frame. 390 // Whenever we descend into a new frame, a new map will be created. 391 typedef HashMap<const RenderLayer*, Vector<const LocalFrame*> > LayerFrameMap; 392 static void makeLayerChildFrameMap(const LocalFrame* currentFrame, LayerFrameMap* map) 393 { 394 map->clear(); 395 const FrameTree& tree = currentFrame->tree(); 396 for (const Frame* child = tree.firstChild(); child; child = child->tree().nextSibling()) { 397 if (!child->isLocalFrame()) 398 continue; 399 const RenderObject* ownerRenderer = toLocalFrame(child)->ownerRenderer(); 400 if (!ownerRenderer) 401 continue; 402 const RenderLayer* containingLayer = ownerRenderer->enclosingLayer(); 403 LayerFrameMap::iterator iter = map->find(containingLayer); 404 if (iter == map->end()) 405 map->add(containingLayer, Vector<const LocalFrame*>()).storedValue->value.append(toLocalFrame(child)); 406 else 407 iter->value.append(toLocalFrame(child)); 408 } 409 } 410 411 static void projectRectsToGraphicsLayerSpaceRecursive( 412 const RenderLayer* curLayer, 413 const LayerHitTestRects& layerRects, 414 GraphicsLayerHitTestRects& graphicsRects, 415 RenderGeometryMap& geometryMap, 416 HashSet<const RenderLayer*>& layersWithRects, 417 LayerFrameMap& layerChildFrameMap) 418 { 419 // Project any rects for the current layer 420 LayerHitTestRects::const_iterator layerIter = layerRects.find(curLayer); 421 if (layerIter != layerRects.end()) { 422 // Find the enclosing composited layer when it's in another document (for non-composited iframes). 423 const RenderLayer* compositedLayer = layerIter->key->enclosingLayerForPaintInvalidationCrossingFrameBoundaries(); 424 ASSERT(compositedLayer); 425 426 // Find the appropriate GraphicsLayer for the composited RenderLayer. 427 GraphicsLayer* graphicsLayer = compositedLayer->graphicsLayerBackingForScrolling(); 428 429 GraphicsLayerHitTestRects::iterator glIter = graphicsRects.find(graphicsLayer); 430 Vector<LayoutRect>* glRects; 431 if (glIter == graphicsRects.end()) 432 glRects = &graphicsRects.add(graphicsLayer, Vector<LayoutRect>()).storedValue->value; 433 else 434 glRects = &glIter->value; 435 436 // Transform each rect to the co-ordinate space of the graphicsLayer. 437 for (size_t i = 0; i < layerIter->value.size(); ++i) { 438 LayoutRect rect = layerIter->value[i]; 439 if (compositedLayer != curLayer) { 440 FloatQuad compositorQuad = geometryMap.mapToContainer(rect, compositedLayer->renderer()); 441 rect = LayoutRect(compositorQuad.boundingBox()); 442 // If the enclosing composited layer itself is scrolled, we have to undo the subtraction 443 // of its scroll offset since we want the offset relative to the scrolling content, not 444 // the element itself. 445 if (compositedLayer->renderer()->hasOverflowClip()) 446 rect.move(compositedLayer->renderBox()->scrolledContentOffset()); 447 } 448 RenderLayer::mapRectToPaintBackingCoordinates(compositedLayer->renderer(), rect); 449 glRects->append(rect); 450 } 451 } 452 453 // Walk child layers of interest 454 for (const RenderLayer* childLayer = curLayer->firstChild(); childLayer; childLayer = childLayer->nextSibling()) { 455 if (layersWithRects.contains(childLayer)) { 456 geometryMap.pushMappingsToAncestor(childLayer, curLayer); 457 projectRectsToGraphicsLayerSpaceRecursive(childLayer, layerRects, graphicsRects, geometryMap, layersWithRects, layerChildFrameMap); 458 geometryMap.popMappingsToAncestor(curLayer); 459 } 460 } 461 462 // If this layer has any frames of interest as a child of it, walk those (with an updated frame map). 463 LayerFrameMap::iterator mapIter = layerChildFrameMap.find(curLayer); 464 if (mapIter != layerChildFrameMap.end()) { 465 for (size_t i = 0; i < mapIter->value.size(); i++) { 466 const LocalFrame* childFrame = mapIter->value[i]; 467 const RenderLayer* childLayer = childFrame->view()->renderView()->layer(); 468 if (layersWithRects.contains(childLayer)) { 469 LayerFrameMap newLayerChildFrameMap; 470 makeLayerChildFrameMap(childFrame, &newLayerChildFrameMap); 471 geometryMap.pushMappingsToAncestor(childLayer, curLayer); 472 projectRectsToGraphicsLayerSpaceRecursive(childLayer, layerRects, graphicsRects, geometryMap, layersWithRects, newLayerChildFrameMap); 473 geometryMap.popMappingsToAncestor(curLayer); 474 } 475 } 476 } 477 } 478 479 static void projectRectsToGraphicsLayerSpace(LocalFrame* mainFrame, const LayerHitTestRects& layerRects, GraphicsLayerHitTestRects& graphicsRects) 480 { 481 TRACE_EVENT0("input", "ScrollingCoordinator::projectRectsToGraphicsLayerSpace"); 482 bool touchHandlerInChildFrame = false; 483 484 // We have a set of rects per RenderLayer, we need to map them to their bounding boxes in their 485 // enclosing composited layer. To do this most efficiently we'll walk the RenderLayer tree using 486 // RenderGeometryMap. First record all the branches we should traverse in the tree (including 487 // all documents on the page). 488 HashSet<const RenderLayer*> layersWithRects; 489 for (LayerHitTestRects::const_iterator layerIter = layerRects.begin(); layerIter != layerRects.end(); ++layerIter) { 490 const RenderLayer* layer = layerIter->key; 491 do { 492 if (!layersWithRects.add(layer).isNewEntry) 493 break; 494 495 if (layer->parent()) { 496 layer = layer->parent(); 497 } else if (RenderObject* parentDocRenderer = layer->renderer()->frame()->ownerRenderer()) { 498 layer = parentDocRenderer->enclosingLayer(); 499 touchHandlerInChildFrame = true; 500 } 501 } while (layer); 502 } 503 504 // Now walk the layer projecting rects while maintaining a RenderGeometryMap 505 MapCoordinatesFlags flags = UseTransforms; 506 if (touchHandlerInChildFrame) 507 flags |= TraverseDocumentBoundaries; 508 RenderLayer* rootLayer = mainFrame->contentRenderer()->layer(); 509 RenderGeometryMap geometryMap(flags); 510 geometryMap.pushMappingsToAncestor(rootLayer, 0); 511 LayerFrameMap layerChildFrameMap; 512 makeLayerChildFrameMap(mainFrame, &layerChildFrameMap); 513 projectRectsToGraphicsLayerSpaceRecursive(rootLayer, layerRects, graphicsRects, geometryMap, layersWithRects, layerChildFrameMap); 514 } 515 516 void ScrollingCoordinator::updateTouchEventTargetRectsIfNeeded() 517 { 518 TRACE_EVENT0("input", "ScrollingCoordinator::updateTouchEventTargetRectsIfNeeded"); 519 520 if (!RuntimeEnabledFeatures::touchEnabled()) 521 return; 522 523 LayerHitTestRects touchEventTargetRects; 524 computeTouchEventTargetRects(touchEventTargetRects); 525 setTouchEventTargetRects(touchEventTargetRects); 526 } 527 528 void ScrollingCoordinator::reset() 529 { 530 for (ScrollbarMap::iterator it = m_horizontalScrollbars.begin(); it != m_horizontalScrollbars.end(); ++it) 531 GraphicsLayer::unregisterContentsLayer(it->value->layer()); 532 for (ScrollbarMap::iterator it = m_verticalScrollbars.begin(); it != m_verticalScrollbars.end(); ++it) 533 GraphicsLayer::unregisterContentsLayer(it->value->layer()); 534 535 m_horizontalScrollbars.clear(); 536 m_verticalScrollbars.clear(); 537 m_layersWithTouchRects.clear(); 538 m_wasFrameScrollable = false; 539 540 // This is retained for testing. 541 m_lastMainThreadScrollingReasons = 0; 542 setShouldUpdateScrollLayerPositionOnMainThread(m_lastMainThreadScrollingReasons); 543 } 544 545 // Note that in principle this could be called more often than computeTouchEventTargetRects, for 546 // example during a non-composited scroll (although that's not yet implemented - crbug.com/261307). 547 void ScrollingCoordinator::setTouchEventTargetRects(LayerHitTestRects& layerRects) 548 { 549 TRACE_EVENT0("input", "ScrollingCoordinator::setTouchEventTargetRects"); 550 551 // Update the list of layers with touch hit rects. 552 HashSet<const RenderLayer*> oldLayersWithTouchRects; 553 m_layersWithTouchRects.swap(oldLayersWithTouchRects); 554 for (LayerHitTestRects::iterator it = layerRects.begin(); it != layerRects.end(); ++it) { 555 if (!it->value.isEmpty()) { 556 const RenderLayer* compositedLayer = it->key->enclosingLayerForPaintInvalidationCrossingFrameBoundaries(); 557 ASSERT(compositedLayer); 558 m_layersWithTouchRects.add(compositedLayer); 559 } 560 } 561 562 // Ensure we have an entry for each composited layer that previously had rects (so that old 563 // ones will get cleared out). Note that ideally we'd track this on GraphicsLayer instead of 564 // RenderLayer, but we have no good hook into the lifetime of a GraphicsLayer. 565 for (HashSet<const RenderLayer*>::iterator it = oldLayersWithTouchRects.begin(); it != oldLayersWithTouchRects.end(); ++it) { 566 if (!layerRects.contains(*it)) 567 layerRects.add(*it, Vector<LayoutRect>()); 568 } 569 570 GraphicsLayerHitTestRects graphicsLayerRects; 571 projectRectsToGraphicsLayerSpace(m_page->deprecatedLocalMainFrame(), layerRects, graphicsLayerRects); 572 573 for (GraphicsLayerHitTestRects::const_iterator iter = graphicsLayerRects.begin(); iter != graphicsLayerRects.end(); ++iter) { 574 const GraphicsLayer* graphicsLayer = iter->key; 575 WebVector<WebRect> webRects(iter->value.size()); 576 for (size_t i = 0; i < iter->value.size(); ++i) 577 webRects[i] = enclosingIntRect(iter->value[i]); 578 graphicsLayer->platformLayer()->setTouchEventHandlerRegion(webRects); 579 } 580 } 581 582 void ScrollingCoordinator::touchEventTargetRectsDidChange() 583 { 584 if (!RuntimeEnabledFeatures::touchEnabled()) 585 return; 586 587 // Wait until after layout to update. 588 if (!m_page->deprecatedLocalMainFrame()->view() || m_page->deprecatedLocalMainFrame()->view()->needsLayout()) 589 return; 590 591 // FIXME: scheduleAnimation() is just a method of forcing the compositor to realize that it 592 // needs to commit here. We should expose a cleaner API for this. 593 RenderView* renderView = m_page->deprecatedLocalMainFrame()->contentRenderer(); 594 if (renderView && renderView->compositor() && renderView->compositor()->staleInCompositingMode()) 595 m_page->deprecatedLocalMainFrame()->view()->scheduleAnimation(); 596 597 m_touchEventTargetRectsAreDirty = true; 598 } 599 600 void ScrollingCoordinator::updateScrollParentForGraphicsLayer(GraphicsLayer* child, RenderLayer* parent) 601 { 602 WebLayer* scrollParentWebLayer = 0; 603 if (parent && parent->hasCompositedLayerMapping()) 604 scrollParentWebLayer = toWebLayer(parent->compositedLayerMapping()->scrollingContentsLayer()); 605 606 child->setScrollParent(scrollParentWebLayer); 607 } 608 609 void ScrollingCoordinator::updateClipParentForGraphicsLayer(GraphicsLayer* child, RenderLayer* parent) 610 { 611 WebLayer* clipParentWebLayer = 0; 612 if (parent && parent->hasCompositedLayerMapping()) 613 clipParentWebLayer = toWebLayer(parent->compositedLayerMapping()->parentForSublayers()); 614 615 child->setClipParent(clipParentWebLayer); 616 } 617 618 void ScrollingCoordinator::willDestroyRenderLayer(RenderLayer* layer) 619 { 620 m_layersWithTouchRects.remove(layer); 621 } 622 623 void ScrollingCoordinator::updateHaveWheelEventHandlers() 624 { 625 ASSERT(isMainThread()); 626 ASSERT(m_page); 627 if (!m_page->mainFrame()->isLocalFrame() || !m_page->deprecatedLocalMainFrame()->view()) 628 return; 629 630 if (WebLayer* scrollLayer = toWebLayer(m_page->deprecatedLocalMainFrame()->view()->layerForScrolling())) { 631 bool haveHandlers = m_page->frameHost().eventHandlerRegistry().hasEventHandlers(EventHandlerRegistry::WheelEvent); 632 scrollLayer->setHaveWheelEventHandlers(haveHandlers); 633 } 634 } 635 636 void ScrollingCoordinator::updateHaveScrollEventHandlers() 637 { 638 ASSERT(isMainThread()); 639 ASSERT(m_page); 640 if (!m_page->mainFrame()->isLocalFrame() || !m_page->deprecatedLocalMainFrame()->view()) 641 return; 642 643 // Currently the compositor only cares whether there are scroll handlers anywhere on the page 644 // instead on a per-layer basis. We therefore only update this information for the root 645 // scrolling layer. 646 if (WebLayer* scrollLayer = toWebLayer(m_page->deprecatedLocalMainFrame()->view()->layerForScrolling())) { 647 bool haveHandlers = m_page->frameHost().eventHandlerRegistry().hasEventHandlers(EventHandlerRegistry::ScrollEvent); 648 scrollLayer->setHaveScrollEventHandlers(haveHandlers); 649 } 650 } 651 652 void ScrollingCoordinator::setShouldUpdateScrollLayerPositionOnMainThread(MainThreadScrollingReasons reasons) 653 { 654 if (!m_page->mainFrame()->isLocalFrame()) 655 return; 656 if (WebLayer* scrollLayer = toWebLayer(m_page->deprecatedLocalMainFrame()->view()->layerForScrolling())) { 657 m_lastMainThreadScrollingReasons = reasons; 658 scrollLayer->setShouldScrollOnMainThread(reasons); 659 } 660 } 661 662 void ScrollingCoordinator::willBeDestroyed() 663 { 664 ASSERT(m_page); 665 m_page = 0; 666 for (ScrollbarMap::iterator it = m_horizontalScrollbars.begin(); it != m_horizontalScrollbars.end(); ++it) 667 GraphicsLayer::unregisterContentsLayer(it->value->layer()); 668 for (ScrollbarMap::iterator it = m_verticalScrollbars.begin(); it != m_verticalScrollbars.end(); ++it) 669 GraphicsLayer::unregisterContentsLayer(it->value->layer()); 670 } 671 672 bool ScrollingCoordinator::coordinatesScrollingForFrameView(FrameView* frameView) const 673 { 674 ASSERT(isMainThread()); 675 ASSERT(m_page); 676 677 // We currently only handle the main frame. 678 if (&frameView->frame() != m_page->mainFrame()) 679 return false; 680 681 if (!m_page->mainFrame()->isLocalFrame()) 682 return false; 683 684 // We currently only support composited mode. 685 RenderView* renderView = m_page->deprecatedLocalMainFrame()->contentRenderer(); 686 if (!renderView) 687 return false; 688 return renderView->usesCompositing(); 689 } 690 691 Region ScrollingCoordinator::computeShouldHandleScrollGestureOnMainThreadRegion(const LocalFrame* frame, const IntPoint& frameLocation) const 692 { 693 Region shouldHandleScrollGestureOnMainThreadRegion; 694 FrameView* frameView = frame->view(); 695 if (!frameView) 696 return shouldHandleScrollGestureOnMainThreadRegion; 697 698 IntPoint offset = frameLocation; 699 offset.moveBy(frameView->frameRect().location()); 700 701 if (const FrameView::ScrollableAreaSet* scrollableAreas = frameView->scrollableAreas()) { 702 for (FrameView::ScrollableAreaSet::const_iterator it = scrollableAreas->begin(), end = scrollableAreas->end(); it != end; ++it) { 703 ScrollableArea* scrollableArea = *it; 704 // Composited scrollable areas can be scrolled off the main thread. 705 if (scrollableArea->usesCompositedScrolling()) 706 continue; 707 IntRect box = scrollableArea->scrollableAreaBoundingBox(); 708 box.moveBy(offset); 709 shouldHandleScrollGestureOnMainThreadRegion.unite(box); 710 } 711 } 712 713 // We use GestureScrollBegin/Update/End for moving the resizer handle. So we mark these 714 // small resizer areas as non-fast-scrollable to allow the scroll gestures to be passed to 715 // main thread if they are targeting the resizer area. (Resizing is done in EventHandler.cpp 716 // on main thread). 717 if (const FrameView::ResizerAreaSet* resizerAreas = frameView->resizerAreas()) { 718 for (FrameView::ResizerAreaSet::const_iterator it = resizerAreas->begin(), end = resizerAreas->end(); it != end; ++it) { 719 RenderBox* box = *it; 720 IntRect bounds = box->absoluteBoundingBoxRect(); 721 IntRect corner = box->layer()->scrollableArea()->touchResizerCornerRect(bounds); 722 corner.moveBy(offset); 723 shouldHandleScrollGestureOnMainThreadRegion.unite(corner); 724 } 725 } 726 727 if (const HashSet<RefPtr<Widget> >* children = frameView->children()) { 728 for (HashSet<RefPtr<Widget> >::const_iterator it = children->begin(), end = children->end(); it != end; ++it) { 729 if (!(*it)->isPluginView()) 730 continue; 731 732 PluginView* pluginView = toPluginView(it->get()); 733 if (pluginView->wantsWheelEvents()) 734 shouldHandleScrollGestureOnMainThreadRegion.unite(pluginView->frameRect()); 735 } 736 } 737 738 const FrameTree& tree = frame->tree(); 739 for (Frame* subFrame = tree.firstChild(); subFrame; subFrame = subFrame->tree().nextSibling()) { 740 if (subFrame->isLocalFrame()) 741 shouldHandleScrollGestureOnMainThreadRegion.unite(computeShouldHandleScrollGestureOnMainThreadRegion(toLocalFrame(subFrame), offset)); 742 } 743 744 return shouldHandleScrollGestureOnMainThreadRegion; 745 } 746 747 static void accumulateDocumentTouchEventTargetRects(LayerHitTestRects& rects, const Document* document) 748 { 749 ASSERT(document); 750 const EventTargetSet* targets = document->frameHost()->eventHandlerRegistry().eventHandlerTargets(EventHandlerRegistry::TouchEvent); 751 if (!targets) 752 return; 753 754 // If there's a handler on the window, document, html or body element (fairly common in practice), 755 // then we can quickly mark the entire document and skip looking at any other handlers. 756 // Note that technically a handler on the body doesn't cover the whole document, but it's 757 // reasonable to be conservative and report the whole document anyway. 758 // 759 // Fullscreen HTML5 video when OverlayFullscreenVideo is enabled is implemented by replacing the 760 // root cc::layer with the video layer so doing this optimization causes the compositor to think 761 // that there are no handlers, therefore skip it. 762 if (!document->renderView()->compositor()->inOverlayFullscreenVideo()) { 763 for (EventTargetSet::const_iterator iter = targets->begin(); iter != targets->end(); ++iter) { 764 EventTarget* target = iter->key; 765 Node* node = target->toNode(); 766 if (target->toDOMWindow() || node == document || node == document->documentElement() || node == document->body()) { 767 if (RenderView* rendererView = document->renderView()) { 768 rendererView->computeLayerHitTestRects(rects); 769 } 770 return; 771 } 772 } 773 } 774 775 for (EventTargetSet::const_iterator iter = targets->begin(); iter != targets->end(); ++iter) { 776 EventTarget* target = iter->key; 777 Node* node = target->toNode(); 778 if (!node || !node->inDocument()) 779 continue; 780 781 // If the document belongs to an invisible subframe it does not have a composited layer 782 // and should be skipped. 783 if (node->document().isInInvisibleSubframe()) 784 continue; 785 786 if (node->isDocumentNode() && node != document) { 787 accumulateDocumentTouchEventTargetRects(rects, toDocument(node)); 788 } else if (RenderObject* renderer = node->renderer()) { 789 // If the set also contains one of our ancestor nodes then processing 790 // this node would be redundant. 791 bool hasTouchEventTargetAncestor = false; 792 for (Node* ancestor = node->parentNode(); ancestor && !hasTouchEventTargetAncestor; ancestor = ancestor->parentNode()) { 793 if (targets->contains(ancestor)) 794 hasTouchEventTargetAncestor = true; 795 } 796 if (!hasTouchEventTargetAncestor) { 797 // Walk up the tree to the outermost non-composited scrollable layer. 798 RenderLayer* enclosingNonCompositedScrollLayer = 0; 799 for (RenderLayer* parent = renderer->enclosingLayer(); parent && parent->compositingState() == NotComposited; parent = parent->parent()) { 800 if (parent->scrollsOverflow()) 801 enclosingNonCompositedScrollLayer = parent; 802 } 803 804 // Report the whole non-composited scroll layer as a touch hit rect because any 805 // rects inside of it may move around relative to their enclosing composited layer 806 // without causing the rects to be recomputed. Non-composited scrolling occurs on 807 // the main thread, so we're not getting much benefit from compositor touch hit 808 // testing in this case anyway. 809 if (enclosingNonCompositedScrollLayer) 810 enclosingNonCompositedScrollLayer->computeSelfHitTestRects(rects); 811 812 renderer->computeLayerHitTestRects(rects); 813 } 814 } 815 } 816 } 817 818 void ScrollingCoordinator::computeTouchEventTargetRects(LayerHitTestRects& rects) 819 { 820 TRACE_EVENT0("input", "ScrollingCoordinator::computeTouchEventTargetRects"); 821 ASSERT(RuntimeEnabledFeatures::touchEnabled()); 822 823 Document* document = m_page->deprecatedLocalMainFrame()->document(); 824 if (!document || !document->view()) 825 return; 826 827 accumulateDocumentTouchEventTargetRects(rects, document); 828 } 829 830 void ScrollingCoordinator::frameViewHasSlowRepaintObjectsDidChange(FrameView* frameView) 831 { 832 ASSERT(isMainThread()); 833 ASSERT(m_page); 834 835 if (!coordinatesScrollingForFrameView(frameView)) 836 return; 837 838 m_shouldScrollOnMainThreadDirty = true; 839 } 840 841 void ScrollingCoordinator::frameViewFixedObjectsDidChange(FrameView* frameView) 842 { 843 ASSERT(isMainThread()); 844 ASSERT(m_page); 845 846 if (!coordinatesScrollingForFrameView(frameView)) 847 return; 848 849 m_shouldScrollOnMainThreadDirty = true; 850 } 851 852 bool ScrollingCoordinator::isForMainFrame(ScrollableArea* scrollableArea) const 853 { 854 return m_page->mainFrame()->isLocalFrame() ? scrollableArea == m_page->deprecatedLocalMainFrame()->view() : false; 855 } 856 857 void ScrollingCoordinator::frameViewRootLayerDidChange(FrameView* frameView) 858 { 859 ASSERT(isMainThread()); 860 ASSERT(m_page); 861 862 if (!coordinatesScrollingForFrameView(frameView)) 863 return; 864 865 notifyLayoutUpdated(); 866 updateHaveWheelEventHandlers(); 867 updateHaveScrollEventHandlers(); 868 } 869 870 #if OS(MACOSX) 871 void ScrollingCoordinator::handleWheelEventPhase(PlatformWheelEventPhase phase) 872 { 873 ASSERT(isMainThread()); 874 875 if (!m_page) 876 return; 877 878 FrameView* frameView = m_page->deprecatedLocalMainFrame()->view(); 879 if (!frameView) 880 return; 881 882 frameView->scrollAnimator()->handleWheelEventPhase(phase); 883 } 884 #endif 885 886 bool ScrollingCoordinator::hasVisibleSlowRepaintViewportConstrainedObjects(FrameView* frameView) const 887 { 888 const FrameView::ViewportConstrainedObjectSet* viewportConstrainedObjects = frameView->viewportConstrainedObjects(); 889 if (!viewportConstrainedObjects) 890 return false; 891 892 for (FrameView::ViewportConstrainedObjectSet::const_iterator it = viewportConstrainedObjects->begin(), end = viewportConstrainedObjects->end(); it != end; ++it) { 893 RenderObject* renderer = *it; 894 ASSERT(renderer->isBoxModelObject() && renderer->hasLayer()); 895 ASSERT(renderer->style()->position() == FixedPosition); 896 RenderLayer* layer = toRenderBoxModelObject(renderer)->layer(); 897 898 // Whether the RenderLayer scrolls with the viewport is a tree-depenent 899 // property and our viewportConstrainedObjects collection is maintained 900 // with only RenderObject-level information. 901 if (!layer->scrollsWithViewport()) 902 continue; 903 904 // If the whole subtree is invisible, there's no reason to scroll on 905 // the main thread because we don't need to generate invalidations 906 // for invisible content. 907 if (layer->subtreeIsInvisible()) 908 continue; 909 910 // We're only smart enough to scroll viewport-constrainted objects 911 // in the compositor if they have their own backing or they paint 912 // into a grouped back (which necessarily all have the same viewport 913 // constraints). 914 CompositingState compositingState = layer->compositingState(); 915 if (compositingState != PaintsIntoOwnBacking && compositingState != PaintsIntoGroupedBacking) 916 return true; 917 } 918 return false; 919 } 920 921 MainThreadScrollingReasons ScrollingCoordinator::mainThreadScrollingReasons() const 922 { 923 MainThreadScrollingReasons reasons = static_cast<MainThreadScrollingReasons>(0); 924 925 if (!m_page->settings().threadedScrollingEnabled()) 926 reasons |= ThreadedScrollingDisabled; 927 928 if (!m_page->mainFrame()->isLocalFrame()) 929 return reasons; 930 FrameView* frameView = m_page->deprecatedLocalMainFrame()->view(); 931 if (!frameView) 932 return reasons; 933 934 if (frameView->hasSlowRepaintObjects()) 935 reasons |= HasSlowRepaintObjects; 936 FrameView::ScrollingReasons scrollingReasons = frameView->scrollingReasons(); 937 const bool mayBeScrolledByInput = (scrollingReasons == FrameView::Scrollable); 938 const bool mayBeScrolledByScript = mayBeScrolledByInput || (scrollingReasons == 939 FrameView::NotScrollableExplicitlyDisabled); 940 941 // TODO(awoloszyn) Currently crbug.com/304810 will let certain 942 // overflow:hidden elements scroll on the compositor thread, so we should 943 // not let this move there path as an optimization, when we have slow-repaint 944 // elements. 945 if (mayBeScrolledByScript && hasVisibleSlowRepaintViewportConstrainedObjects(frameView)) { 946 reasons |= HasNonLayerViewportConstrainedObjects; 947 } 948 949 return reasons; 950 } 951 952 String ScrollingCoordinator::mainThreadScrollingReasonsAsText(MainThreadScrollingReasons reasons) 953 { 954 StringBuilder stringBuilder; 955 956 if (reasons & ScrollingCoordinator::HasSlowRepaintObjects) 957 stringBuilder.appendLiteral("Has slow repaint objects, "); 958 if (reasons & ScrollingCoordinator::HasViewportConstrainedObjectsWithoutSupportingFixedLayers) 959 stringBuilder.appendLiteral("Has viewport constrained objects without supporting fixed layers, "); 960 if (reasons & ScrollingCoordinator::HasNonLayerViewportConstrainedObjects) 961 stringBuilder.appendLiteral("Has non-layer viewport-constrained objects, "); 962 if (reasons & ScrollingCoordinator::ThreadedScrollingDisabled) 963 stringBuilder.appendLiteral("Threaded scrolling is disabled, "); 964 965 if (stringBuilder.length()) 966 stringBuilder.resize(stringBuilder.length() - 2); 967 return stringBuilder.toString(); 968 } 969 970 String ScrollingCoordinator::mainThreadScrollingReasonsAsText() const 971 { 972 ASSERT(m_page->deprecatedLocalMainFrame()->document()->lifecycle().state() >= DocumentLifecycle::CompositingClean); 973 return mainThreadScrollingReasonsAsText(m_lastMainThreadScrollingReasons); 974 } 975 976 bool ScrollingCoordinator::frameViewIsDirty() const 977 { 978 FrameView* frameView = m_page->mainFrame()->isLocalFrame() ? m_page->deprecatedLocalMainFrame()->view() : 0; 979 bool frameIsScrollable = frameView && frameView->isScrollable(); 980 if (frameIsScrollable != m_wasFrameScrollable) 981 return true; 982 983 if (WebLayer* scrollLayer = frameView ? toWebLayer(frameView->layerForScrolling()) : 0) 984 return blink::WebSize(frameView->contentsSize()) != scrollLayer->bounds(); 985 return false; 986 } 987 988 } // namespace blink 989