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 "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"
52#if OS(MACOSX)
53#include "platform/mac/ScrollAnimatorMac.h"
54#endif
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"
64
65using blink::WebLayer;
66using blink::WebLayerPositionConstraint;
67using blink::WebRect;
68using blink::WebScrollbarLayer;
69using blink::WebVector;
70
71namespace {
72
73WebLayer* toWebLayer(blink::GraphicsLayer* layer)
74{
75    return layer ? layer->platformLayer() : 0;
76}
77
78} // namespace
79
80namespace blink {
81
82PassOwnPtr<ScrollingCoordinator> ScrollingCoordinator::create(Page* page)
83{
84    return adoptPtr(new ScrollingCoordinator(page));
85}
86
87ScrollingCoordinator::ScrollingCoordinator(Page* page)
88    : m_page(page)
89    , m_scrollGestureRegionIsDirty(false)
90    , m_touchEventTargetRectsAreDirty(false)
91    , m_shouldScrollOnMainThreadDirty(false)
92    , m_wasFrameScrollable(false)
93    , m_lastMainThreadScrollingReasons(0)
94{
95}
96
97ScrollingCoordinator::~ScrollingCoordinator()
98{
99}
100
101void ScrollingCoordinator::setShouldHandleScrollGestureOnMainThreadRegion(const Region& region)
102{
103    if (!m_page->mainFrame()->isLocalFrame())
104        return;
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);
111    }
112}
113
114void ScrollingCoordinator::notifyLayoutUpdated()
115{
116    m_scrollGestureRegionIsDirty = true;
117    m_touchEventTargetRectsAreDirty = true;
118    m_shouldScrollOnMainThreadDirty = true;
119}
120
121void ScrollingCoordinator::updateAfterCompositingChangeIfNeeded()
122{
123    if (!m_page->mainFrame()->isLocalFrame())
124        return;
125
126    if (!shouldUpdateAfterCompositingChange())
127        return;
128
129    TRACE_EVENT0("input", "ScrollingCoordinator::updateAfterCompositingChangeIfNeeded");
130
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.
139        // 3. Plugin areas.
140        Region shouldHandleScrollGestureOnMainThreadRegion = computeShouldHandleScrollGestureOnMainThreadRegion(m_page->deprecatedLocalMainFrame(), IntPoint());
141        setShouldHandleScrollGestureOnMainThreadRegion(shouldHandleScrollGestureOnMainThreadRegion);
142        m_scrollGestureRegionIsDirty = false;
143    }
144
145    if (m_touchEventTargetRectsAreDirty) {
146        updateTouchEventTargetRectsIfNeeded();
147        m_touchEventTargetRectsAreDirty = false;
148    }
149
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;
155    }
156    m_wasFrameScrollable = frameIsScrollable;
157
158    // The mainFrame view doesn't get included in the FrameTree below, so we
159    // update its size separately.
160    if (WebLayer* scrollingWebLayer = frameView ? toWebLayer(frameView->layerForScrolling()) : 0) {
161        // If there is a fullscreen element, set the scroll bounds to empty so the main frame won't scroll.
162        Document* mainFrameDocument = m_page->deprecatedLocalMainFrame()->document();
163        Element* fullscreenElement = Fullscreen::fullscreenElementFrom(*mainFrameDocument);
164        if (fullscreenElement && fullscreenElement != mainFrameDocument->documentElement())
165            scrollingWebLayer->setBounds(IntSize());
166        else
167            scrollingWebLayer->setBounds(frameView->contentsSize());
168    }
169
170    const FrameTree& tree = m_page->mainFrame()->tree();
171    for (const Frame* child = tree.firstChild(); child; child = child->tree().nextSibling()) {
172        if (!child->isLocalFrame())
173            continue;
174        if (WebLayer* scrollLayer = toWebLayer(toLocalFrame(child)->view()->layerForScrolling()))
175            scrollLayer->setBounds(toLocalFrame(child)->view()->contentsSize());
176    }
177}
178
179void ScrollingCoordinator::setLayerIsContainerForFixedPositionLayers(GraphicsLayer* layer, bool enable)
180{
181    if (WebLayer* scrollableLayer = toWebLayer(layer))
182        scrollableLayer->setIsContainerForFixedPositionLayers(enable);
183}
184
185static void clearPositionConstraintExceptForLayer(GraphicsLayer* layer, GraphicsLayer* except)
186{
187    if (layer && layer != except && toWebLayer(layer))
188        toWebLayer(layer)->setPositionConstraint(WebLayerPositionConstraint());
189}
190
191static WebLayerPositionConstraint computePositionConstraint(const RenderLayer* layer)
192{
193    ASSERT(layer->hasCompositedLayerMapping());
194    do {
195        if (layer->renderer()->style()->position() == FixedPosition) {
196            const RenderObject* fixedPositionObject = layer->renderer();
197            bool fixedToRight = !fixedPositionObject->style()->right().isAuto();
198            bool fixedToBottom = !fixedPositionObject->style()->bottom().isAuto();
199            return WebLayerPositionConstraint::fixedPosition(fixedToRight, fixedToBottom);
200        }
201
202        layer = layer->parent();
203
204        // Composited layers that inherit a fixed position state will be positioned with respect to the nearest compositedLayerMapping's GraphicsLayer.
205        // So, once we find a layer that has its own compositedLayerMapping, we can stop searching for a fixed position RenderObject.
206    } while (layer && !layer->hasCompositedLayerMapping());
207    return WebLayerPositionConstraint();
208}
209
210void ScrollingCoordinator::updateLayerPositionConstraint(RenderLayer* layer)
211{
212    ASSERT(layer->hasCompositedLayerMapping());
213    CompositedLayerMapping* compositedLayerMapping = layer->compositedLayerMapping();
214    GraphicsLayer* mainLayer = compositedLayerMapping->childForSuperlayers();
215
216    // Avoid unnecessary commits
217    clearPositionConstraintExceptForLayer(compositedLayerMapping->squashingContainmentLayer(), mainLayer);
218    clearPositionConstraintExceptForLayer(compositedLayerMapping->ancestorClippingLayer(), mainLayer);
219    clearPositionConstraintExceptForLayer(compositedLayerMapping->mainGraphicsLayer(), mainLayer);
220
221    if (WebLayer* scrollableLayer = toWebLayer(mainLayer))
222        scrollableLayer->setPositionConstraint(computePositionConstraint(layer));
223}
224
225void ScrollingCoordinator::willDestroyScrollableArea(ScrollableArea* scrollableArea)
226{
227    removeWebScrollbarLayer(scrollableArea, HorizontalScrollbar);
228    removeWebScrollbarLayer(scrollableArea, VerticalScrollbar);
229}
230
231void ScrollingCoordinator::removeWebScrollbarLayer(ScrollableArea* scrollableArea, ScrollbarOrientation orientation)
232{
233    ScrollbarMap& scrollbars = orientation == HorizontalScrollbar ? m_horizontalScrollbars : m_verticalScrollbars;
234    if (OwnPtr<WebScrollbarLayer> scrollbarLayer = scrollbars.take(scrollableArea))
235        GraphicsLayer::unregisterContentsLayer(scrollbarLayer->layer());
236}
237
238static PassOwnPtr<WebScrollbarLayer> createScrollbarLayer(Scrollbar* scrollbar)
239{
240    ScrollbarTheme* theme = scrollbar->theme();
241    blink::WebScrollbarThemePainter painter(theme, scrollbar);
242    OwnPtr<blink::WebScrollbarThemeGeometry> geometry(blink::WebScrollbarThemeGeometryNative::create(theme));
243
244    OwnPtr<WebScrollbarLayer> scrollbarLayer = adoptPtr(blink::Platform::current()->compositorSupport()->createScrollbarLayer(new blink::WebScrollbarImpl(scrollbar), painter, geometry.leakPtr()));
245    GraphicsLayer::registerContentsLayer(scrollbarLayer->layer());
246    return scrollbarLayer.release();
247}
248
249PassOwnPtr<WebScrollbarLayer> ScrollingCoordinator::createSolidColorScrollbarLayer(ScrollbarOrientation orientation, int thumbThickness, int trackStart, bool isLeftSideVerticalScrollbar)
250{
251    blink::WebScrollbar::Orientation webOrientation = (orientation == HorizontalScrollbar) ? blink::WebScrollbar::Horizontal : blink::WebScrollbar::Vertical;
252    OwnPtr<WebScrollbarLayer> scrollbarLayer = adoptPtr(blink::Platform::current()->compositorSupport()->createSolidColorScrollbarLayer(webOrientation, thumbThickness, trackStart, isLeftSideVerticalScrollbar));
253    GraphicsLayer::registerContentsLayer(scrollbarLayer->layer());
254    return scrollbarLayer.release();
255}
256
257static void detachScrollbarLayer(GraphicsLayer* scrollbarGraphicsLayer)
258{
259    ASSERT(scrollbarGraphicsLayer);
260
261    scrollbarGraphicsLayer->setContentsToPlatformLayer(0);
262    scrollbarGraphicsLayer->setDrawsContent(true);
263}
264
265static void setupScrollbarLayer(GraphicsLayer* scrollbarGraphicsLayer, WebScrollbarLayer* scrollbarLayer, WebLayer* scrollLayer, WebLayer* containerLayer)
266{
267    ASSERT(scrollbarGraphicsLayer);
268    ASSERT(scrollbarLayer);
269
270    if (!scrollLayer) {
271        detachScrollbarLayer(scrollbarGraphicsLayer);
272        return;
273    }
274    scrollbarLayer->setScrollLayer(scrollLayer);
275    scrollbarLayer->setClipLayer(containerLayer);
276    scrollbarGraphicsLayer->setContentsToPlatformLayer(scrollbarLayer->layer());
277    scrollbarGraphicsLayer->setDrawsContent(false);
278}
279
280WebScrollbarLayer* ScrollingCoordinator::addWebScrollbarLayer(ScrollableArea* scrollableArea, ScrollbarOrientation orientation, PassOwnPtr<blink::WebScrollbarLayer> scrollbarLayer)
281{
282    ScrollbarMap& scrollbars = orientation == HorizontalScrollbar ? m_horizontalScrollbars : m_verticalScrollbars;
283    return scrollbars.add(scrollableArea, scrollbarLayer).storedValue->value.get();
284}
285
286WebScrollbarLayer* ScrollingCoordinator::getWebScrollbarLayer(ScrollableArea* scrollableArea, ScrollbarOrientation orientation)
287{
288    ScrollbarMap& scrollbars = orientation == HorizontalScrollbar ? m_horizontalScrollbars : m_verticalScrollbars;
289    return scrollbars.get(scrollableArea);
290}
291
292void ScrollingCoordinator::scrollableAreaScrollbarLayerDidChange(ScrollableArea* scrollableArea, ScrollbarOrientation orientation)
293{
294// FIXME: Instead of hardcode here, we should make a setting flag.
295#if OS(MACOSX)
296    static const bool platformSupportsCoordinatedScrollbar = ScrollAnimatorMac::canUseCoordinatedScrollbar();
297    static const bool platformSupportsMainFrameOnly = false; // Don't care.
298#elif OS(ANDROID)
299    static const bool platformSupportsCoordinatedScrollbar = true;
300    static const bool platformSupportsMainFrameOnly = false;
301#else
302    static const bool platformSupportsCoordinatedScrollbar = true;
303    static const bool platformSupportsMainFrameOnly = true;
304#endif
305    if (!platformSupportsCoordinatedScrollbar)
306        return;
307
308    bool isMainFrame = isForMainFrame(scrollableArea);
309    if (!isMainFrame && platformSupportsMainFrameOnly)
310        return;
311
312    GraphicsLayer* scrollbarGraphicsLayer = orientation == HorizontalScrollbar
313        ? scrollableArea->layerForHorizontalScrollbar()
314        : scrollableArea->layerForVerticalScrollbar();
315
316    if (scrollbarGraphicsLayer) {
317        Scrollbar* scrollbar = orientation == HorizontalScrollbar ? scrollableArea->horizontalScrollbar() : scrollableArea->verticalScrollbar();
318        if (scrollbar->isCustomScrollbar()) {
319            detachScrollbarLayer(scrollbarGraphicsLayer);
320            return;
321        }
322
323        WebScrollbarLayer* scrollbarLayer = getWebScrollbarLayer(scrollableArea, orientation);
324        if (!scrollbarLayer) {
325            Settings* settings = m_page->mainFrame()->settings();
326
327            OwnPtr<WebScrollbarLayer> webScrollbarLayer;
328            if (settings->useSolidColorScrollbars()) {
329                ASSERT(RuntimeEnabledFeatures::overlayScrollbarsEnabled());
330                webScrollbarLayer = createSolidColorScrollbarLayer(orientation, scrollbar->theme()->thumbThickness(scrollbar), scrollbar->theme()->trackPosition(scrollbar), scrollableArea->shouldPlaceVerticalScrollbarOnLeft());
331            } else {
332                webScrollbarLayer = createScrollbarLayer(scrollbar);
333            }
334            scrollbarLayer = addWebScrollbarLayer(scrollableArea, orientation, webScrollbarLayer.release());
335        }
336
337        // Root layer non-overlay scrollbars should be marked opaque to disable
338        // blending.
339        bool isOpaqueScrollbar = !scrollbar->isOverlayScrollbar();
340        scrollbarGraphicsLayer->setContentsOpaque(isMainFrame && isOpaqueScrollbar);
341
342        WebLayer* scrollLayer = toWebLayer(scrollableArea->layerForScrolling());
343        WebLayer* containerLayer = toWebLayer(scrollableArea->layerForContainer());
344        setupScrollbarLayer(scrollbarGraphicsLayer, scrollbarLayer, scrollLayer, containerLayer);
345    } else
346        removeWebScrollbarLayer(scrollableArea, orientation);
347}
348
349bool ScrollingCoordinator::scrollableAreaScrollLayerDidChange(ScrollableArea* scrollableArea)
350{
351    GraphicsLayer* scrollLayer = scrollableArea->layerForScrolling();
352
353    if (scrollLayer) {
354        ASSERT(m_page);
355        // With pinch virtual viewport we no longer need to special case the main frame.
356        bool pinchVirtualViewportEnabled = m_page->settings().pinchVirtualViewportEnabled();
357        bool layerScrollShouldFireGraphicsLayerDidScroll = isForMainFrame(scrollableArea) && !pinchVirtualViewportEnabled;
358        scrollLayer->setScrollableArea(scrollableArea, layerScrollShouldFireGraphicsLayerDidScroll);
359    }
360
361    WebLayer* webLayer = toWebLayer(scrollableArea->layerForScrolling());
362    WebLayer* containerLayer = toWebLayer(scrollableArea->layerForContainer());
363    if (webLayer) {
364        webLayer->setScrollClipLayer(containerLayer);
365        webLayer->setScrollPosition(IntPoint(scrollableArea->scrollPosition() - scrollableArea->minimumScrollPosition()));
366        webLayer->setBounds(scrollableArea->contentsSize());
367        bool canScrollX = scrollableArea->userInputScrollable(HorizontalScrollbar);
368        bool canScrollY = scrollableArea->userInputScrollable(VerticalScrollbar);
369        webLayer->setUserScrollable(canScrollX, canScrollY);
370    }
371    if (WebScrollbarLayer* scrollbarLayer = getWebScrollbarLayer(scrollableArea, HorizontalScrollbar)) {
372        GraphicsLayer* horizontalScrollbarLayer = scrollableArea->layerForHorizontalScrollbar();
373        if (horizontalScrollbarLayer)
374            setupScrollbarLayer(horizontalScrollbarLayer, scrollbarLayer, webLayer, containerLayer);
375    }
376    if (WebScrollbarLayer* scrollbarLayer = getWebScrollbarLayer(scrollableArea, VerticalScrollbar)) {
377        GraphicsLayer* verticalScrollbarLayer = scrollableArea->layerForVerticalScrollbar();
378        if (verticalScrollbarLayer)
379            setupScrollbarLayer(verticalScrollbarLayer, scrollbarLayer, webLayer, containerLayer);
380    }
381
382    return !!webLayer;
383}
384
385typedef WTF::HashMap<const GraphicsLayer*, Vector<LayoutRect> > GraphicsLayerHitTestRects;
386
387// In order to do a DFS cross-frame walk of the RenderLayer tree, we need to know which
388// RenderLayers have child frames inside of them. This computes a mapping for the
389// current frame which we can consult while walking the layers of that frame.
390// Whenever we descend into a new frame, a new map will be created.
391typedef HashMap<const RenderLayer*, Vector<const LocalFrame*> > LayerFrameMap;
392static void makeLayerChildFrameMap(const LocalFrame* currentFrame, LayerFrameMap* map)
393{
394    map->clear();
395    const FrameTree& tree = currentFrame->tree();
396    for (const Frame* child = tree.firstChild(); child; child = child->tree().nextSibling()) {
397        if (!child->isLocalFrame())
398            continue;
399        const RenderObject* ownerRenderer = toLocalFrame(child)->ownerRenderer();
400        if (!ownerRenderer)
401            continue;
402        const RenderLayer* containingLayer = ownerRenderer->enclosingLayer();
403        LayerFrameMap::iterator iter = map->find(containingLayer);
404        if (iter == map->end())
405            map->add(containingLayer, Vector<const LocalFrame*>()).storedValue->value.append(toLocalFrame(child));
406        else
407            iter->value.append(toLocalFrame(child));
408    }
409}
410
411static void projectRectsToGraphicsLayerSpaceRecursive(
412    const RenderLayer* curLayer,
413    const LayerHitTestRects& layerRects,
414    GraphicsLayerHitTestRects& graphicsRects,
415    RenderGeometryMap& geometryMap,
416    HashSet<const RenderLayer*>& layersWithRects,
417    LayerFrameMap& layerChildFrameMap)
418{
419    // Project any rects for the current layer
420    LayerHitTestRects::const_iterator layerIter = layerRects.find(curLayer);
421    if (layerIter != layerRects.end()) {
422        // Find the enclosing composited layer when it's in another document (for non-composited iframes).
423        const RenderLayer* compositedLayer = layerIter->key->enclosingLayerForPaintInvalidationCrossingFrameBoundaries();
424        ASSERT(compositedLayer);
425
426        // Find the appropriate GraphicsLayer for the composited RenderLayer.
427        GraphicsLayer* graphicsLayer = compositedLayer->graphicsLayerBackingForScrolling();
428
429        GraphicsLayerHitTestRects::iterator glIter = graphicsRects.find(graphicsLayer);
430        Vector<LayoutRect>* glRects;
431        if (glIter == graphicsRects.end())
432            glRects = &graphicsRects.add(graphicsLayer, Vector<LayoutRect>()).storedValue->value;
433        else
434            glRects = &glIter->value;
435
436        // Transform each rect to the co-ordinate space of the graphicsLayer.
437        for (size_t i = 0; i < layerIter->value.size(); ++i) {
438            LayoutRect rect = layerIter->value[i];
439            if (compositedLayer != curLayer) {
440                FloatQuad compositorQuad = geometryMap.mapToContainer(rect, compositedLayer->renderer());
441                rect = LayoutRect(compositorQuad.boundingBox());
442                // If the enclosing composited layer itself is scrolled, we have to undo the subtraction
443                // of its scroll offset since we want the offset relative to the scrolling content, not
444                // the element itself.
445                if (compositedLayer->renderer()->hasOverflowClip())
446                    rect.move(compositedLayer->renderBox()->scrolledContentOffset());
447            }
448            RenderLayer::mapRectToPaintBackingCoordinates(compositedLayer->renderer(), rect);
449            glRects->append(rect);
450        }
451    }
452
453    // Walk child layers of interest
454    for (const RenderLayer* childLayer = curLayer->firstChild(); childLayer; childLayer = childLayer->nextSibling()) {
455        if (layersWithRects.contains(childLayer)) {
456            geometryMap.pushMappingsToAncestor(childLayer, curLayer);
457            projectRectsToGraphicsLayerSpaceRecursive(childLayer, layerRects, graphicsRects, geometryMap, layersWithRects, layerChildFrameMap);
458            geometryMap.popMappingsToAncestor(curLayer);
459        }
460    }
461
462    // If this layer has any frames of interest as a child of it, walk those (with an updated frame map).
463    LayerFrameMap::iterator mapIter = layerChildFrameMap.find(curLayer);
464    if (mapIter != layerChildFrameMap.end()) {
465        for (size_t i = 0; i < mapIter->value.size(); i++) {
466            const LocalFrame* childFrame = mapIter->value[i];
467            const RenderLayer* childLayer = childFrame->view()->renderView()->layer();
468            if (layersWithRects.contains(childLayer)) {
469                LayerFrameMap newLayerChildFrameMap;
470                makeLayerChildFrameMap(childFrame, &newLayerChildFrameMap);
471                geometryMap.pushMappingsToAncestor(childLayer, curLayer);
472                projectRectsToGraphicsLayerSpaceRecursive(childLayer, layerRects, graphicsRects, geometryMap, layersWithRects, newLayerChildFrameMap);
473                geometryMap.popMappingsToAncestor(curLayer);
474            }
475        }
476    }
477}
478
479static void projectRectsToGraphicsLayerSpace(LocalFrame* mainFrame, const LayerHitTestRects& layerRects, GraphicsLayerHitTestRects& graphicsRects)
480{
481    TRACE_EVENT0("input", "ScrollingCoordinator::projectRectsToGraphicsLayerSpace");
482    bool touchHandlerInChildFrame = false;
483
484    // We have a set of rects per RenderLayer, we need to map them to their bounding boxes in their
485    // enclosing composited layer. To do this most efficiently we'll walk the RenderLayer tree using
486    // RenderGeometryMap. First record all the branches we should traverse in the tree (including
487    // all documents on the page).
488    HashSet<const RenderLayer*> layersWithRects;
489    for (LayerHitTestRects::const_iterator layerIter = layerRects.begin(); layerIter != layerRects.end(); ++layerIter) {
490        const RenderLayer* layer = layerIter->key;
491        do {
492            if (!layersWithRects.add(layer).isNewEntry)
493                break;
494
495            if (layer->parent()) {
496                layer = layer->parent();
497            } else if (RenderObject* parentDocRenderer = layer->renderer()->frame()->ownerRenderer()) {
498                layer = parentDocRenderer->enclosingLayer();
499                touchHandlerInChildFrame = true;
500            }
501        } while (layer);
502    }
503
504    // Now walk the layer projecting rects while maintaining a RenderGeometryMap
505    MapCoordinatesFlags flags = UseTransforms;
506    if (touchHandlerInChildFrame)
507        flags |= TraverseDocumentBoundaries;
508    RenderLayer* rootLayer = mainFrame->contentRenderer()->layer();
509    RenderGeometryMap geometryMap(flags);
510    geometryMap.pushMappingsToAncestor(rootLayer, 0);
511    LayerFrameMap layerChildFrameMap;
512    makeLayerChildFrameMap(mainFrame, &layerChildFrameMap);
513    projectRectsToGraphicsLayerSpaceRecursive(rootLayer, layerRects, graphicsRects, geometryMap, layersWithRects, layerChildFrameMap);
514}
515
516void ScrollingCoordinator::updateTouchEventTargetRectsIfNeeded()
517{
518    TRACE_EVENT0("input", "ScrollingCoordinator::updateTouchEventTargetRectsIfNeeded");
519
520    if (!RuntimeEnabledFeatures::touchEnabled())
521        return;
522
523    LayerHitTestRects touchEventTargetRects;
524    computeTouchEventTargetRects(touchEventTargetRects);
525    setTouchEventTargetRects(touchEventTargetRects);
526}
527
528void ScrollingCoordinator::reset()
529{
530    for (ScrollbarMap::iterator it = m_horizontalScrollbars.begin(); it != m_horizontalScrollbars.end(); ++it)
531        GraphicsLayer::unregisterContentsLayer(it->value->layer());
532    for (ScrollbarMap::iterator it = m_verticalScrollbars.begin(); it != m_verticalScrollbars.end(); ++it)
533        GraphicsLayer::unregisterContentsLayer(it->value->layer());
534
535    m_horizontalScrollbars.clear();
536    m_verticalScrollbars.clear();
537    m_layersWithTouchRects.clear();
538    m_wasFrameScrollable = false;
539
540    // This is retained for testing.
541    m_lastMainThreadScrollingReasons = 0;
542    setShouldUpdateScrollLayerPositionOnMainThread(m_lastMainThreadScrollingReasons);
543}
544
545// Note that in principle this could be called more often than computeTouchEventTargetRects, for
546// example during a non-composited scroll (although that's not yet implemented - crbug.com/261307).
547void ScrollingCoordinator::setTouchEventTargetRects(LayerHitTestRects& layerRects)
548{
549    TRACE_EVENT0("input", "ScrollingCoordinator::setTouchEventTargetRects");
550
551    // Update the list of layers with touch hit rects.
552    HashSet<const RenderLayer*> oldLayersWithTouchRects;
553    m_layersWithTouchRects.swap(oldLayersWithTouchRects);
554    for (LayerHitTestRects::iterator it = layerRects.begin(); it != layerRects.end(); ++it) {
555        if (!it->value.isEmpty()) {
556            const RenderLayer* compositedLayer = it->key->enclosingLayerForPaintInvalidationCrossingFrameBoundaries();
557            ASSERT(compositedLayer);
558            m_layersWithTouchRects.add(compositedLayer);
559        }
560    }
561
562    // Ensure we have an entry for each composited layer that previously had rects (so that old
563    // ones will get cleared out). Note that ideally we'd track this on GraphicsLayer instead of
564    // RenderLayer, but we have no good hook into the lifetime of a GraphicsLayer.
565    for (HashSet<const RenderLayer*>::iterator it = oldLayersWithTouchRects.begin(); it != oldLayersWithTouchRects.end(); ++it) {
566        if (!layerRects.contains(*it))
567            layerRects.add(*it, Vector<LayoutRect>());
568    }
569
570    GraphicsLayerHitTestRects graphicsLayerRects;
571    projectRectsToGraphicsLayerSpace(m_page->deprecatedLocalMainFrame(), layerRects, graphicsLayerRects);
572
573    for (GraphicsLayerHitTestRects::const_iterator iter = graphicsLayerRects.begin(); iter != graphicsLayerRects.end(); ++iter) {
574        const GraphicsLayer* graphicsLayer = iter->key;
575        WebVector<WebRect> webRects(iter->value.size());
576        for (size_t i = 0; i < iter->value.size(); ++i)
577            webRects[i] = enclosingIntRect(iter->value[i]);
578        graphicsLayer->platformLayer()->setTouchEventHandlerRegion(webRects);
579    }
580}
581
582void ScrollingCoordinator::touchEventTargetRectsDidChange()
583{
584    if (!RuntimeEnabledFeatures::touchEnabled())
585        return;
586
587    // Wait until after layout to update.
588    if (!m_page->deprecatedLocalMainFrame()->view() || m_page->deprecatedLocalMainFrame()->view()->needsLayout())
589        return;
590
591    // FIXME: scheduleAnimation() is just a method of forcing the compositor to realize that it
592    // needs to commit here. We should expose a cleaner API for this.
593    RenderView* renderView = m_page->deprecatedLocalMainFrame()->contentRenderer();
594    if (renderView && renderView->compositor() && renderView->compositor()->staleInCompositingMode())
595        m_page->deprecatedLocalMainFrame()->view()->scheduleAnimation();
596
597    m_touchEventTargetRectsAreDirty = true;
598}
599
600void ScrollingCoordinator::updateScrollParentForGraphicsLayer(GraphicsLayer* child, RenderLayer* parent)
601{
602    WebLayer* scrollParentWebLayer = 0;
603    if (parent && parent->hasCompositedLayerMapping())
604        scrollParentWebLayer = toWebLayer(parent->compositedLayerMapping()->scrollingContentsLayer());
605
606    child->setScrollParent(scrollParentWebLayer);
607}
608
609void ScrollingCoordinator::updateClipParentForGraphicsLayer(GraphicsLayer* child, RenderLayer* parent)
610{
611    WebLayer* clipParentWebLayer = 0;
612    if (parent && parent->hasCompositedLayerMapping())
613        clipParentWebLayer = toWebLayer(parent->compositedLayerMapping()->parentForSublayers());
614
615    child->setClipParent(clipParentWebLayer);
616}
617
618void ScrollingCoordinator::willDestroyRenderLayer(RenderLayer* layer)
619{
620    m_layersWithTouchRects.remove(layer);
621}
622
623void ScrollingCoordinator::updateHaveWheelEventHandlers()
624{
625    ASSERT(isMainThread());
626    ASSERT(m_page);
627    if (!m_page->mainFrame()->isLocalFrame() || !m_page->deprecatedLocalMainFrame()->view())
628        return;
629
630    if (WebLayer* scrollLayer = toWebLayer(m_page->deprecatedLocalMainFrame()->view()->layerForScrolling())) {
631        bool haveHandlers = m_page->frameHost().eventHandlerRegistry().hasEventHandlers(EventHandlerRegistry::WheelEvent);
632        scrollLayer->setHaveWheelEventHandlers(haveHandlers);
633    }
634}
635
636void ScrollingCoordinator::updateHaveScrollEventHandlers()
637{
638    ASSERT(isMainThread());
639    ASSERT(m_page);
640    if (!m_page->mainFrame()->isLocalFrame() || !m_page->deprecatedLocalMainFrame()->view())
641        return;
642
643    // Currently the compositor only cares whether there are scroll handlers anywhere on the page
644    // instead on a per-layer basis. We therefore only update this information for the root
645    // scrolling layer.
646    if (WebLayer* scrollLayer = toWebLayer(m_page->deprecatedLocalMainFrame()->view()->layerForScrolling())) {
647        bool haveHandlers = m_page->frameHost().eventHandlerRegistry().hasEventHandlers(EventHandlerRegistry::ScrollEvent);
648        scrollLayer->setHaveScrollEventHandlers(haveHandlers);
649    }
650}
651
652void ScrollingCoordinator::setShouldUpdateScrollLayerPositionOnMainThread(MainThreadScrollingReasons reasons)
653{
654    if (!m_page->mainFrame()->isLocalFrame())
655        return;
656    if (WebLayer* scrollLayer = toWebLayer(m_page->deprecatedLocalMainFrame()->view()->layerForScrolling())) {
657        m_lastMainThreadScrollingReasons = reasons;
658        scrollLayer->setShouldScrollOnMainThread(reasons);
659    }
660}
661
662void ScrollingCoordinator::willBeDestroyed()
663{
664    ASSERT(m_page);
665    m_page = 0;
666    for (ScrollbarMap::iterator it = m_horizontalScrollbars.begin(); it != m_horizontalScrollbars.end(); ++it)
667        GraphicsLayer::unregisterContentsLayer(it->value->layer());
668    for (ScrollbarMap::iterator it = m_verticalScrollbars.begin(); it != m_verticalScrollbars.end(); ++it)
669        GraphicsLayer::unregisterContentsLayer(it->value->layer());
670}
671
672bool ScrollingCoordinator::coordinatesScrollingForFrameView(FrameView* frameView) const
673{
674    ASSERT(isMainThread());
675    ASSERT(m_page);
676
677    // We currently only handle the main frame.
678    if (&frameView->frame() != m_page->mainFrame())
679        return false;
680
681    if (!m_page->mainFrame()->isLocalFrame())
682        return false;
683
684    // We currently only support composited mode.
685    RenderView* renderView = m_page->deprecatedLocalMainFrame()->contentRenderer();
686    if (!renderView)
687        return false;
688    return renderView->usesCompositing();
689}
690
691Region ScrollingCoordinator::computeShouldHandleScrollGestureOnMainThreadRegion(const LocalFrame* frame, const IntPoint& frameLocation) const
692{
693    Region shouldHandleScrollGestureOnMainThreadRegion;
694    FrameView* frameView = frame->view();
695    if (!frameView)
696        return shouldHandleScrollGestureOnMainThreadRegion;
697
698    IntPoint offset = frameLocation;
699    offset.moveBy(frameView->frameRect().location());
700
701    if (const FrameView::ScrollableAreaSet* scrollableAreas = frameView->scrollableAreas()) {
702        for (FrameView::ScrollableAreaSet::const_iterator it = scrollableAreas->begin(), end = scrollableAreas->end(); it != end; ++it) {
703            ScrollableArea* scrollableArea = *it;
704            // Composited scrollable areas can be scrolled off the main thread.
705            if (scrollableArea->usesCompositedScrolling())
706                continue;
707            IntRect box = scrollableArea->scrollableAreaBoundingBox();
708            box.moveBy(offset);
709            shouldHandleScrollGestureOnMainThreadRegion.unite(box);
710        }
711    }
712
713    // We use GestureScrollBegin/Update/End for moving the resizer handle. So we mark these
714    // small resizer areas as non-fast-scrollable to allow the scroll gestures to be passed to
715    // main thread if they are targeting the resizer area. (Resizing is done in EventHandler.cpp
716    // on main thread).
717    if (const FrameView::ResizerAreaSet* resizerAreas = frameView->resizerAreas()) {
718        for (FrameView::ResizerAreaSet::const_iterator it = resizerAreas->begin(), end = resizerAreas->end(); it != end; ++it) {
719            RenderBox* box = *it;
720            IntRect bounds = box->absoluteBoundingBoxRect();
721            IntRect corner = box->layer()->scrollableArea()->touchResizerCornerRect(bounds);
722            corner.moveBy(offset);
723            shouldHandleScrollGestureOnMainThreadRegion.unite(corner);
724        }
725    }
726
727    if (const HashSet<RefPtr<Widget> >* children = frameView->children()) {
728        for (HashSet<RefPtr<Widget> >::const_iterator it = children->begin(), end = children->end(); it != end; ++it) {
729            if (!(*it)->isPluginView())
730                continue;
731
732            PluginView* pluginView = toPluginView(it->get());
733            if (pluginView->wantsWheelEvents())
734                shouldHandleScrollGestureOnMainThreadRegion.unite(pluginView->frameRect());
735        }
736    }
737
738    const FrameTree& tree = frame->tree();
739    for (Frame* subFrame = tree.firstChild(); subFrame; subFrame = subFrame->tree().nextSibling()) {
740        if (subFrame->isLocalFrame())
741            shouldHandleScrollGestureOnMainThreadRegion.unite(computeShouldHandleScrollGestureOnMainThreadRegion(toLocalFrame(subFrame), offset));
742    }
743
744    return shouldHandleScrollGestureOnMainThreadRegion;
745}
746
747static void accumulateDocumentTouchEventTargetRects(LayerHitTestRects& rects, const Document* document)
748{
749    ASSERT(document);
750    const EventTargetSet* targets = document->frameHost()->eventHandlerRegistry().eventHandlerTargets(EventHandlerRegistry::TouchEvent);
751    if (!targets)
752        return;
753
754    // If there's a handler on the window, document, html or body element (fairly common in practice),
755    // then we can quickly mark the entire document and skip looking at any other handlers.
756    // Note that technically a handler on the body doesn't cover the whole document, but it's
757    // reasonable to be conservative and report the whole document anyway.
758    //
759    // Fullscreen HTML5 video when OverlayFullscreenVideo is enabled is implemented by replacing the
760    // root cc::layer with the video layer so doing this optimization causes the compositor to think
761    // that there are no handlers, therefore skip it.
762    if (!document->renderView()->compositor()->inOverlayFullscreenVideo()) {
763        for (EventTargetSet::const_iterator iter = targets->begin(); iter != targets->end(); ++iter) {
764            EventTarget* target = iter->key;
765            Node* node = target->toNode();
766            if (target->toDOMWindow() || node == document || node == document->documentElement() || node == document->body()) {
767                if (RenderView* rendererView = document->renderView()) {
768                    rendererView->computeLayerHitTestRects(rects);
769                }
770                return;
771            }
772        }
773    }
774
775    for (EventTargetSet::const_iterator iter = targets->begin(); iter != targets->end(); ++iter) {
776        EventTarget* target = iter->key;
777        Node* node = target->toNode();
778        if (!node || !node->inDocument())
779            continue;
780
781        // If the document belongs to an invisible subframe it does not have a composited layer
782        // and should be skipped.
783        if (node->document().isInInvisibleSubframe())
784            continue;
785
786        if (node->isDocumentNode() && node != document) {
787            accumulateDocumentTouchEventTargetRects(rects, toDocument(node));
788        } else if (RenderObject* renderer = node->renderer()) {
789            // If the set also contains one of our ancestor nodes then processing
790            // this node would be redundant.
791            bool hasTouchEventTargetAncestor = false;
792            for (Node* ancestor = node->parentNode(); ancestor && !hasTouchEventTargetAncestor; ancestor = ancestor->parentNode()) {
793                if (targets->contains(ancestor))
794                    hasTouchEventTargetAncestor = true;
795            }
796            if (!hasTouchEventTargetAncestor) {
797                // Walk up the tree to the outermost non-composited scrollable layer.
798                RenderLayer* enclosingNonCompositedScrollLayer = 0;
799                for (RenderLayer* parent = renderer->enclosingLayer(); parent && parent->compositingState() == NotComposited; parent = parent->parent()) {
800                    if (parent->scrollsOverflow())
801                        enclosingNonCompositedScrollLayer = parent;
802                }
803
804                // Report the whole non-composited scroll layer as a touch hit rect because any
805                // rects inside of it may move around relative to their enclosing composited layer
806                // without causing the rects to be recomputed. Non-composited scrolling occurs on
807                // the main thread, so we're not getting much benefit from compositor touch hit
808                // testing in this case anyway.
809                if (enclosingNonCompositedScrollLayer)
810                    enclosingNonCompositedScrollLayer->computeSelfHitTestRects(rects);
811
812                renderer->computeLayerHitTestRects(rects);
813            }
814        }
815    }
816}
817
818void ScrollingCoordinator::computeTouchEventTargetRects(LayerHitTestRects& rects)
819{
820    TRACE_EVENT0("input", "ScrollingCoordinator::computeTouchEventTargetRects");
821    ASSERT(RuntimeEnabledFeatures::touchEnabled());
822
823    Document* document = m_page->deprecatedLocalMainFrame()->document();
824    if (!document || !document->view())
825        return;
826
827    accumulateDocumentTouchEventTargetRects(rects, document);
828}
829
830void ScrollingCoordinator::frameViewHasSlowRepaintObjectsDidChange(FrameView* frameView)
831{
832    ASSERT(isMainThread());
833    ASSERT(m_page);
834
835    if (!coordinatesScrollingForFrameView(frameView))
836        return;
837
838    m_shouldScrollOnMainThreadDirty = true;
839}
840
841void ScrollingCoordinator::frameViewFixedObjectsDidChange(FrameView* frameView)
842{
843    ASSERT(isMainThread());
844    ASSERT(m_page);
845
846    if (!coordinatesScrollingForFrameView(frameView))
847        return;
848
849    m_shouldScrollOnMainThreadDirty = true;
850}
851
852bool ScrollingCoordinator::isForMainFrame(ScrollableArea* scrollableArea) const
853{
854    return m_page->mainFrame()->isLocalFrame() ? scrollableArea == m_page->deprecatedLocalMainFrame()->view() : false;
855}
856
857void ScrollingCoordinator::frameViewRootLayerDidChange(FrameView* frameView)
858{
859    ASSERT(isMainThread());
860    ASSERT(m_page);
861
862    if (!coordinatesScrollingForFrameView(frameView))
863        return;
864
865    notifyLayoutUpdated();
866    updateHaveWheelEventHandlers();
867    updateHaveScrollEventHandlers();
868}
869
870#if OS(MACOSX)
871void ScrollingCoordinator::handleWheelEventPhase(PlatformWheelEventPhase phase)
872{
873    ASSERT(isMainThread());
874
875    if (!m_page)
876        return;
877
878    FrameView* frameView = m_page->deprecatedLocalMainFrame()->view();
879    if (!frameView)
880        return;
881
882    frameView->scrollAnimator()->handleWheelEventPhase(phase);
883}
884#endif
885
886bool ScrollingCoordinator::hasVisibleSlowRepaintViewportConstrainedObjects(FrameView* frameView) const
887{
888    const FrameView::ViewportConstrainedObjectSet* viewportConstrainedObjects = frameView->viewportConstrainedObjects();
889    if (!viewportConstrainedObjects)
890        return false;
891
892    for (FrameView::ViewportConstrainedObjectSet::const_iterator it = viewportConstrainedObjects->begin(), end = viewportConstrainedObjects->end(); it != end; ++it) {
893        RenderObject* renderer = *it;
894        ASSERT(renderer->isBoxModelObject() && renderer->hasLayer());
895        ASSERT(renderer->style()->position() == FixedPosition);
896        RenderLayer* layer = toRenderBoxModelObject(renderer)->layer();
897
898        // Whether the RenderLayer scrolls with the viewport is a tree-depenent
899        // property and our viewportConstrainedObjects collection is maintained
900        // with only RenderObject-level information.
901        if (!layer->scrollsWithViewport())
902            continue;
903
904        // If the whole subtree is invisible, there's no reason to scroll on
905        // the main thread because we don't need to generate invalidations
906        // for invisible content.
907        if (layer->subtreeIsInvisible())
908            continue;
909
910        // We're only smart enough to scroll viewport-constrainted objects
911        // in the compositor if they have their own backing or they paint
912        // into a grouped back (which necessarily all have the same viewport
913        // constraints).
914        CompositingState compositingState = layer->compositingState();
915        if (compositingState != PaintsIntoOwnBacking && compositingState != PaintsIntoGroupedBacking)
916            return true;
917    }
918    return false;
919}
920
921MainThreadScrollingReasons ScrollingCoordinator::mainThreadScrollingReasons() const
922{
923    MainThreadScrollingReasons reasons = static_cast<MainThreadScrollingReasons>(0);
924
925    if (!m_page->settings().threadedScrollingEnabled())
926        reasons |= ThreadedScrollingDisabled;
927
928    if (!m_page->mainFrame()->isLocalFrame())
929        return reasons;
930    FrameView* frameView = m_page->deprecatedLocalMainFrame()->view();
931    if (!frameView)
932        return reasons;
933
934    if (frameView->hasSlowRepaintObjects())
935        reasons |= HasSlowRepaintObjects;
936    FrameView::ScrollingReasons scrollingReasons = frameView->scrollingReasons();
937    const bool mayBeScrolledByInput = (scrollingReasons == FrameView::Scrollable);
938    const bool mayBeScrolledByScript = mayBeScrolledByInput || (scrollingReasons ==
939        FrameView::NotScrollableExplicitlyDisabled);
940
941    // TODO(awoloszyn) Currently crbug.com/304810 will let certain
942    // overflow:hidden elements scroll on the compositor thread, so we should
943    // not let this move there path as an optimization, when we have slow-repaint
944    // elements.
945    if (mayBeScrolledByScript && hasVisibleSlowRepaintViewportConstrainedObjects(frameView)) {
946        reasons |= HasNonLayerViewportConstrainedObjects;
947    }
948
949    return reasons;
950}
951
952String ScrollingCoordinator::mainThreadScrollingReasonsAsText(MainThreadScrollingReasons reasons)
953{
954    StringBuilder stringBuilder;
955
956    if (reasons & ScrollingCoordinator::HasSlowRepaintObjects)
957        stringBuilder.appendLiteral("Has slow repaint objects, ");
958    if (reasons & ScrollingCoordinator::HasViewportConstrainedObjectsWithoutSupportingFixedLayers)
959        stringBuilder.appendLiteral("Has viewport constrained objects without supporting fixed layers, ");
960    if (reasons & ScrollingCoordinator::HasNonLayerViewportConstrainedObjects)
961        stringBuilder.appendLiteral("Has non-layer viewport-constrained objects, ");
962    if (reasons & ScrollingCoordinator::ThreadedScrollingDisabled)
963        stringBuilder.appendLiteral("Threaded scrolling is disabled, ");
964
965    if (stringBuilder.length())
966        stringBuilder.resize(stringBuilder.length() - 2);
967    return stringBuilder.toString();
968}
969
970String ScrollingCoordinator::mainThreadScrollingReasonsAsText() const
971{
972    ASSERT(m_page->deprecatedLocalMainFrame()->document()->lifecycle().state() >= DocumentLifecycle::CompositingClean);
973    return mainThreadScrollingReasonsAsText(m_lastMainThreadScrollingReasons);
974}
975
976bool ScrollingCoordinator::frameViewIsDirty() const
977{
978    FrameView* frameView = m_page->mainFrame()->isLocalFrame() ? m_page->deprecatedLocalMainFrame()->view() : 0;
979    bool frameIsScrollable = frameView && frameView->isScrollable();
980    if (frameIsScrollable != m_wasFrameScrollable)
981        return true;
982
983    if (WebLayer* scrollLayer = frameView ? toWebLayer(frameView->layerForScrolling()) : 0)
984        return blink::WebSize(frameView->contentsSize()) != scrollLayer->bounds();
985    return false;
986}
987
988} // namespace blink
989