| 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()
|
|
|