2 * Copyright (C) 2011 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
28 #include "core/page/scrolling/ScrollingCoordinator.h"
30 #include "core/dom/Document.h"
31 #include "core/dom/Fullscreen.h"
32 #include "core/dom/Node.h"
33 #include "core/frame/EventHandlerRegistry.h"
34 #include "core/frame/FrameView.h"
35 #include "core/frame/LocalFrame.h"
36 #include "core/frame/Settings.h"
37 #include "core/html/HTMLElement.h"
38 #include "core/page/Page.h"
39 #include "core/plugins/PluginView.h"
40 #include "core/rendering/RenderGeometryMap.h"
41 #include "core/rendering/RenderPart.h"
42 #include "core/rendering/RenderView.h"
43 #include "core/rendering/compositing/CompositedLayerMapping.h"
44 #include "core/rendering/compositing/RenderLayerCompositor.h"
45 #include "platform/RuntimeEnabledFeatures.h"
46 #include "platform/TraceEvent.h"
47 #include "platform/exported/WebScrollbarImpl.h"
48 #include "platform/exported/WebScrollbarThemeGeometryNative.h"
49 #include "platform/geometry/Region.h"
50 #include "platform/geometry/TransformState.h"
51 #include "platform/graphics/GraphicsLayer.h"
53 #include "platform/mac/ScrollAnimatorMac.h"
55 #include "platform/scroll/ScrollAnimator.h"
56 #include "platform/scroll/ScrollbarTheme.h"
57 #include "public/platform/Platform.h"
58 #include "public/platform/WebCompositorSupport.h"
59 #include "public/platform/WebLayerPositionConstraint.h"
60 #include "public/platform/WebScrollbarLayer.h"
61 #include "public/platform/WebScrollbarThemeGeometry.h"
62 #include "public/platform/WebScrollbarThemePainter.h"
63 #include "wtf/text/StringBuilder.h"
65 using blink::WebLayer;
66 using blink::WebLayerPositionConstraint;
68 using blink::WebScrollbarLayer;
69 using blink::WebVector;
73 WebLayer* toWebLayer(blink::GraphicsLayer* layer)
75 return layer ? layer->platformLayer() : nullptr;
82 PassOwnPtr<ScrollingCoordinator> ScrollingCoordinator::create(Page* page)
84 return adoptPtr(new ScrollingCoordinator(page));
87 ScrollingCoordinator::ScrollingCoordinator(Page* page)
89 , m_scrollGestureRegionIsDirty(false)
90 , m_touchEventTargetRectsAreDirty(false)
91 , m_shouldScrollOnMainThreadDirty(false)
92 , m_wasFrameScrollable(false)
93 , m_lastMainThreadScrollingReasons(0)
97 ScrollingCoordinator::~ScrollingCoordinator()
101 void ScrollingCoordinator::setShouldHandleScrollGestureOnMainThreadRegion(const Region& region)
103 if (!m_page->mainFrame()->isLocalFrame())
105 if (WebLayer* scrollLayer = toWebLayer(m_page->deprecatedLocalMainFrame()->view()->layerForScrolling())) {
106 Vector<IntRect> rects = region.rects();
107 WebVector<WebRect> webRects(rects.size());
108 for (size_t i = 0; i < rects.size(); ++i)
109 webRects[i] = rects[i];
110 scrollLayer->setNonFastScrollableRegion(webRects);
114 void ScrollingCoordinator::notifyLayoutUpdated()
116 m_scrollGestureRegionIsDirty = true;
117 m_touchEventTargetRectsAreDirty = true;
118 m_shouldScrollOnMainThreadDirty = true;
121 void ScrollingCoordinator::updateAfterCompositingChangeIfNeeded()
123 if (!m_page->mainFrame()->isLocalFrame())
126 if (!shouldUpdateAfterCompositingChange())
129 TRACE_EVENT0("input", "ScrollingCoordinator::updateAfterCompositingChangeIfNeeded");
131 if (m_scrollGestureRegionIsDirty) {
132 // Compute the region of the page where we can't handle scroll gestures and mousewheel events
133 // on the impl thread. This currently includes:
134 // 1. All scrollable areas, such as subframes, overflow divs and list boxes, whose composited
135 // scrolling are not enabled. We need to do this even if the frame view whose layout was updated
136 // is not the main frame.
137 // 2. Resize control areas, e.g. the small rect at the right bottom of div/textarea/iframe when
138 // CSS property "resize" is enabled.
140 Region shouldHandleScrollGestureOnMainThreadRegion = computeShouldHandleScrollGestureOnMainThreadRegion(m_page->deprecatedLocalMainFrame(), IntPoint());
141 setShouldHandleScrollGestureOnMainThreadRegion(shouldHandleScrollGestureOnMainThreadRegion);
142 m_scrollGestureRegionIsDirty = false;
145 if (m_touchEventTargetRectsAreDirty) {
146 updateTouchEventTargetRectsIfNeeded();
147 m_touchEventTargetRectsAreDirty = false;
150 FrameView* frameView = m_page->deprecatedLocalMainFrame()->view();
151 bool frameIsScrollable = frameView && frameView->isScrollable();
152 if (m_shouldScrollOnMainThreadDirty || m_wasFrameScrollable != frameIsScrollable) {
153 setShouldUpdateScrollLayerPositionOnMainThread(mainThreadScrollingReasons());
154 m_shouldScrollOnMainThreadDirty = false;
156 m_wasFrameScrollable = frameIsScrollable;
158 if (WebLayer* scrollingWebLayer = frameView ? toWebLayer(frameView->layerForScrolling()) : nullptr) {
159 scrollingWebLayer->setBounds(frameView->contentsSize());
161 // If there is a non-root fullscreen element, prevent the main frame
163 Document* mainFrameDocument = m_page->deprecatedLocalMainFrame()->document();
164 Element* fullscreenElement = Fullscreen::fullscreenElementFrom(*mainFrameDocument);
165 if (fullscreenElement && fullscreenElement != mainFrameDocument->documentElement()) {
166 if (m_page->settings().pinchVirtualViewportEnabled()) {
167 toWebLayer(m_page->frameHost().pinchViewport().scrollLayer())->setUserScrollable(false, false);
169 scrollingWebLayer->setBounds(IntSize());
172 if (m_page->settings().pinchVirtualViewportEnabled())
173 toWebLayer(m_page->frameHost().pinchViewport().scrollLayer())->setUserScrollable(true, true);
176 scrollingWebLayer->setUserScrollable(frameView->userInputScrollable(HorizontalScrollbar), frameView->userInputScrollable(VerticalScrollbar));
179 const FrameTree& tree = m_page->mainFrame()->tree();
180 for (const Frame* child = tree.firstChild(); child; child = child->tree().nextSibling()) {
181 if (!child->isLocalFrame())
183 if (WebLayer* scrollLayer = toWebLayer(toLocalFrame(child)->view()->layerForScrolling()))
184 scrollLayer->setBounds(toLocalFrame(child)->view()->contentsSize());
188 void ScrollingCoordinator::setLayerIsContainerForFixedPositionLayers(GraphicsLayer* layer, bool enable)
190 if (WebLayer* scrollableLayer = toWebLayer(layer))
191 scrollableLayer->setIsContainerForFixedPositionLayers(enable);
194 static void clearPositionConstraintExceptForLayer(GraphicsLayer* layer, GraphicsLayer* except)
196 if (layer && layer != except && toWebLayer(layer))
197 toWebLayer(layer)->setPositionConstraint(WebLayerPositionConstraint());
200 static WebLayerPositionConstraint computePositionConstraint(const RenderLayer* layer)
202 ASSERT(layer->hasCompositedLayerMapping());
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);
211 layer = layer->parent();
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();
219 void ScrollingCoordinator::updateLayerPositionConstraint(RenderLayer* layer)
221 ASSERT(layer->hasCompositedLayerMapping());
222 CompositedLayerMapping* compositedLayerMapping = layer->compositedLayerMapping();
223 GraphicsLayer* mainLayer = compositedLayerMapping->childForSuperlayers();
225 // Avoid unnecessary commits
226 clearPositionConstraintExceptForLayer(compositedLayerMapping->squashingContainmentLayer(), mainLayer);
227 clearPositionConstraintExceptForLayer(compositedLayerMapping->ancestorClippingLayer(), mainLayer);
228 clearPositionConstraintExceptForLayer(compositedLayerMapping->mainGraphicsLayer(), mainLayer);
230 if (WebLayer* scrollableLayer = toWebLayer(mainLayer))
231 scrollableLayer->setPositionConstraint(computePositionConstraint(layer));
234 void ScrollingCoordinator::willDestroyScrollableArea(ScrollableArea* scrollableArea)
236 removeWebScrollbarLayer(scrollableArea, HorizontalScrollbar);
237 removeWebScrollbarLayer(scrollableArea, VerticalScrollbar);
240 void ScrollingCoordinator::removeWebScrollbarLayer(ScrollableArea* scrollableArea, ScrollbarOrientation orientation)
242 ScrollbarMap& scrollbars = orientation == HorizontalScrollbar ? m_horizontalScrollbars : m_verticalScrollbars;
243 if (OwnPtr<WebScrollbarLayer> scrollbarLayer = scrollbars.take(scrollableArea))
244 GraphicsLayer::unregisterContentsLayer(scrollbarLayer->layer());
247 static PassOwnPtr<WebScrollbarLayer> createScrollbarLayer(Scrollbar* scrollbar)
249 ScrollbarTheme* theme = scrollbar->theme();
250 blink::WebScrollbarThemePainter painter(theme, scrollbar);
251 OwnPtr<blink::WebScrollbarThemeGeometry> geometry(blink::WebScrollbarThemeGeometryNative::create(theme));
253 OwnPtr<WebScrollbarLayer> scrollbarLayer = adoptPtr(blink::Platform::current()->compositorSupport()->createScrollbarLayer(WebScrollbarImpl::create(scrollbar), painter, geometry.leakPtr()));
254 GraphicsLayer::registerContentsLayer(scrollbarLayer->layer());
255 return scrollbarLayer.release();
258 PassOwnPtr<WebScrollbarLayer> ScrollingCoordinator::createSolidColorScrollbarLayer(ScrollbarOrientation orientation, int thumbThickness, int trackStart, bool isLeftSideVerticalScrollbar)
260 blink::WebScrollbar::Orientation webOrientation = (orientation == HorizontalScrollbar) ? blink::WebScrollbar::Horizontal : blink::WebScrollbar::Vertical;
261 OwnPtr<WebScrollbarLayer> scrollbarLayer = adoptPtr(blink::Platform::current()->compositorSupport()->createSolidColorScrollbarLayer(webOrientation, thumbThickness, trackStart, isLeftSideVerticalScrollbar));
262 GraphicsLayer::registerContentsLayer(scrollbarLayer->layer());
263 return scrollbarLayer.release();
266 static void detachScrollbarLayer(GraphicsLayer* scrollbarGraphicsLayer)
268 ASSERT(scrollbarGraphicsLayer);
270 scrollbarGraphicsLayer->setContentsToPlatformLayer(nullptr);
271 scrollbarGraphicsLayer->setDrawsContent(true);
274 static void setupScrollbarLayer(GraphicsLayer* scrollbarGraphicsLayer, WebScrollbarLayer* scrollbarLayer, WebLayer* scrollLayer, WebLayer* containerLayer)
276 ASSERT(scrollbarGraphicsLayer);
277 ASSERT(scrollbarLayer);
280 detachScrollbarLayer(scrollbarGraphicsLayer);
283 scrollbarLayer->setScrollLayer(scrollLayer);
284 scrollbarLayer->setClipLayer(containerLayer);
285 scrollbarGraphicsLayer->setContentsToPlatformLayer(scrollbarLayer->layer());
286 scrollbarGraphicsLayer->setDrawsContent(false);
289 WebScrollbarLayer* ScrollingCoordinator::addWebScrollbarLayer(ScrollableArea* scrollableArea, ScrollbarOrientation orientation, PassOwnPtr<blink::WebScrollbarLayer> scrollbarLayer)
291 ScrollbarMap& scrollbars = orientation == HorizontalScrollbar ? m_horizontalScrollbars : m_verticalScrollbars;
292 return scrollbars.add(scrollableArea, scrollbarLayer).storedValue->value.get();
295 WebScrollbarLayer* ScrollingCoordinator::getWebScrollbarLayer(ScrollableArea* scrollableArea, ScrollbarOrientation orientation)
297 ScrollbarMap& scrollbars = orientation == HorizontalScrollbar ? m_horizontalScrollbars : m_verticalScrollbars;
298 return scrollbars.get(scrollableArea);
301 void ScrollingCoordinator::scrollableAreaScrollbarLayerDidChange(ScrollableArea* scrollableArea, ScrollbarOrientation orientation)
303 // FIXME: Instead of hardcode here, we should make a setting flag.
305 static const bool platformSupportsCoordinatedScrollbar = ScrollAnimatorMac::canUseCoordinatedScrollbar();
306 static const bool platformSupportsMainFrameOnly = false; // Don't care.
308 static const bool platformSupportsCoordinatedScrollbar = true;
309 static const bool platformSupportsMainFrameOnly = false;
311 static const bool platformSupportsCoordinatedScrollbar = true;
312 static const bool platformSupportsMainFrameOnly = true;
314 if (!platformSupportsCoordinatedScrollbar)
317 bool isMainFrame = isForMainFrame(scrollableArea);
318 if (!isMainFrame && platformSupportsMainFrameOnly)
321 GraphicsLayer* scrollbarGraphicsLayer = orientation == HorizontalScrollbar
322 ? scrollableArea->layerForHorizontalScrollbar()
323 : scrollableArea->layerForVerticalScrollbar();
325 if (scrollbarGraphicsLayer) {
326 Scrollbar* scrollbar = orientation == HorizontalScrollbar ? scrollableArea->horizontalScrollbar() : scrollableArea->verticalScrollbar();
327 if (scrollbar->isCustomScrollbar()) {
328 detachScrollbarLayer(scrollbarGraphicsLayer);
332 WebScrollbarLayer* scrollbarLayer = getWebScrollbarLayer(scrollableArea, orientation);
333 if (!scrollbarLayer) {
334 Settings* settings = m_page->mainFrame()->settings();
336 OwnPtr<WebScrollbarLayer> webScrollbarLayer;
337 if (settings->useSolidColorScrollbars()) {
338 ASSERT(RuntimeEnabledFeatures::overlayScrollbarsEnabled());
339 webScrollbarLayer = createSolidColorScrollbarLayer(orientation, scrollbar->theme()->thumbThickness(scrollbar), scrollbar->theme()->trackPosition(scrollbar), scrollableArea->shouldPlaceVerticalScrollbarOnLeft());
341 webScrollbarLayer = createScrollbarLayer(scrollbar);
343 scrollbarLayer = addWebScrollbarLayer(scrollableArea, orientation, webScrollbarLayer.release());
346 WebLayer* scrollLayer = toWebLayer(scrollableArea->layerForScrolling());
347 WebLayer* containerLayer = toWebLayer(scrollableArea->layerForContainer());
348 setupScrollbarLayer(scrollbarGraphicsLayer, scrollbarLayer, scrollLayer, containerLayer);
350 // Root layer non-overlay scrollbars should be marked opaque to disable
352 bool isOpaqueScrollbar = !scrollbar->isOverlayScrollbar();
353 scrollbarGraphicsLayer->setContentsOpaque(isMainFrame && isOpaqueScrollbar);
355 removeWebScrollbarLayer(scrollableArea, orientation);
358 bool ScrollingCoordinator::scrollableAreaScrollLayerDidChange(ScrollableArea* scrollableArea)
360 GraphicsLayer* scrollLayer = scrollableArea->layerForScrolling();
364 scrollLayer->setScrollableArea(scrollableArea, isForViewport(scrollableArea));
367 WebLayer* webLayer = toWebLayer(scrollableArea->layerForScrolling());
368 WebLayer* containerLayer = toWebLayer(scrollableArea->layerForContainer());
370 webLayer->setScrollClipLayer(containerLayer);
371 webLayer->setScrollPositionDouble(DoublePoint(scrollableArea->scrollPositionDouble() - scrollableArea->minimumScrollPosition()));
372 webLayer->setBounds(scrollableArea->contentsSize());
373 bool canScrollX = scrollableArea->userInputScrollable(HorizontalScrollbar);
374 bool canScrollY = scrollableArea->userInputScrollable(VerticalScrollbar);
375 webLayer->setUserScrollable(canScrollX, canScrollY);
377 if (WebScrollbarLayer* scrollbarLayer = getWebScrollbarLayer(scrollableArea, HorizontalScrollbar)) {
378 GraphicsLayer* horizontalScrollbarLayer = scrollableArea->layerForHorizontalScrollbar();
379 if (horizontalScrollbarLayer)
380 setupScrollbarLayer(horizontalScrollbarLayer, scrollbarLayer, webLayer, containerLayer);
382 if (WebScrollbarLayer* scrollbarLayer = getWebScrollbarLayer(scrollableArea, VerticalScrollbar)) {
383 GraphicsLayer* verticalScrollbarLayer = scrollableArea->layerForVerticalScrollbar();
384 if (verticalScrollbarLayer)
385 setupScrollbarLayer(verticalScrollbarLayer, scrollbarLayer, webLayer, containerLayer);
391 using GraphicsLayerHitTestRects = WTF::HashMap<const GraphicsLayer*, Vector<LayoutRect>>;
393 // In order to do a DFS cross-frame walk of the RenderLayer tree, we need to know which
394 // RenderLayers have child frames inside of them. This computes a mapping for the
395 // current frame which we can consult while walking the layers of that frame.
396 // Whenever we descend into a new frame, a new map will be created.
397 using LayerFrameMap = HashMap<const RenderLayer*, Vector<const LocalFrame*>>;
398 static void makeLayerChildFrameMap(const LocalFrame* currentFrame, LayerFrameMap* map)
401 const FrameTree& tree = currentFrame->tree();
402 for (const Frame* child = tree.firstChild(); child; child = child->tree().nextSibling()) {
403 if (!child->isLocalFrame())
405 const RenderObject* ownerRenderer = toLocalFrame(child)->ownerRenderer();
408 const RenderLayer* containingLayer = ownerRenderer->enclosingLayer();
409 LayerFrameMap::iterator iter = map->find(containingLayer);
410 if (iter == map->end())
411 map->add(containingLayer, Vector<const LocalFrame*>()).storedValue->value.append(toLocalFrame(child));
413 iter->value.append(toLocalFrame(child));
417 static void projectRectsToGraphicsLayerSpaceRecursive(
418 const RenderLayer* curLayer,
419 const LayerHitTestRects& layerRects,
420 GraphicsLayerHitTestRects& graphicsRects,
421 RenderGeometryMap& geometryMap,
422 HashSet<const RenderLayer*>& layersWithRects,
423 LayerFrameMap& layerChildFrameMap)
425 // Project any rects for the current layer
426 LayerHitTestRects::const_iterator layerIter = layerRects.find(curLayer);
427 if (layerIter != layerRects.end()) {
428 // Find the enclosing composited layer when it's in another document (for non-composited iframes).
429 const RenderLayer* compositedLayer = layerIter->key->enclosingLayerForPaintInvalidationCrossingFrameBoundaries();
430 ASSERT(compositedLayer);
432 // Find the appropriate GraphicsLayer for the composited RenderLayer.
433 GraphicsLayer* graphicsLayer = compositedLayer->graphicsLayerBackingForScrolling();
435 GraphicsLayerHitTestRects::iterator glIter = graphicsRects.find(graphicsLayer);
436 Vector<LayoutRect>* glRects;
437 if (glIter == graphicsRects.end())
438 glRects = &graphicsRects.add(graphicsLayer, Vector<LayoutRect>()).storedValue->value;
440 glRects = &glIter->value;
442 // Transform each rect to the co-ordinate space of the graphicsLayer.
443 for (size_t i = 0; i < layerIter->value.size(); ++i) {
444 LayoutRect rect = layerIter->value[i];
445 if (compositedLayer != curLayer) {
446 FloatQuad compositorQuad = geometryMap.mapToContainer(rect, compositedLayer->renderer());
447 rect = LayoutRect(compositorQuad.boundingBox());
448 // If the enclosing composited layer itself is scrolled, we have to undo the subtraction
449 // of its scroll offset since we want the offset relative to the scrolling content, not
450 // the element itself.
451 if (compositedLayer->renderer()->hasOverflowClip())
452 rect.move(compositedLayer->renderBox()->scrolledContentOffset());
454 RenderLayer::mapRectToPaintBackingCoordinates(compositedLayer->renderer(), rect);
455 glRects->append(rect);
459 // Walk child layers of interest
460 for (const RenderLayer* childLayer = curLayer->firstChild(); childLayer; childLayer = childLayer->nextSibling()) {
461 if (layersWithRects.contains(childLayer)) {
462 geometryMap.pushMappingsToAncestor(childLayer, curLayer);
463 projectRectsToGraphicsLayerSpaceRecursive(childLayer, layerRects, graphicsRects, geometryMap, layersWithRects, layerChildFrameMap);
464 geometryMap.popMappingsToAncestor(curLayer);
468 // If this layer has any frames of interest as a child of it, walk those (with an updated frame map).
469 LayerFrameMap::iterator mapIter = layerChildFrameMap.find(curLayer);
470 if (mapIter != layerChildFrameMap.end()) {
471 for (size_t i = 0; i < mapIter->value.size(); i++) {
472 const LocalFrame* childFrame = mapIter->value[i];
473 const RenderLayer* childLayer = childFrame->view()->renderView()->layer();
474 if (layersWithRects.contains(childLayer)) {
475 LayerFrameMap newLayerChildFrameMap;
476 makeLayerChildFrameMap(childFrame, &newLayerChildFrameMap);
477 geometryMap.pushMappingsToAncestor(childLayer, curLayer);
478 projectRectsToGraphicsLayerSpaceRecursive(childLayer, layerRects, graphicsRects, geometryMap, layersWithRects, newLayerChildFrameMap);
479 geometryMap.popMappingsToAncestor(curLayer);
485 static void projectRectsToGraphicsLayerSpace(LocalFrame* mainFrame, const LayerHitTestRects& layerRects, GraphicsLayerHitTestRects& graphicsRects)
487 TRACE_EVENT0("input", "ScrollingCoordinator::projectRectsToGraphicsLayerSpace");
488 bool touchHandlerInChildFrame = false;
490 // We have a set of rects per RenderLayer, we need to map them to their bounding boxes in their
491 // enclosing composited layer. To do this most efficiently we'll walk the RenderLayer tree using
492 // RenderGeometryMap. First record all the branches we should traverse in the tree (including
493 // all documents on the page).
494 HashSet<const RenderLayer*> layersWithRects;
495 for (const auto& layerRect : layerRects) {
496 const RenderLayer* layer = layerRect.key;
498 if (!layersWithRects.add(layer).isNewEntry)
501 if (layer->parent()) {
502 layer = layer->parent();
503 } else if (RenderObject* parentDocRenderer = layer->renderer()->frame()->ownerRenderer()) {
504 layer = parentDocRenderer->enclosingLayer();
505 touchHandlerInChildFrame = true;
510 // Now walk the layer projecting rects while maintaining a RenderGeometryMap
511 MapCoordinatesFlags flags = UseTransforms;
512 if (touchHandlerInChildFrame)
513 flags |= TraverseDocumentBoundaries;
514 RenderLayer* rootLayer = mainFrame->contentRenderer()->layer();
515 RenderGeometryMap geometryMap(flags);
516 geometryMap.pushMappingsToAncestor(rootLayer, 0);
517 LayerFrameMap layerChildFrameMap;
518 makeLayerChildFrameMap(mainFrame, &layerChildFrameMap);
519 projectRectsToGraphicsLayerSpaceRecursive(rootLayer, layerRects, graphicsRects, geometryMap, layersWithRects, layerChildFrameMap);
522 void ScrollingCoordinator::updateTouchEventTargetRectsIfNeeded()
524 TRACE_EVENT0("input", "ScrollingCoordinator::updateTouchEventTargetRectsIfNeeded");
526 if (!RuntimeEnabledFeatures::touchEnabled())
529 LayerHitTestRects touchEventTargetRects;
530 computeTouchEventTargetRects(touchEventTargetRects);
531 setTouchEventTargetRects(touchEventTargetRects);
534 void ScrollingCoordinator::reset()
536 for (const auto& scrollbar : m_horizontalScrollbars)
537 GraphicsLayer::unregisterContentsLayer(scrollbar.value->layer());
538 for (const auto& scrollbar : m_verticalScrollbars)
539 GraphicsLayer::unregisterContentsLayer(scrollbar.value->layer());
541 m_horizontalScrollbars.clear();
542 m_verticalScrollbars.clear();
543 m_layersWithTouchRects.clear();
544 m_wasFrameScrollable = false;
546 // This is retained for testing.
547 m_lastMainThreadScrollingReasons = 0;
548 setShouldUpdateScrollLayerPositionOnMainThread(m_lastMainThreadScrollingReasons);
551 // Note that in principle this could be called more often than computeTouchEventTargetRects, for
552 // example during a non-composited scroll (although that's not yet implemented - crbug.com/261307).
553 void ScrollingCoordinator::setTouchEventTargetRects(LayerHitTestRects& layerRects)
555 TRACE_EVENT0("input", "ScrollingCoordinator::setTouchEventTargetRects");
557 // Update the list of layers with touch hit rects.
558 HashSet<const RenderLayer*> oldLayersWithTouchRects;
559 m_layersWithTouchRects.swap(oldLayersWithTouchRects);
560 for (const auto& layerRect : layerRects) {
561 if (!layerRect.value.isEmpty()) {
562 const RenderLayer* compositedLayer = layerRect.key->enclosingLayerForPaintInvalidationCrossingFrameBoundaries();
563 ASSERT(compositedLayer);
564 m_layersWithTouchRects.add(compositedLayer);
568 // Ensure we have an entry for each composited layer that previously had rects (so that old
569 // ones will get cleared out). Note that ideally we'd track this on GraphicsLayer instead of
570 // RenderLayer, but we have no good hook into the lifetime of a GraphicsLayer.
571 for (const RenderLayer* layer : oldLayersWithTouchRects) {
572 if (!layerRects.contains(layer))
573 layerRects.add(layer, Vector<LayoutRect>());
576 GraphicsLayerHitTestRects graphicsLayerRects;
577 projectRectsToGraphicsLayerSpace(m_page->deprecatedLocalMainFrame(), layerRects, graphicsLayerRects);
579 for (const auto& layerRect : graphicsLayerRects) {
580 const GraphicsLayer* graphicsLayer = layerRect.key;
581 WebVector<WebRect> webRects(layerRect.value.size());
582 for (size_t i = 0; i < layerRect.value.size(); ++i)
583 webRects[i] = enclosingIntRect(layerRect.value[i]);
584 graphicsLayer->platformLayer()->setTouchEventHandlerRegion(webRects);
588 void ScrollingCoordinator::touchEventTargetRectsDidChange()
590 if (!RuntimeEnabledFeatures::touchEnabled())
593 // Wait until after layout to update.
594 if (!m_page->deprecatedLocalMainFrame()->view() || m_page->deprecatedLocalMainFrame()->view()->needsLayout())
597 // FIXME: scheduleAnimation() is just a method of forcing the compositor to realize that it
598 // needs to commit here. We should expose a cleaner API for this.
599 RenderView* renderView = m_page->deprecatedLocalMainFrame()->contentRenderer();
600 if (renderView && renderView->compositor() && renderView->compositor()->staleInCompositingMode())
601 m_page->deprecatedLocalMainFrame()->view()->scheduleAnimation();
603 m_touchEventTargetRectsAreDirty = true;
606 void ScrollingCoordinator::updateScrollParentForGraphicsLayer(GraphicsLayer* child, RenderLayer* parent)
608 WebLayer* scrollParentWebLayer = nullptr;
609 if (parent && parent->hasCompositedLayerMapping())
610 scrollParentWebLayer = toWebLayer(parent->compositedLayerMapping()->scrollingContentsLayer());
612 child->setScrollParent(scrollParentWebLayer);
615 void ScrollingCoordinator::updateClipParentForGraphicsLayer(GraphicsLayer* child, RenderLayer* parent)
617 WebLayer* clipParentWebLayer = nullptr;
618 if (parent && parent->hasCompositedLayerMapping())
619 clipParentWebLayer = toWebLayer(parent->compositedLayerMapping()->parentForSublayers());
621 child->setClipParent(clipParentWebLayer);
624 void ScrollingCoordinator::willDestroyRenderLayer(RenderLayer* layer)
626 m_layersWithTouchRects.remove(layer);
629 void ScrollingCoordinator::updateHaveWheelEventHandlers()
631 ASSERT(isMainThread());
633 if (!m_page->mainFrame()->isLocalFrame() || !m_page->deprecatedLocalMainFrame()->view())
636 if (WebLayer* scrollLayer = toWebLayer(m_page->deprecatedLocalMainFrame()->view()->layerForScrolling())) {
637 bool haveHandlers = m_page->frameHost().eventHandlerRegistry().hasEventHandlers(EventHandlerRegistry::WheelEvent);
638 scrollLayer->setHaveWheelEventHandlers(haveHandlers);
642 void ScrollingCoordinator::updateHaveScrollEventHandlers()
644 ASSERT(isMainThread());
646 if (!m_page->mainFrame()->isLocalFrame() || !m_page->deprecatedLocalMainFrame()->view())
649 // Currently the compositor only cares whether there are scroll handlers anywhere on the page
650 // instead on a per-layer basis. We therefore only update this information for the root
652 if (WebLayer* scrollLayer = toWebLayer(m_page->deprecatedLocalMainFrame()->view()->layerForScrolling())) {
653 bool haveHandlers = m_page->frameHost().eventHandlerRegistry().hasEventHandlers(EventHandlerRegistry::ScrollEvent);
654 scrollLayer->setHaveScrollEventHandlers(haveHandlers);
658 void ScrollingCoordinator::setShouldUpdateScrollLayerPositionOnMainThread(MainThreadScrollingReasons reasons)
660 if (!m_page->mainFrame()->isLocalFrame())
662 if (WebLayer* scrollLayer = toWebLayer(m_page->deprecatedLocalMainFrame()->view()->layerForScrolling())) {
663 m_lastMainThreadScrollingReasons = reasons;
664 scrollLayer->setShouldScrollOnMainThread(reasons);
668 void ScrollingCoordinator::willBeDestroyed()
672 for (const auto& scrollbar : m_horizontalScrollbars)
673 GraphicsLayer::unregisterContentsLayer(scrollbar.value->layer());
674 for (const auto& scrollbar : m_verticalScrollbars)
675 GraphicsLayer::unregisterContentsLayer(scrollbar.value->layer());
678 bool ScrollingCoordinator::coordinatesScrollingForFrameView(FrameView* frameView) const
680 ASSERT(isMainThread());
683 // We currently only handle the main frame.
684 if (&frameView->frame() != m_page->mainFrame())
687 if (!m_page->mainFrame()->isLocalFrame())
690 // We currently only support composited mode.
691 RenderView* renderView = m_page->deprecatedLocalMainFrame()->contentRenderer();
694 return renderView->usesCompositing();
697 Region ScrollingCoordinator::computeShouldHandleScrollGestureOnMainThreadRegion(const LocalFrame* frame, const IntPoint& frameLocation) const
699 Region shouldHandleScrollGestureOnMainThreadRegion;
700 FrameView* frameView = frame->view();
702 return shouldHandleScrollGestureOnMainThreadRegion;
704 IntPoint offset = frameLocation;
705 offset.moveBy(frameView->frameRect().location());
707 if (const FrameView::ScrollableAreaSet* scrollableAreas = frameView->scrollableAreas()) {
708 for (const ScrollableArea* scrollableArea : *scrollableAreas) {
709 // Composited scrollable areas can be scrolled off the main thread.
710 if (scrollableArea->usesCompositedScrolling())
712 IntRect box = scrollableArea->scrollableAreaBoundingBox();
714 shouldHandleScrollGestureOnMainThreadRegion.unite(box);
718 // We use GestureScrollBegin/Update/End for moving the resizer handle. So we mark these
719 // small resizer areas as non-fast-scrollable to allow the scroll gestures to be passed to
720 // main thread if they are targeting the resizer area. (Resizing is done in EventHandler.cpp
722 if (const FrameView::ResizerAreaSet* resizerAreas = frameView->resizerAreas()) {
723 for (const RenderBox* box : *resizerAreas) {
724 IntRect bounds = box->absoluteBoundingBoxRect();
725 IntRect corner = box->layer()->scrollableArea()->touchResizerCornerRect(bounds);
726 corner.moveBy(offset);
727 shouldHandleScrollGestureOnMainThreadRegion.unite(corner);
731 if (const FrameView::ChildrenWidgetSet* children = frameView->children()) {
732 for (const RefPtrWillBeMember<Widget>& child : *children) {
733 if (!(*child).isPluginView())
736 PluginView* pluginView = toPluginView(child.get());
737 if (pluginView->wantsWheelEvents())
738 shouldHandleScrollGestureOnMainThreadRegion.unite(pluginView->frameRect());
742 const FrameTree& tree = frame->tree();
743 for (Frame* subFrame = tree.firstChild(); subFrame; subFrame = subFrame->tree().nextSibling()) {
744 if (subFrame->isLocalFrame())
745 shouldHandleScrollGestureOnMainThreadRegion.unite(computeShouldHandleScrollGestureOnMainThreadRegion(toLocalFrame(subFrame), offset));
748 return shouldHandleScrollGestureOnMainThreadRegion;
751 static void accumulateDocumentTouchEventTargetRects(LayerHitTestRects& rects, const Document* document)
754 const EventTargetSet* targets = document->frameHost()->eventHandlerRegistry().eventHandlerTargets(EventHandlerRegistry::TouchEvent);
758 // If there's a handler on the window, document, html or body element (fairly common in practice),
759 // then we can quickly mark the entire document and skip looking at any other handlers.
760 // Note that technically a handler on the body doesn't cover the whole document, but it's
761 // reasonable to be conservative and report the whole document anyway.
763 // Fullscreen HTML5 video when OverlayFullscreenVideo is enabled is implemented by replacing the
764 // root cc::layer with the video layer so doing this optimization causes the compositor to think
765 // that there are no handlers, therefore skip it.
766 if (!document->renderView()->compositor()->inOverlayFullscreenVideo()) {
767 for (const auto& eventTarget : *targets) {
768 EventTarget* target = eventTarget.key;
769 Node* node = target->toNode();
770 if (target->toDOMWindow() || node == document || node == document->documentElement() || node == document->body()) {
771 if (RenderView* rendererView = document->renderView()) {
772 rendererView->computeLayerHitTestRects(rects);
779 for (const auto& eventTarget : *targets) {
780 EventTarget* target = eventTarget.key;
781 Node* node = target->toNode();
782 if (!node || !node->inDocument())
785 // If the document belongs to an invisible subframe it does not have a composited layer
786 // and should be skipped.
787 if (node->document().isInInvisibleSubframe())
790 if (node->isDocumentNode() && node != document) {
791 accumulateDocumentTouchEventTargetRects(rects, toDocument(node));
792 } else if (RenderObject* renderer = node->renderer()) {
793 // If the set also contains one of our ancestor nodes then processing
794 // this node would be redundant.
795 bool hasTouchEventTargetAncestor = false;
796 for (Node* ancestor = node->parentNode(); ancestor && !hasTouchEventTargetAncestor; ancestor = ancestor->parentNode()) {
797 if (targets->contains(ancestor))
798 hasTouchEventTargetAncestor = true;
800 if (!hasTouchEventTargetAncestor) {
801 // Walk up the tree to the outermost non-composited scrollable layer.
802 RenderLayer* enclosingNonCompositedScrollLayer = nullptr;
803 for (RenderLayer* parent = renderer->enclosingLayer(); parent && parent->compositingState() == NotComposited; parent = parent->parent()) {
804 if (parent->scrollsOverflow())
805 enclosingNonCompositedScrollLayer = parent;
808 // Report the whole non-composited scroll layer as a touch hit rect because any
809 // rects inside of it may move around relative to their enclosing composited layer
810 // without causing the rects to be recomputed. Non-composited scrolling occurs on
811 // the main thread, so we're not getting much benefit from compositor touch hit
812 // testing in this case anyway.
813 if (enclosingNonCompositedScrollLayer)
814 enclosingNonCompositedScrollLayer->computeSelfHitTestRects(rects);
816 renderer->computeLayerHitTestRects(rects);
822 void ScrollingCoordinator::computeTouchEventTargetRects(LayerHitTestRects& rects)
824 TRACE_EVENT0("input", "ScrollingCoordinator::computeTouchEventTargetRects");
825 ASSERT(RuntimeEnabledFeatures::touchEnabled());
827 Document* document = m_page->deprecatedLocalMainFrame()->document();
828 if (!document || !document->view())
831 accumulateDocumentTouchEventTargetRects(rects, document);
834 void ScrollingCoordinator::frameViewHasSlowRepaintObjectsDidChange(FrameView* frameView)
836 ASSERT(isMainThread());
839 if (!coordinatesScrollingForFrameView(frameView))
842 m_shouldScrollOnMainThreadDirty = true;
845 void ScrollingCoordinator::frameViewFixedObjectsDidChange(FrameView* frameView)
847 ASSERT(isMainThread());
850 if (!coordinatesScrollingForFrameView(frameView))
853 m_shouldScrollOnMainThreadDirty = true;
856 bool ScrollingCoordinator::isForMainFrame(ScrollableArea* scrollableArea) const
858 if (!m_page->mainFrame()->isLocalFrame())
861 return scrollableArea == m_page->deprecatedLocalMainFrame()->view();
864 bool ScrollingCoordinator::isForViewport(ScrollableArea* scrollableArea) const
866 return isForMainFrame(scrollableArea)
867 || scrollableArea == &m_page->frameHost().pinchViewport();
870 void ScrollingCoordinator::frameViewRootLayerDidChange(FrameView* frameView)
872 ASSERT(isMainThread());
875 if (!coordinatesScrollingForFrameView(frameView))
878 notifyLayoutUpdated();
879 updateHaveWheelEventHandlers();
880 updateHaveScrollEventHandlers();
884 void ScrollingCoordinator::handleWheelEventPhase(PlatformWheelEventPhase phase)
886 ASSERT(isMainThread());
891 FrameView* frameView = m_page->deprecatedLocalMainFrame()->view();
895 frameView->scrollAnimator()->handleWheelEventPhase(phase);
899 bool ScrollingCoordinator::hasVisibleSlowRepaintViewportConstrainedObjects(FrameView* frameView) const
901 const FrameView::ViewportConstrainedObjectSet* viewportConstrainedObjects = frameView->viewportConstrainedObjects();
902 if (!viewportConstrainedObjects)
905 for (const RenderObject* renderer : *viewportConstrainedObjects) {
906 ASSERT(renderer->isBoxModelObject() && renderer->hasLayer());
907 ASSERT(renderer->style()->position() == FixedPosition);
908 RenderLayer* layer = toRenderBoxModelObject(renderer)->layer();
910 // Whether the RenderLayer scrolls with the viewport is a tree-depenent
911 // property and our viewportConstrainedObjects collection is maintained
912 // with only RenderObject-level information.
913 if (!layer->scrollsWithViewport())
916 // If the whole subtree is invisible, there's no reason to scroll on
917 // the main thread because we don't need to generate invalidations
918 // for invisible content.
919 if (layer->subtreeIsInvisible())
922 // We're only smart enough to scroll viewport-constrainted objects
923 // in the compositor if they have their own backing or they paint
924 // into a grouped back (which necessarily all have the same viewport
926 CompositingState compositingState = layer->compositingState();
927 if (compositingState != PaintsIntoOwnBacking && compositingState != PaintsIntoGroupedBacking)
933 MainThreadScrollingReasons ScrollingCoordinator::mainThreadScrollingReasons() const
935 MainThreadScrollingReasons reasons = static_cast<MainThreadScrollingReasons>(0);
937 // FIXME: make threaded scrolling work correctly with rootLayerScrolls.
938 if (!m_page->settings().threadedScrollingEnabled() || m_page->settings().rootLayerScrolls())
939 reasons |= ThreadedScrollingDisabled;
941 if (!m_page->mainFrame()->isLocalFrame())
943 FrameView* frameView = m_page->deprecatedLocalMainFrame()->view();
947 if (frameView->hasSlowRepaintObjects())
948 reasons |= HasSlowRepaintObjects;
949 FrameView::ScrollingReasons scrollingReasons = frameView->scrollingReasons();
950 const bool mayBeScrolledByInput = (scrollingReasons == FrameView::Scrollable);
951 const bool mayBeScrolledByScript = mayBeScrolledByInput || (scrollingReasons ==
952 FrameView::NotScrollableExplicitlyDisabled);
954 // TODO(awoloszyn) Currently crbug.com/304810 will let certain
955 // overflow:hidden elements scroll on the compositor thread, so we should
956 // not let this move there path as an optimization, when we have slow-repaint
958 if (mayBeScrolledByScript && hasVisibleSlowRepaintViewportConstrainedObjects(frameView)) {
959 reasons |= HasNonLayerViewportConstrainedObjects;
965 String ScrollingCoordinator::mainThreadScrollingReasonsAsText(MainThreadScrollingReasons reasons)
967 StringBuilder stringBuilder;
969 if (reasons & ScrollingCoordinator::HasSlowRepaintObjects)
970 stringBuilder.appendLiteral("Has slow repaint objects, ");
971 if (reasons & ScrollingCoordinator::HasViewportConstrainedObjectsWithoutSupportingFixedLayers)
972 stringBuilder.appendLiteral("Has viewport constrained objects without supporting fixed layers, ");
973 if (reasons & ScrollingCoordinator::HasNonLayerViewportConstrainedObjects)
974 stringBuilder.appendLiteral("Has non-layer viewport-constrained objects, ");
975 if (reasons & ScrollingCoordinator::ThreadedScrollingDisabled)
976 stringBuilder.appendLiteral("Threaded scrolling is disabled, ");
978 if (stringBuilder.length())
979 stringBuilder.resize(stringBuilder.length() - 2);
980 return stringBuilder.toString();
983 String ScrollingCoordinator::mainThreadScrollingReasonsAsText() const
985 ASSERT(m_page->deprecatedLocalMainFrame()->document()->lifecycle().state() >= DocumentLifecycle::CompositingClean);
986 return mainThreadScrollingReasonsAsText(m_lastMainThreadScrollingReasons);
989 bool ScrollingCoordinator::frameViewIsDirty() const
991 FrameView* frameView = m_page->mainFrame()->isLocalFrame() ? m_page->deprecatedLocalMainFrame()->view() : nullptr;
992 bool frameIsScrollable = frameView && frameView->isScrollable();
993 if (frameIsScrollable != m_wasFrameScrollable)
996 if (WebLayer* scrollLayer = frameView ? toWebLayer(frameView->layerForScrolling()) : nullptr)
997 return blink::WebSize(frameView->contentsSize()) != scrollLayer->bounds();
1001 } // namespace blink