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