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