Chromium Code Reviews| Index: Source/core/page/scrolling/ScrollingCoordinator.cpp |
| diff --git a/Source/core/page/scrolling/ScrollingCoordinator.cpp b/Source/core/page/scrolling/ScrollingCoordinator.cpp |
| index 87829b18a3c5f9c46d57013babf4398a2386cbf7..a4c14bdbd3ea5f83726ff203ede64145fad566a3 100644 |
| --- a/Source/core/page/scrolling/ScrollingCoordinator.cpp |
| +++ b/Source/core/page/scrolling/ScrollingCoordinator.cpp |
| @@ -27,18 +27,23 @@ |
| #include "core/page/scrolling/ScrollingCoordinator.h" |
| +#include "RuntimeEnabledFeatures.h" |
| #include "core/dom/Document.h" |
| +#include "core/dom/Node.h" |
| +#include "core/html/HTMLElement.h" |
| #include "core/page/Frame.h" |
| #include "core/page/FrameView.h" |
| #include "core/page/Page.h" |
| #include "core/platform/PlatformWheelEvent.h" |
| #include "core/platform/ScrollAnimator.h" |
| #include "core/platform/ScrollbarThemeComposite.h" |
| +#include "core/platform/chromium/TraceEvent.h" |
| #include "core/platform/chromium/support/WebScrollbarImpl.h" |
| #include "core/platform/chromium/support/WebScrollbarThemeGeometryNative.h" |
| #include "core/platform/graphics/GraphicsLayer.h" |
| #include "core/platform/graphics/IntRect.h" |
| #include "core/platform/graphics/Region.h" |
| +#include "core/platform/graphics/transforms/TransformState.h" |
| #include "core/plugins/PluginView.h" |
| #include "core/rendering/RenderLayerBacking.h" |
| #include "core/rendering/RenderLayerCompositor.h" |
| @@ -105,6 +110,10 @@ void ScrollingCoordinator::setShouldHandleScrollGestureOnMainThreadRegion(const |
| void ScrollingCoordinator::frameViewLayoutUpdated(FrameView* frameView) |
| { |
| ASSERT(m_page); |
| + if (!RuntimeEnabledFeatures::touchEnabled()) |
| + return; |
| + |
| + TRACE_EVENT0("input", "ScrollingCoordinator::frameViewLayoutUpdated"); |
| // Compute the region of the page that we can't handle scroll gestures on impl thread: |
| // This currently includes: |
| @@ -116,8 +125,8 @@ void ScrollingCoordinator::frameViewLayoutUpdated(FrameView* frameView) |
| // 3. Plugin areas. |
| Region shouldHandleScrollGestureOnMainThreadRegion = computeShouldHandleScrollGestureOnMainThreadRegion(m_page->mainFrame(), IntPoint()); |
| setShouldHandleScrollGestureOnMainThreadRegion(shouldHandleScrollGestureOnMainThreadRegion); |
| - Vector<IntRect> touchEventTargetRects; |
| - computeAbsoluteTouchEventTargetRects(m_page->mainFrame()->document(), touchEventTargetRects); |
| + LayerHitTestRects touchEventTargetRects; |
| + computeTouchEventTargetRects(touchEventTargetRects); |
| setTouchEventTargetRects(touchEventTargetRects); |
| if (WebLayer* scrollLayer = scrollingWebLayerForScrollableArea(frameView)) |
| scrollLayer->setBounds(frameView->contentsSize()); |
| @@ -296,28 +305,88 @@ bool ScrollingCoordinator::scrollableAreaScrollLayerDidChange(ScrollableArea* sc |
| return !!webLayer; |
| } |
| -void ScrollingCoordinator::setTouchEventTargetRects(const Vector<IntRect>& absoluteHitTestRects) |
| -{ |
| - if (WebLayer* scrollLayer = scrollingWebLayerForScrollableArea(m_page->mainFrame()->view())) { |
| - WebVector<WebRect> webRects(absoluteHitTestRects.size()); |
| - for (size_t i = 0; i < absoluteHitTestRects.size(); ++i) |
| - webRects[i] = absoluteHitTestRects[i]; |
| - scrollLayer->setTouchEventHandlerRegion(webRects); |
| +static void convertLayerRectsToEnclosingCompositedLayer(const LayerHitTestRects& layerRects, LayerHitTestRects& compositorRects) |
| +{ |
| + // We have a set of rects per RenderLayer, we need to map them to their bounding boxes in their |
| + // enclosing composited layer. |
| + for (LayerHitTestRects::const_iterator layerIter = layerRects.begin(); layerIter != layerRects.end(); ++layerIter) { |
| + // Find the enclosing composited layer when it's in another document (for non-composited iframes). |
| + RenderLayer* compositedLayer = 0; |
| + for (const RenderLayer* layer = layerIter->key; !compositedLayer;) { |
| + compositedLayer = layer->enclosingCompositingLayerForRepaint(); |
| + if (!compositedLayer) { |
| + RenderObject* owner = layer->renderer()->frame()->ownerRenderer(); |
| + if (!owner) |
| + break; |
| + layer = owner->enclosingLayer(); |
| + } |
| + } |
| + if (!compositedLayer) |
| + continue; |
|
leviw_travelin_and_unemployed
2013/07/18 18:08:39
Does this happen? When?
|
| + |
| + LayerHitTestRects::iterator compIter = compositorRects.find(compositedLayer); |
| + if (compIter == compositorRects.end()) |
| + compIter = compositorRects.add(compositedLayer, Vector<IntRect>()).iterator; |
| + |
| + // Transform each rect to the co-ordinate space of it's enclosing composited layer. |
| + // Ideally we'd compute a transformation matrix once and re-use it for each rect, but |
| + // there doesn't appear to be any easy way to do it (mapLocalToContainer will flatten |
| + // the TransformState, so we can't use setQuad/mappedQuad over and over again). Perhaps |
| + // RenderGeometryMap? |
| + for (size_t i = 0; i < layerIter->value.size(); ++i) { |
| + FloatQuad localQuad(layerIter->value[i]); |
| + TransformState transformState(TransformState::ApplyTransformDirection, localQuad); |
| + MapCoordinatesFlags flags = ApplyContainerFlip | UseTransforms | TraverseDocumentBoundaries; |
| + layerIter->key->renderer()->mapLocalToContainer(compositedLayer->renderer(), transformState, flags); |
| + transformState.flatten(); |
| + IntRect compositorRect = enclosingIntRect(transformState.lastPlanarQuad().boundingBox()); |
| + compIter->value.append(compositorRect); |
| + } |
| + } |
| +} |
| + |
| +void ScrollingCoordinator::setTouchEventTargetRects(const LayerHitTestRects& layerRects) |
| +{ |
| + TRACE_EVENT0("input", "ScrollingCoordinator::setTouchEventTargetRects"); |
| + |
| + LayerHitTestRects compositorRects; |
| + convertLayerRectsToEnclosingCompositedLayer(layerRects, compositorRects); |
| + |
| + // Inform any observers (i.e. for testing) of these new rects. |
| + HashSet<TouchEventTargetRectsObserver*>::iterator stop = m_touchEventTargetRectsObservers.end(); |
| + for (HashSet<TouchEventTargetRectsObserver*>::iterator it = m_touchEventTargetRectsObservers.begin(); it != stop; ++it) |
| + (*it)->touchEventTargetRectsChanged(compositorRects); |
| + |
| + // Note that ideally we'd clear the touch event handler region on all layers first, |
| + // in case there are others that no longer have any handlers. But it's unlikely to |
| + // matter much in practice (just makes us more conservative). |
| + for (LayerHitTestRects::const_iterator iter = compositorRects.begin(); iter != compositorRects.end(); ++iter) { |
| + WebVector<WebRect> webRects(iter->value.size()); |
| + for (size_t i = 0; i < iter->value.size(); ++i) |
| + webRects[i] = iter->value[i]; |
| + RenderLayerBacking* backing = iter->key->backing(); |
| + // If the layer is using composited scrolling, then it's the contents that these |
| + // rects apply to. |
| + GraphicsLayer* graphicsLayer = backing->scrollingContentsLayer(); |
| + if (!graphicsLayer) |
| + graphicsLayer = backing->graphicsLayer(); |
| + graphicsLayer->platformLayer()->setTouchEventHandlerRegion(webRects); |
| } |
| } |
| void ScrollingCoordinator::touchEventTargetRectsDidChange(const Document*) |
| { |
| - // The rects are always evaluated and used in the main frame coordinates. |
| - FrameView* frameView = m_page->mainFrame()->view(); |
| - Document* document = m_page->mainFrame()->document(); |
| + if (!RuntimeEnabledFeatures::touchEnabled()) |
| + return; |
| // Wait until after layout to update. |
| - if (frameView->needsLayout() || !document) |
| + if (m_page->mainFrame()->view()->needsLayout()) |
| return; |
| - Vector<IntRect> touchEventTargetRects; |
| - computeAbsoluteTouchEventTargetRects(document, touchEventTargetRects); |
| + TRACE_EVENT0("input", "ScrollingCoordinator::touchEventTargetRectsDidChange"); |
| + |
| + LayerHitTestRects touchEventTargetRects; |
| + computeTouchEventTargetRects(touchEventTargetRects); |
| setTouchEventTargetRects(touchEventTargetRects); |
| } |
| @@ -363,6 +432,7 @@ bool ScrollingCoordinator::coordinatesScrollingForFrameView(FrameView* frameView |
| Region ScrollingCoordinator::computeShouldHandleScrollGestureOnMainThreadRegion(const Frame* frame, const IntPoint& frameLocation) const |
| { |
| + TRACE_EVENT0("input", "ScrollingCoordinator::computeShouldHandleScrollGestureOnMainThreadRegion"); |
| Region shouldHandleScrollGestureOnMainThreadRegion; |
| FrameView* frameView = frame->view(); |
| if (!frameView) |
| @@ -415,75 +485,63 @@ Region ScrollingCoordinator::computeShouldHandleScrollGestureOnMainThreadRegion( |
| return shouldHandleScrollGestureOnMainThreadRegion; |
| } |
| -static void accumulateRendererTouchEventTargetRects(Vector<IntRect>& rects, const RenderObject* renderer, const IntRect& parentRect = IntRect()) |
| +void ScrollingCoordinator::addTouchEventTargetRectsObserver(TouchEventTargetRectsObserver* observer) |
| { |
| - IntRect adjustedParentRect = parentRect; |
| - if (parentRect.isEmpty() || renderer->isFloating() || renderer->isPositioned() || renderer->hasTransform()) { |
| - // FIXME: This method is O(N^2) as it walks the tree to the root for every renderer. RenderGeometryMap would fix this. |
| - IntRect r = enclosingIntRect(renderer->clippedOverflowRectForRepaint(0)); |
| - if (!r.isEmpty()) { |
| - // Convert to the top-level view's coordinates. |
| - ASSERT(renderer->document()->view()); |
| - r = renderer->document()->view()->convertToRootView(r); |
| - |
| - if (!parentRect.contains(r)) { |
| - rects.append(r); |
| - adjustedParentRect = r; |
| - } |
| - } |
| - } |
| + m_touchEventTargetRectsObservers.add(observer); |
| +} |
| - for (RenderObject* child = renderer->firstChild(); child; child = child->nextSibling()) |
| - accumulateRendererTouchEventTargetRects(rects, child, adjustedParentRect); |
| +void ScrollingCoordinator::removeTouchEventTargetRectsObserver(TouchEventTargetRectsObserver* observer) |
| +{ |
| + m_touchEventTargetRectsObservers.remove(observer); |
| } |
| -static void accumulateDocumentEventTargetRects(Vector<IntRect>& rects, const Document* document) |
| +static void accumulateDocumentTouchEventTargetRects(LayerHitTestRects& rects, const Document* document) |
| { |
| ASSERT(document); |
| if (!document->touchEventTargets()) |
| return; |
| const TouchEventTargetSet* targets = document->touchEventTargets(); |
| - for (TouchEventTargetSet::const_iterator iter = targets->begin(); iter != targets->end(); ++iter) { |
| - const Node* touchTarget = iter->key; |
| - if (!touchTarget->inDocument()) |
| - continue; |
| - if (touchTarget == document) { |
| - if (RenderView* view = document->renderView()) { |
| - IntRect r; |
| - if (touchTarget == document->topDocument()) |
| - r = view->documentRect(); |
| - else |
| - r = enclosingIntRect(view->clippedOverflowRectForRepaint(0)); |
| - |
| - if (!r.isEmpty()) { |
| - ASSERT(view->document()->view()); |
| - r = view->document()->view()->convertToRootView(r); |
| - rects.append(r); |
| - } |
| + // If there's a handler on the document, html or body element (fairly common in practice), |
| + // then we can quickly mark the entire document and skip looking at any other handlers. |
| + // Note that technically a handler on the body doesn't cover the whole document, but it's |
| + // reasonable to be conservative and report the whole document anyway. |
| + for (TouchEventTargetSet::const_iterator iter = targets->begin(); iter != targets->end(); ++iter) { |
| + Node* target = iter->key; |
| + if (target == document || target == document->documentElement() || target == document->body()) { |
| + if (RenderObject* renderer = document->renderer()) { |
| + renderer->computeLayerHitTestRects(rects); |
| } |
| return; |
| } |
| + } |
| - if (touchTarget->isDocumentNode() && touchTarget != document) { |
| - accumulateDocumentEventTargetRects(rects, toDocument(touchTarget)); |
| + // Examine every target in detail. |
|
leviw_travelin_and_unemployed
2013/07/18 18:08:39
This comment doesn't really help.
Rick Byers
2013/07/18 19:58:52
Removed
|
| + for (TouchEventTargetSet::const_iterator iter = targets->begin(); iter != targets->end(); ++iter) { |
| + const Node* target = iter->key; |
| + if (!target->inDocument()) |
| continue; |
| - } |
| - if (RenderObject* renderer = touchTarget->renderer()) |
| - accumulateRendererTouchEventTargetRects(rects, renderer); |
| + if (target->isDocumentNode()) { |
| + ASSERT(target != document); |
| + accumulateDocumentTouchEventTargetRects(rects, toDocument(target)); |
| + } else if (RenderObject* renderer = target->renderer()) { |
| + renderer->computeLayerHitTestRects(rects); |
| + } |
| } |
| + |
| } |
| -void ScrollingCoordinator::computeAbsoluteTouchEventTargetRects(const Document* document, Vector<IntRect>& rects) |
| +void ScrollingCoordinator::computeTouchEventTargetRects(LayerHitTestRects& rects) |
| { |
| - ASSERT(document); |
| - if (!document->view()) |
| + TRACE_EVENT0("input", "ScrollingCoordinator::computeTouchEventTargetRects"); |
| + |
| + Document* document = m_page->mainFrame()->document(); |
| + if (!document || !document->view()) |
| return; |
| - // FIXME: These rects won't be properly updated if the renderers are in a sub-tree that scrolls. |
| - accumulateDocumentEventTargetRects(rects, document); |
| + accumulateDocumentTouchEventTargetRects(rects, document); |
| } |
| unsigned ScrollingCoordinator::computeCurrentWheelEventHandlerCount() |