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/html/HTMLElement.h" 34 #include "core/page/Frame.h" 35 #include "core/page/FrameView.h" 36 #include "core/page/Page.h" 37 #include "core/page/Settings.h" 38 #include "core/platform/PlatformWheelEvent.h" 39 #include "core/platform/ScrollAnimator.h" 40 #include "core/platform/ScrollbarTheme.h" 41 #include "core/platform/chromium/TraceEvent.h" 42 #include "core/platform/chromium/support/WebScrollbarImpl.h" 43 #include "core/platform/chromium/support/WebScrollbarThemeGeometryNative.h" 44 #include "core/platform/graphics/GraphicsLayer.h" 45 #include "core/platform/graphics/IntRect.h" 46 #include "core/platform/graphics/Region.h" 47 #include "core/platform/graphics/transforms/TransformState.h" 48 #if OS(DARWIN) 49 #include "core/platform/mac/ScrollAnimatorMac.h" 50 #endif 51 #include "core/plugins/PluginView.h" 52 #include "core/rendering/RenderLayerBacking.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 WebKit::WebLayer; 64 using WebKit::WebLayerPositionConstraint; 65 using WebKit::WebRect; 66 using WebKit::WebScrollbarLayer; 67 using WebKit::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 { 91 } 92 93 ScrollingCoordinator::~ScrollingCoordinator() 94 { 95 ASSERT(!m_page); 96 for (ScrollbarMap::iterator it = m_horizontalScrollbars.begin(); it != m_horizontalScrollbars.end(); ++it) 97 GraphicsLayer::unregisterContentsLayer(it->value->layer()); 98 for (ScrollbarMap::iterator it = m_verticalScrollbars.begin(); it != m_verticalScrollbars.end(); ++it) 99 GraphicsLayer::unregisterContentsLayer(it->value->layer()); 100 101 } 102 103 bool ScrollingCoordinator::touchHitTestingEnabled() const 104 { 105 RenderView* contentRenderer = m_page->mainFrame()->contentRenderer(); 106 Settings* settings = m_page->mainFrame()->document()->settings(); 107 return RuntimeEnabledFeatures::touchEnabled() && settings->compositorTouchHitTesting() && contentRenderer && contentRenderer->usesCompositing(); 108 } 109 110 void ScrollingCoordinator::setShouldHandleScrollGestureOnMainThreadRegion(const Region& region) 111 { 112 if (WebLayer* scrollLayer = scrollingWebLayerForScrollableArea(m_page->mainFrame()->view())) { 113 Vector<IntRect> rects = region.rects(); 114 WebVector<WebRect> webRects(rects.size()); 115 for (size_t i = 0; i < rects.size(); ++i) 116 webRects[i] = rects[i]; 117 scrollLayer->setNonFastScrollableRegion(webRects); 118 } 119 } 120 121 void ScrollingCoordinator::frameViewLayoutUpdated(FrameView* frameView) 122 { 123 TRACE_EVENT0("input", "ScrollingCoordinator::frameViewLayoutUpdated"); 124 125 // Compute the region of the page where we can't handle scroll gestures and mousewheel events 126 // on the impl thread. This currently includes: 127 // 1. All scrollable areas, such as subframes, overflow divs and list boxes, whose composited 128 // scrolling are not enabled. We need to do this even if the frame view whose layout was updated 129 // is not the main frame. 130 // 2. Resize control areas, e.g. the small rect at the right bottom of div/textarea/iframe when 131 // CSS property "resize" is enabled. 132 // 3. Plugin areas. 133 Region shouldHandleScrollGestureOnMainThreadRegion = computeShouldHandleScrollGestureOnMainThreadRegion(m_page->mainFrame(), IntPoint()); 134 setShouldHandleScrollGestureOnMainThreadRegion(shouldHandleScrollGestureOnMainThreadRegion); 135 136 if (touchHitTestingEnabled()) { 137 LayerHitTestRects touchEventTargetRects; 138 computeTouchEventTargetRects(touchEventTargetRects); 139 setTouchEventTargetRects(touchEventTargetRects); 140 } 141 142 if (WebLayer* scrollLayer = scrollingWebLayerForScrollableArea(frameView)) 143 scrollLayer->setBounds(frameView->contentsSize()); 144 } 145 146 void ScrollingCoordinator::setLayerIsContainerForFixedPositionLayers(GraphicsLayer* layer, bool enable) 147 { 148 if (WebLayer* scrollableLayer = scrollingWebLayerForGraphicsLayer(layer)) 149 scrollableLayer->setIsContainerForFixedPositionLayers(enable); 150 } 151 152 static void clearPositionConstraintExceptForLayer(GraphicsLayer* layer, GraphicsLayer* except) 153 { 154 if (layer && layer != except && scrollingWebLayerForGraphicsLayer(layer)) 155 scrollingWebLayerForGraphicsLayer(layer)->setPositionConstraint(WebLayerPositionConstraint()); 156 } 157 158 static WebLayerPositionConstraint computePositionConstraint(const RenderLayer* layer) 159 { 160 ASSERT(layer->isComposited()); 161 do { 162 if (layer->renderer()->style()->position() == FixedPosition) { 163 const RenderObject* fixedPositionObject = layer->renderer(); 164 bool fixedToRight = !fixedPositionObject->style()->right().isAuto(); 165 bool fixedToBottom = !fixedPositionObject->style()->bottom().isAuto(); 166 return WebLayerPositionConstraint::fixedPosition(fixedToRight, fixedToBottom); 167 } 168 169 layer = layer->parent(); 170 } while (layer && !layer->isComposited()); 171 return WebLayerPositionConstraint(); 172 } 173 174 void ScrollingCoordinator::updateLayerPositionConstraint(RenderLayer* layer) 175 { 176 ASSERT(layer->backing()); 177 RenderLayerBacking* backing = layer->backing(); 178 GraphicsLayer* mainLayer = backing->childForSuperlayers(); 179 180 // Avoid unnecessary commits 181 clearPositionConstraintExceptForLayer(backing->ancestorClippingLayer(), mainLayer); 182 clearPositionConstraintExceptForLayer(backing->graphicsLayer(), mainLayer); 183 184 if (WebLayer* scrollableLayer = scrollingWebLayerForGraphicsLayer(mainLayer)) 185 scrollableLayer->setPositionConstraint(computePositionConstraint(layer)); 186 } 187 188 void ScrollingCoordinator::willDestroyScrollableArea(ScrollableArea* scrollableArea) 189 { 190 removeWebScrollbarLayer(scrollableArea, HorizontalScrollbar); 191 removeWebScrollbarLayer(scrollableArea, VerticalScrollbar); 192 } 193 194 void ScrollingCoordinator::removeWebScrollbarLayer(ScrollableArea* scrollableArea, ScrollbarOrientation orientation) 195 { 196 ScrollbarMap& scrollbars = orientation == HorizontalScrollbar ? m_horizontalScrollbars : m_verticalScrollbars; 197 if (OwnPtr<WebScrollbarLayer> scrollbarLayer = scrollbars.take(scrollableArea)) 198 GraphicsLayer::unregisterContentsLayer(scrollbarLayer->layer()); 199 } 200 201 static PassOwnPtr<WebScrollbarLayer> createScrollbarLayer(Scrollbar* scrollbar) 202 { 203 ScrollbarTheme* theme = scrollbar->theme(); 204 WebKit::WebScrollbarThemePainter painter(theme, scrollbar); 205 OwnPtr<WebKit::WebScrollbarThemeGeometry> geometry(WebKit::WebScrollbarThemeGeometryNative::create(theme)); 206 207 OwnPtr<WebScrollbarLayer> scrollbarLayer = adoptPtr(WebKit::Platform::current()->compositorSupport()->createScrollbarLayer(new WebKit::WebScrollbarImpl(scrollbar), painter, geometry.leakPtr())); 208 GraphicsLayer::registerContentsLayer(scrollbarLayer->layer()); 209 return scrollbarLayer.release(); 210 } 211 212 static void detachScrollbarLayer(GraphicsLayer* scrollbarGraphicsLayer) 213 { 214 ASSERT(scrollbarGraphicsLayer); 215 216 scrollbarGraphicsLayer->setContentsToPlatformLayer(0); 217 scrollbarGraphicsLayer->setDrawsContent(true); 218 } 219 220 static void setupScrollbarLayer(GraphicsLayer* scrollbarGraphicsLayer, WebScrollbarLayer* scrollbarLayer, WebLayer* scrollLayer) 221 { 222 ASSERT(scrollbarGraphicsLayer); 223 ASSERT(scrollbarLayer); 224 225 if (!scrollLayer) { 226 detachScrollbarLayer(scrollbarGraphicsLayer); 227 return; 228 } 229 scrollbarLayer->setScrollLayer(scrollLayer); 230 scrollbarGraphicsLayer->setContentsToPlatformLayer(scrollbarLayer->layer()); 231 scrollbarGraphicsLayer->setDrawsContent(false); 232 } 233 234 WebScrollbarLayer* ScrollingCoordinator::addWebScrollbarLayer(ScrollableArea* scrollableArea, ScrollbarOrientation orientation, PassOwnPtr<WebKit::WebScrollbarLayer> scrollbarLayer) 235 { 236 ScrollbarMap& scrollbars = orientation == HorizontalScrollbar ? m_horizontalScrollbars : m_verticalScrollbars; 237 return scrollbars.add(scrollableArea, scrollbarLayer).iterator->value.get(); 238 } 239 240 WebScrollbarLayer* ScrollingCoordinator::getWebScrollbarLayer(ScrollableArea* scrollableArea, ScrollbarOrientation orientation) 241 { 242 ScrollbarMap& scrollbars = orientation == HorizontalScrollbar ? m_horizontalScrollbars : m_verticalScrollbars; 243 return scrollbars.get(scrollableArea); 244 } 245 246 void ScrollingCoordinator::scrollableAreaScrollbarLayerDidChange(ScrollableArea* scrollableArea, ScrollbarOrientation orientation) 247 { 248 // FIXME: Instead of hardcode here, we should make a setting flag. 249 #if OS(DARWIN) 250 static const bool platformSupportsCoordinatedScrollbar = ScrollAnimatorMac::canUseCoordinatedScrollbar(); 251 static const bool platformSupportsMainFrameOnly = false; // Don't care. 252 #elif OS(ANDROID) 253 static const bool platformSupportsCoordinatedScrollbar = true; 254 static const bool platformSupportsMainFrameOnly = false; 255 #else 256 static const bool platformSupportsCoordinatedScrollbar = true; 257 static const bool platformSupportsMainFrameOnly = true; 258 #endif 259 if (!platformSupportsCoordinatedScrollbar) 260 return; 261 262 bool isMainFrame = isForMainFrame(scrollableArea); 263 if (!isMainFrame && platformSupportsMainFrameOnly) 264 return; 265 266 GraphicsLayer* scrollbarGraphicsLayer = orientation == HorizontalScrollbar ? horizontalScrollbarLayerForScrollableArea(scrollableArea) : verticalScrollbarLayerForScrollableArea(scrollableArea); 267 if (scrollbarGraphicsLayer) { 268 Scrollbar* scrollbar = orientation == HorizontalScrollbar ? scrollableArea->horizontalScrollbar() : scrollableArea->verticalScrollbar(); 269 if (scrollbar->isCustomScrollbar()) { 270 detachScrollbarLayer(scrollbarGraphicsLayer); 271 return; 272 } 273 274 WebScrollbarLayer* scrollbarLayer = getWebScrollbarLayer(scrollableArea, orientation); 275 if (!scrollbarLayer) 276 scrollbarLayer = addWebScrollbarLayer(scrollableArea, orientation, createScrollbarLayer(scrollbar)); 277 278 // Root layer non-overlay scrollbars should be marked opaque to disable 279 // blending. 280 bool isOpaqueScrollbar = !scrollbar->isOverlayScrollbar(); 281 if (!scrollbarGraphicsLayer->contentsOpaque()) 282 scrollbarGraphicsLayer->setContentsOpaque(isMainFrame && isOpaqueScrollbar); 283 scrollbarLayer->layer()->setOpaque(scrollbarGraphicsLayer->contentsOpaque()); 284 285 setupScrollbarLayer(scrollbarGraphicsLayer, scrollbarLayer, scrollingWebLayerForScrollableArea(scrollableArea)); 286 } else 287 removeWebScrollbarLayer(scrollableArea, orientation); 288 } 289 290 bool ScrollingCoordinator::scrollableAreaScrollLayerDidChange(ScrollableArea* scrollableArea) 291 { 292 GraphicsLayer* scrollLayer = scrollLayerForScrollableArea(scrollableArea); 293 if (scrollLayer) { 294 bool isMainFrame = isForMainFrame(scrollableArea); 295 scrollLayer->setScrollableArea(scrollableArea, isMainFrame); 296 } 297 298 WebLayer* webLayer = scrollingWebLayerForScrollableArea(scrollableArea); 299 if (webLayer) { 300 webLayer->setScrollable(true); 301 webLayer->setScrollPosition(IntPoint(scrollableArea->scrollPosition() - scrollableArea->minimumScrollPosition())); 302 webLayer->setMaxScrollPosition(IntSize(scrollableArea->scrollSize(HorizontalScrollbar), scrollableArea->scrollSize(VerticalScrollbar))); 303 } 304 if (WebScrollbarLayer* scrollbarLayer = getWebScrollbarLayer(scrollableArea, HorizontalScrollbar)) { 305 GraphicsLayer* horizontalScrollbarLayer = horizontalScrollbarLayerForScrollableArea(scrollableArea); 306 if (horizontalScrollbarLayer) 307 setupScrollbarLayer(horizontalScrollbarLayer, scrollbarLayer, webLayer); 308 } 309 if (WebScrollbarLayer* scrollbarLayer = getWebScrollbarLayer(scrollableArea, VerticalScrollbar)) { 310 GraphicsLayer* verticalScrollbarLayer = verticalScrollbarLayerForScrollableArea(scrollableArea); 311 if (verticalScrollbarLayer) 312 setupScrollbarLayer(verticalScrollbarLayer, scrollbarLayer, webLayer); 313 } 314 315 return !!webLayer; 316 } 317 318 static void convertLayerRectsToEnclosingCompositedLayer(const LayerHitTestRects& layerRects, LayerHitTestRects& compositorRects) 319 { 320 TRACE_EVENT0("input", "ScrollingCoordinator::convertLayerRectsToEnclosingCompositedLayer"); 321 322 // We have a set of rects per RenderLayer, we need to map them to their bounding boxes in their 323 // enclosing composited layer. 324 for (LayerHitTestRects::const_iterator layerIter = layerRects.begin(); layerIter != layerRects.end(); ++layerIter) { 325 // Find the enclosing composited layer when it's in another document (for non-composited iframes). 326 RenderLayer* compositedLayer = 0; 327 for (const RenderLayer* layer = layerIter->key; !compositedLayer;) { 328 compositedLayer = layer->enclosingCompositingLayerForRepaint(); 329 if (!compositedLayer) { 330 RenderObject* owner = layer->renderer()->frame()->ownerRenderer(); 331 if (!owner) 332 break; 333 layer = owner->enclosingLayer(); 334 } 335 } 336 if (!compositedLayer) { 337 // Since this machinery is used only when accelerated compositing is enabled, we expect 338 // that every layer should have an enclosing composited layer. 339 ASSERT_NOT_REACHED(); 340 continue; 341 } 342 343 LayerHitTestRects::iterator compIter = compositorRects.find(compositedLayer); 344 if (compIter == compositorRects.end()) 345 compIter = compositorRects.add(compositedLayer, Vector<LayoutRect>()).iterator; 346 // Transform each rect to the co-ordinate space of it's enclosing composited layer. 347 // Ideally we'd compute a transformation matrix once and re-use it for each rect. 348 // RenderGeometryMap can be used for this (but needs to be updated to support crossing 349 // iframe boundaries), but in practice doesn't appear to provide much performance benefit. 350 for (size_t i = 0; i < layerIter->value.size(); ++i) { 351 FloatQuad localQuad(layerIter->value[i]); 352 TransformState transformState(TransformState::ApplyTransformDirection, localQuad); 353 MapCoordinatesFlags flags = ApplyContainerFlip | UseTransforms | TraverseDocumentBoundaries; 354 layerIter->key->renderer()->mapLocalToContainer(compositedLayer->renderer(), transformState, flags); 355 transformState.flatten(); 356 LayoutRect compositorRect = LayoutRect(transformState.lastPlanarQuad().boundingBox()); 357 compIter->value.append(compositorRect); 358 } 359 } 360 } 361 362 // Note that in principle this could be called more often than computeTouchEventTargetRects, for 363 // example during a non-composited scroll (although that's not yet implemented - crbug.com/261307). 364 void ScrollingCoordinator::setTouchEventTargetRects(const LayerHitTestRects& layerRects) 365 { 366 TRACE_EVENT0("input", "ScrollingCoordinator::setTouchEventTargetRects"); 367 368 LayerHitTestRects compositorRects; 369 convertLayerRectsToEnclosingCompositedLayer(layerRects, compositorRects); 370 371 // Inform any observers (i.e. for testing) of these new rects. 372 HashSet<TouchEventTargetRectsObserver*>::iterator stop = m_touchEventTargetRectsObservers.end(); 373 for (HashSet<TouchEventTargetRectsObserver*>::iterator it = m_touchEventTargetRectsObservers.begin(); it != stop; ++it) 374 (*it)->touchEventTargetRectsChanged(compositorRects); 375 376 // Note that ideally we'd clear the touch event handler region on all layers first, 377 // in case there are others that no longer have any handlers. But it's unlikely to 378 // matter much in practice (just makes us more conservative). 379 for (LayerHitTestRects::const_iterator iter = compositorRects.begin(); iter != compositorRects.end(); ++iter) { 380 WebVector<WebRect> webRects(iter->value.size()); 381 for (size_t i = 0; i < iter->value.size(); ++i) 382 webRects[i] = enclosingIntRect(iter->value[i]); 383 RenderLayerBacking* backing = iter->key->backing(); 384 // If the layer is using composited scrolling, then it's the contents that these 385 // rects apply to. 386 GraphicsLayer* graphicsLayer = backing->scrollingContentsLayer(); 387 if (!graphicsLayer) 388 graphicsLayer = backing->graphicsLayer(); 389 graphicsLayer->platformLayer()->setTouchEventHandlerRegion(webRects); 390 } 391 } 392 393 void ScrollingCoordinator::touchEventTargetRectsDidChange(const Document*) 394 { 395 if (!touchHitTestingEnabled()) 396 return; 397 398 // Wait until after layout to update. 399 if (m_page->mainFrame()->view()->needsLayout()) 400 return; 401 402 TRACE_EVENT0("input", "ScrollingCoordinator::touchEventTargetRectsDidChange"); 403 404 LayerHitTestRects touchEventTargetRects; 405 computeTouchEventTargetRects(touchEventTargetRects); 406 setTouchEventTargetRects(touchEventTargetRects); 407 } 408 409 void ScrollingCoordinator::setWheelEventHandlerCount(unsigned count) 410 { 411 if (WebLayer* scrollLayer = scrollingWebLayerForScrollableArea(m_page->mainFrame()->view())) 412 scrollLayer->setHaveWheelEventHandlers(count > 0); 413 } 414 415 void ScrollingCoordinator::recomputeWheelEventHandlerCountForFrameView(FrameView* frameView) 416 { 417 UNUSED_PARAM(frameView); 418 setWheelEventHandlerCount(computeCurrentWheelEventHandlerCount()); 419 } 420 421 void ScrollingCoordinator::setShouldUpdateScrollLayerPositionOnMainThread(MainThreadScrollingReasons reasons) 422 { 423 if (WebLayer* scrollLayer = scrollingWebLayerForScrollableArea(m_page->mainFrame()->view())) 424 scrollLayer->setShouldScrollOnMainThread(reasons); 425 } 426 427 void ScrollingCoordinator::pageDestroyed() 428 { 429 ASSERT(m_page); 430 m_page = 0; 431 } 432 433 bool ScrollingCoordinator::coordinatesScrollingForFrameView(FrameView* frameView) const 434 { 435 ASSERT(isMainThread()); 436 ASSERT(m_page); 437 438 // We currently only handle the main frame. 439 if (frameView->frame() != m_page->mainFrame()) 440 return false; 441 442 // We currently only support composited mode. 443 RenderView* renderView = m_page->mainFrame()->contentRenderer(); 444 if (!renderView) 445 return false; 446 return renderView->usesCompositing(); 447 } 448 449 Region ScrollingCoordinator::computeShouldHandleScrollGestureOnMainThreadRegion(const Frame* frame, const IntPoint& frameLocation) const 450 { 451 Region shouldHandleScrollGestureOnMainThreadRegion; 452 FrameView* frameView = frame->view(); 453 if (!frameView) 454 return shouldHandleScrollGestureOnMainThreadRegion; 455 456 IntPoint offset = frameLocation; 457 offset.moveBy(frameView->frameRect().location()); 458 459 if (const FrameView::ScrollableAreaSet* scrollableAreas = frameView->scrollableAreas()) { 460 for (FrameView::ScrollableAreaSet::const_iterator it = scrollableAreas->begin(), end = scrollableAreas->end(); it != end; ++it) { 461 ScrollableArea* scrollableArea = *it; 462 // Composited scrollable areas can be scrolled off the main thread. 463 if (scrollableArea->usesCompositedScrolling()) 464 continue; 465 IntRect box = scrollableArea->scrollableAreaBoundingBox(); 466 box.moveBy(offset); 467 shouldHandleScrollGestureOnMainThreadRegion.unite(box); 468 } 469 } 470 471 // We use GestureScrollBegin/Update/End for moving the resizer handle. So we mark these 472 // small resizer areas as non-fast-scrollable to allow the scroll gestures to be passed to 473 // main thread if they are targeting the resizer area. (Resizing is done in EventHandler.cpp 474 // on main thread). 475 if (const FrameView::ResizerAreaSet* resizerAreas = frameView->resizerAreas()) { 476 for (FrameView::ResizerAreaSet::const_iterator it = resizerAreas->begin(), end = resizerAreas->end(); it != end; ++it) { 477 RenderLayer* layer = *it; 478 IntRect bounds = layer->renderer()->absoluteBoundingBoxRect(); 479 IntRect corner = layer->resizerCornerRect(bounds, RenderLayer::ResizerForTouch); 480 corner.moveBy(offset); 481 shouldHandleScrollGestureOnMainThreadRegion.unite(corner); 482 } 483 } 484 485 if (const HashSet<RefPtr<Widget> >* children = frameView->children()) { 486 for (HashSet<RefPtr<Widget> >::const_iterator it = children->begin(), end = children->end(); it != end; ++it) { 487 if (!(*it)->isPluginView()) 488 continue; 489 490 PluginView* pluginView = toPluginView((*it).get()); 491 if (pluginView->wantsWheelEvents()) 492 shouldHandleScrollGestureOnMainThreadRegion.unite(pluginView->frameRect()); 493 } 494 } 495 496 FrameTree* tree = frame->tree(); 497 for (Frame* subFrame = tree->firstChild(); subFrame; subFrame = subFrame->tree()->nextSibling()) 498 shouldHandleScrollGestureOnMainThreadRegion.unite(computeShouldHandleScrollGestureOnMainThreadRegion(subFrame, offset)); 499 500 return shouldHandleScrollGestureOnMainThreadRegion; 501 } 502 503 void ScrollingCoordinator::addTouchEventTargetRectsObserver(TouchEventTargetRectsObserver* observer) 504 { 505 m_touchEventTargetRectsObservers.add(observer); 506 } 507 508 void ScrollingCoordinator::removeTouchEventTargetRectsObserver(TouchEventTargetRectsObserver* observer) 509 { 510 m_touchEventTargetRectsObservers.remove(observer); 511 } 512 513 static void accumulateDocumentTouchEventTargetRects(LayerHitTestRects& rects, const Document* document) 514 { 515 ASSERT(document); 516 if (!document->touchEventTargets()) 517 return; 518 519 const TouchEventTargetSet* targets = document->touchEventTargets(); 520 521 // If there's a handler on the document, html or body element (fairly common in practice), 522 // then we can quickly mark the entire document and skip looking at any other handlers. 523 // Note that technically a handler on the body doesn't cover the whole document, but it's 524 // reasonable to be conservative and report the whole document anyway. 525 for (TouchEventTargetSet::const_iterator iter = targets->begin(); iter != targets->end(); ++iter) { 526 Node* target = iter->key; 527 if (target == document || target == document->documentElement() || target == document->body()) { 528 if (RenderObject* renderer = document->renderer()) { 529 renderer->computeLayerHitTestRects(rects); 530 } 531 return; 532 } 533 } 534 535 for (TouchEventTargetSet::const_iterator iter = targets->begin(); iter != targets->end(); ++iter) { 536 const Node* target = iter->key; 537 if (!target->inDocument()) 538 continue; 539 540 if (target->isDocumentNode()) { 541 ASSERT(target != document); 542 accumulateDocumentTouchEventTargetRects(rects, toDocument(target)); 543 } else if (RenderObject* renderer = target->renderer()) { 544 // If the set also contains one of our ancestor nodes then processing 545 // this node would be redundant. 546 bool hasTouchEventTargetAncestor = false; 547 for (Node* ancestor = target->parentNode(); ancestor && !hasTouchEventTargetAncestor; ancestor = ancestor->parentNode()) { 548 if (targets->contains(ancestor)) 549 hasTouchEventTargetAncestor = true; 550 } 551 if (!hasTouchEventTargetAncestor) 552 renderer->computeLayerHitTestRects(rects); 553 } 554 } 555 556 } 557 558 void ScrollingCoordinator::computeTouchEventTargetRects(LayerHitTestRects& rects) 559 { 560 TRACE_EVENT0("input", "ScrollingCoordinator::computeTouchEventTargetRects"); 561 ASSERT(touchHitTestingEnabled()); 562 563 Document* document = m_page->mainFrame()->document(); 564 if (!document || !document->view()) 565 return; 566 567 accumulateDocumentTouchEventTargetRects(rects, document); 568 } 569 570 unsigned ScrollingCoordinator::computeCurrentWheelEventHandlerCount() 571 { 572 unsigned wheelEventHandlerCount = 0; 573 574 for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree()->traverseNext()) { 575 if (frame->document()) 576 wheelEventHandlerCount += frame->document()->wheelEventHandlerCount(); 577 } 578 579 return wheelEventHandlerCount; 580 } 581 582 void ScrollingCoordinator::frameViewWheelEventHandlerCountChanged(FrameView* frameView) 583 { 584 ASSERT(isMainThread()); 585 ASSERT(m_page); 586 587 recomputeWheelEventHandlerCountForFrameView(frameView); 588 } 589 590 void ScrollingCoordinator::frameViewHasSlowRepaintObjectsDidChange(FrameView* frameView) 591 { 592 ASSERT(isMainThread()); 593 ASSERT(m_page); 594 595 if (!coordinatesScrollingForFrameView(frameView)) 596 return; 597 598 updateShouldUpdateScrollLayerPositionOnMainThread(); 599 } 600 601 void ScrollingCoordinator::frameViewFixedObjectsDidChange(FrameView* frameView) 602 { 603 ASSERT(isMainThread()); 604 ASSERT(m_page); 605 606 if (!coordinatesScrollingForFrameView(frameView)) 607 return; 608 609 updateShouldUpdateScrollLayerPositionOnMainThread(); 610 } 611 612 GraphicsLayer* ScrollingCoordinator::scrollLayerForScrollableArea(ScrollableArea* scrollableArea) 613 { 614 return scrollableArea->layerForScrolling(); 615 } 616 617 GraphicsLayer* ScrollingCoordinator::horizontalScrollbarLayerForScrollableArea(ScrollableArea* scrollableArea) 618 { 619 return scrollableArea->layerForHorizontalScrollbar(); 620 } 621 622 GraphicsLayer* ScrollingCoordinator::verticalScrollbarLayerForScrollableArea(ScrollableArea* scrollableArea) 623 { 624 return scrollableArea->layerForVerticalScrollbar(); 625 } 626 627 bool ScrollingCoordinator::isForMainFrame(ScrollableArea* scrollableArea) const 628 { 629 return scrollableArea == m_page->mainFrame()->view(); 630 } 631 632 GraphicsLayer* ScrollingCoordinator::scrollLayerForFrameView(FrameView* frameView) 633 { 634 Frame* frame = frameView->frame(); 635 if (!frame) 636 return 0; 637 638 RenderView* renderView = frame->contentRenderer(); 639 if (!renderView) 640 return 0; 641 return renderView->compositor()->scrollLayer(); 642 } 643 644 GraphicsLayer* ScrollingCoordinator::counterScrollingLayerForFrameView(FrameView*) 645 { 646 return 0; 647 } 648 649 void ScrollingCoordinator::frameViewRootLayerDidChange(FrameView* frameView) 650 { 651 ASSERT(isMainThread()); 652 ASSERT(m_page); 653 654 if (!coordinatesScrollingForFrameView(frameView)) 655 return; 656 657 frameViewLayoutUpdated(frameView); 658 recomputeWheelEventHandlerCountForFrameView(frameView); 659 updateShouldUpdateScrollLayerPositionOnMainThread(); 660 } 661 662 #if OS(DARWIN) 663 void ScrollingCoordinator::handleWheelEventPhase(PlatformWheelEventPhase phase) 664 { 665 ASSERT(isMainThread()); 666 667 if (!m_page) 668 return; 669 670 FrameView* frameView = m_page->mainFrame()->view(); 671 if (!frameView) 672 return; 673 674 frameView->scrollAnimator()->handleWheelEventPhase(phase); 675 } 676 #endif 677 678 bool ScrollingCoordinator::hasVisibleSlowRepaintViewportConstrainedObjects(FrameView* frameView) const 679 { 680 const FrameView::ViewportConstrainedObjectSet* viewportConstrainedObjects = frameView->viewportConstrainedObjects(); 681 if (!viewportConstrainedObjects) 682 return false; 683 684 for (FrameView::ViewportConstrainedObjectSet::const_iterator it = viewportConstrainedObjects->begin(), end = viewportConstrainedObjects->end(); it != end; ++it) { 685 RenderObject* viewportConstrainedObject = *it; 686 if (!viewportConstrainedObject->isBoxModelObject() || !viewportConstrainedObject->hasLayer()) 687 return true; 688 RenderLayer* layer = toRenderBoxModelObject(viewportConstrainedObject)->layer(); 689 // Any explicit reason that a fixed position element is not composited shouldn't cause slow scrolling. 690 if (!layer->isComposited() && layer->viewportConstrainedNotCompositedReason() == RenderLayer::NoNotCompositedReason) 691 return true; 692 } 693 return false; 694 } 695 696 MainThreadScrollingReasons ScrollingCoordinator::mainThreadScrollingReasons() const 697 { 698 FrameView* frameView = m_page->mainFrame()->view(); 699 if (!frameView) 700 return static_cast<MainThreadScrollingReasons>(0); 701 702 MainThreadScrollingReasons mainThreadScrollingReasons = (MainThreadScrollingReasons)0; 703 704 if (frameView->hasSlowRepaintObjects()) 705 mainThreadScrollingReasons |= HasSlowRepaintObjects; 706 if (hasVisibleSlowRepaintViewportConstrainedObjects(frameView)) 707 mainThreadScrollingReasons |= HasNonLayerViewportConstrainedObjects; 708 709 return mainThreadScrollingReasons; 710 } 711 712 void ScrollingCoordinator::updateShouldUpdateScrollLayerPositionOnMainThread() 713 { 714 setShouldUpdateScrollLayerPositionOnMainThread(mainThreadScrollingReasons()); 715 } 716 717 String ScrollingCoordinator::mainThreadScrollingReasonsAsText(MainThreadScrollingReasons reasons) 718 { 719 StringBuilder stringBuilder; 720 721 if (reasons & ScrollingCoordinator::HasSlowRepaintObjects) 722 stringBuilder.append("Has slow repaint objects, "); 723 if (reasons & ScrollingCoordinator::HasViewportConstrainedObjectsWithoutSupportingFixedLayers) 724 stringBuilder.append("Has viewport constrained objects without supporting fixed layers, "); 725 if (reasons & ScrollingCoordinator::HasNonLayerViewportConstrainedObjects) 726 stringBuilder.append("Has non-layer viewport-constrained objects, "); 727 728 if (stringBuilder.length()) 729 stringBuilder.resize(stringBuilder.length() - 2); 730 return stringBuilder.toString(); 731 } 732 733 String ScrollingCoordinator::mainThreadScrollingReasonsAsText() const 734 { 735 return mainThreadScrollingReasonsAsText(mainThreadScrollingReasons()); 736 } 737 738 } // namespace WebCore 739