Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1159)

Unified Diff: Source/core/page/scrolling/ScrollingCoordinator.cpp

Issue 17471008: Rework compositor touch hit testing (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: CR feedback - accumulate LayoutRects instead of IntRects, disable when page isn't composited. Also… Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « Source/core/page/scrolling/ScrollingCoordinator.h ('k') | Source/core/rendering/RenderBlock.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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..323298d2e6899a965518185e5ff3e986795373e5 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"
@@ -91,6 +96,12 @@ ScrollingCoordinator::~ScrollingCoordinator()
}
+bool ScrollingCoordinator::touchHitTestingEnabled() const
+{
+ RenderView* contentRenderer = m_page->mainFrame()->contentRenderer();
+ return RuntimeEnabledFeatures::touchEnabled() && contentRenderer && contentRenderer->usesCompositing();
+}
+
void ScrollingCoordinator::setShouldHandleScrollGestureOnMainThreadRegion(const Region& region)
{
if (WebLayer* scrollLayer = scrollingWebLayerForScrollableArea(m_page->mainFrame()->view())) {
@@ -104,7 +115,10 @@ void ScrollingCoordinator::setShouldHandleScrollGestureOnMainThreadRegion(const
void ScrollingCoordinator::frameViewLayoutUpdated(FrameView* frameView)
{
- ASSERT(m_page);
+ if (!touchHitTestingEnabled())
+ 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 +130,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 +310,92 @@ 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) {
+ // Since this machinery is used only when accelerated compositing is enabled, we expect
+ // that every layer should have an enclosing composited layer.
+ ASSERT_NOT_REACHED();
+ continue;
+ }
+
+ LayerHitTestRects::iterator compIter = compositorRects.find(compositedLayer);
+ if (compIter == compositorRects.end())
+ compIter = compositorRects.add(compositedLayer, Vector<LayoutRect>()).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();
+ LayoutRect compositorRect = LayoutRect(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] = enclosingIntRect(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 (!touchHitTestingEnabled())
+ 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 +441,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 +494,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));
+ 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");
+ ASSERT(touchHitTestingEnabled());
+
+ 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()
« no previous file with comments | « Source/core/page/scrolling/ScrollingCoordinator.h ('k') | Source/core/rendering/RenderBlock.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698