Upstream version 11.39.250.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / page / scrolling / ScrollingCoordinator.cpp
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
65 using blink::WebLayer;
66 using blink::WebLayerPositionConstraint;
67 using blink::WebRect;
68 using blink::WebScrollbarLayer;
69 using blink::WebVector;
70
71 namespace {
72
73 WebLayer* toWebLayer(blink::GraphicsLayer* layer)
74 {
75     return layer ? layer->platformLayer() : 0;
76 }
77
78 } // namespace
79
80 namespace blink {
81
82 PassOwnPtr<ScrollingCoordinator> ScrollingCoordinator::create(Page* page)
83 {
84     return adoptPtr(new ScrollingCoordinator(page));
85 }
86
87 ScrollingCoordinator::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
97 ScrollingCoordinator::~ScrollingCoordinator()
98 {
99 }
100
101 void 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
114 void ScrollingCoordinator::notifyLayoutUpdated()
115 {
116     m_scrollGestureRegionIsDirty = true;
117     m_touchEventTargetRectsAreDirty = true;
118     m_shouldScrollOnMainThreadDirty = true;
119 }
120
121 void 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
179 void ScrollingCoordinator::setLayerIsContainerForFixedPositionLayers(GraphicsLayer* layer, bool enable)
180 {
181     if (WebLayer* scrollableLayer = toWebLayer(layer))
182         scrollableLayer->setIsContainerForFixedPositionLayers(enable);
183 }
184
185 static void clearPositionConstraintExceptForLayer(GraphicsLayer* layer, GraphicsLayer* except)
186 {
187     if (layer && layer != except && toWebLayer(layer))
188         toWebLayer(layer)->setPositionConstraint(WebLayerPositionConstraint());
189 }
190
191 static 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
210 void 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
225 void ScrollingCoordinator::willDestroyScrollableArea(ScrollableArea* scrollableArea)
226 {
227     removeWebScrollbarLayer(scrollableArea, HorizontalScrollbar);
228     removeWebScrollbarLayer(scrollableArea, VerticalScrollbar);
229 }
230
231 void 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
238 static 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
249 PassOwnPtr<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
257 static void detachScrollbarLayer(GraphicsLayer* scrollbarGraphicsLayer)
258 {
259     ASSERT(scrollbarGraphicsLayer);
260
261     scrollbarGraphicsLayer->setContentsToPlatformLayer(0);
262     scrollbarGraphicsLayer->setDrawsContent(true);
263 }
264
265 static 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
280 WebScrollbarLayer* 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
286 WebScrollbarLayer* ScrollingCoordinator::getWebScrollbarLayer(ScrollableArea* scrollableArea, ScrollbarOrientation orientation)
287 {
288     ScrollbarMap& scrollbars = orientation == HorizontalScrollbar ? m_horizontalScrollbars : m_verticalScrollbars;
289     return scrollbars.get(scrollableArea);
290 }
291
292 void 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
349 bool 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
385 typedef 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.
391 typedef HashMap<const RenderLayer*, Vector<const LocalFrame*> > LayerFrameMap;
392 static 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
411 static 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
479 static 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
516 void 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
528 void 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).
547 void 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
582 void 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
600 void 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
609 void 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
618 void ScrollingCoordinator::willDestroyRenderLayer(RenderLayer* layer)
619 {
620     m_layersWithTouchRects.remove(layer);
621 }
622
623 void 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
636 void 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
652 void 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
662 void 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
672 bool 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
691 Region 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
747 static 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
818 void 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
830 void 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
841 void 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
852 bool ScrollingCoordinator::isForMainFrame(ScrollableArea* scrollableArea) const
853 {
854     return m_page->mainFrame()->isLocalFrame() ? scrollableArea == m_page->deprecatedLocalMainFrame()->view() : false;
855 }
856
857 void 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)
871 void 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
886 bool 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
921 MainThreadScrollingReasons 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
952 String 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
970 String ScrollingCoordinator::mainThreadScrollingReasonsAsText() const
971 {
972     ASSERT(m_page->deprecatedLocalMainFrame()->document()->lifecycle().state() >= DocumentLifecycle::CompositingClean);
973     return mainThreadScrollingReasonsAsText(m_lastMainThreadScrollingReasons);
974 }
975
976 bool 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