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 "RuntimeEnabledFeatures.h"
31 #include "core/dom/Document.h"
32 #include "core/dom/Node.h"
33 #include "core/dom/WheelController.h"
34 #include "core/html/HTMLElement.h"
35 #include "core/frame/Frame.h"
36 #include "core/frame/FrameView.h"
37 #include "core/page/Page.h"
38 #include "core/frame/Settings.h"
39 #include "platform/TraceEvent.h"
40 #include "platform/exported/WebScrollbarImpl.h"
41 #include "platform/exported/WebScrollbarThemeGeometryNative.h"
42 #include "platform/geometry/Region.h"
43 #include "platform/geometry/TransformState.h"
44 #include "platform/graphics/GraphicsLayer.h"
45 #if OS(MACOSX)
46 #include "platform/mac/ScrollAnimatorMac.h"
47 #endif
48 #include "platform/scroll/ScrollAnimator.h"
49 #include "platform/scroll/ScrollbarTheme.h"
50 #include "core/plugins/PluginView.h"
51 #include "core/rendering/CompositedLayerMapping.h"
52 #include "core/rendering/RenderGeometryMap.h"
53 #include "core/rendering/RenderLayerCompositor.h"
54 #include "core/rendering/RenderView.h"
55 #include "public/platform/Platform.h"
56 #include "public/platform/WebCompositorSupport.h"
57 #include "public/platform/WebLayerPositionConstraint.h"
58 #include "public/platform/WebScrollbarLayer.h"
59 #include "public/platform/WebScrollbarThemeGeometry.h"
60 #include "public/platform/WebScrollbarThemePainter.h"
61 #include "wtf/text/StringBuilder.h"
62
63 using blink::WebLayer;
64 using blink::WebLayerPositionConstraint;
65 using blink::WebRect;
66 using blink::WebScrollbarLayer;
67 using blink::WebVector;
68
69
70 namespace WebCore {
71
scrollingWebLayerForGraphicsLayer(GraphicsLayer * layer)72 static WebLayer* scrollingWebLayerForGraphicsLayer(GraphicsLayer* layer)
73 {
74 return layer->platformLayer();
75 }
76
scrollingWebLayerForScrollableArea(ScrollableArea * scrollableArea)77 WebLayer* ScrollingCoordinator::scrollingWebLayerForScrollableArea(ScrollableArea* scrollableArea)
78 {
79 GraphicsLayer* graphicsLayer = scrollLayerForScrollableArea(scrollableArea);
80 return graphicsLayer ? scrollingWebLayerForGraphicsLayer(graphicsLayer) : 0;
81 }
82
create(Page * page)83 PassRefPtr<ScrollingCoordinator> ScrollingCoordinator::create(Page* page)
84 {
85 return adoptRef(new ScrollingCoordinator(page));
86 }
87
ScrollingCoordinator(Page * page)88 ScrollingCoordinator::ScrollingCoordinator(Page* page)
89 : m_page(page)
90 , m_scrollGestureRegionIsDirty(false)
91 , m_touchEventTargetRectsAreDirty(false)
92 , m_wasFrameScrollable(false)
93 , m_lastMainThreadScrollingReasons(0)
94 {
95 }
96
~ScrollingCoordinator()97 ScrollingCoordinator::~ScrollingCoordinator()
98 {
99 ASSERT(!m_page);
100 for (ScrollbarMap::iterator it = m_horizontalScrollbars.begin(); it != m_horizontalScrollbars.end(); ++it)
101 GraphicsLayer::unregisterContentsLayer(it->value->layer());
102 for (ScrollbarMap::iterator it = m_verticalScrollbars.begin(); it != m_verticalScrollbars.end(); ++it)
103 GraphicsLayer::unregisterContentsLayer(it->value->layer());
104
105 }
106
touchHitTestingEnabled() const107 bool ScrollingCoordinator::touchHitTestingEnabled() const
108 {
109 RenderView* contentRenderer = m_page->mainFrame()->contentRenderer();
110 Settings* settings = m_page->mainFrame()->document()->settings();
111 return RuntimeEnabledFeatures::touchEnabled() && settings->compositorTouchHitTesting() && contentRenderer && contentRenderer->usesCompositing();
112 }
113
setShouldHandleScrollGestureOnMainThreadRegion(const Region & region)114 void ScrollingCoordinator::setShouldHandleScrollGestureOnMainThreadRegion(const Region& region)
115 {
116 if (WebLayer* scrollLayer = scrollingWebLayerForScrollableArea(m_page->mainFrame()->view())) {
117 Vector<IntRect> rects = region.rects();
118 WebVector<WebRect> webRects(rects.size());
119 for (size_t i = 0; i < rects.size(); ++i)
120 webRects[i] = rects[i];
121 scrollLayer->setNonFastScrollableRegion(webRects);
122 }
123 }
124
notifyLayoutUpdated()125 void ScrollingCoordinator::notifyLayoutUpdated()
126 {
127 // These computations need to happen after compositing is updated.
128 m_scrollGestureRegionIsDirty = true;
129 m_touchEventTargetRectsAreDirty = true;
130 }
131
updateAfterCompositingChange()132 void ScrollingCoordinator::updateAfterCompositingChange()
133 {
134 TRACE_EVENT0("input", "ScrollingCoordinator::updateAfterCompositingChange");
135
136 if (m_scrollGestureRegionIsDirty) {
137 // Compute the region of the page where we can't handle scroll gestures and mousewheel events
138 // on the impl thread. This currently includes:
139 // 1. All scrollable areas, such as subframes, overflow divs and list boxes, whose composited
140 // scrolling are not enabled. We need to do this even if the frame view whose layout was updated
141 // is not the main frame.
142 // 2. Resize control areas, e.g. the small rect at the right bottom of div/textarea/iframe when
143 // CSS property "resize" is enabled.
144 // 3. Plugin areas.
145 Region shouldHandleScrollGestureOnMainThreadRegion = computeShouldHandleScrollGestureOnMainThreadRegion(m_page->mainFrame(), IntPoint());
146 setShouldHandleScrollGestureOnMainThreadRegion(shouldHandleScrollGestureOnMainThreadRegion);
147 m_scrollGestureRegionIsDirty = false;
148 }
149
150 if (m_touchEventTargetRectsAreDirty) {
151 updateTouchEventTargetRectsIfNeeded();
152 m_touchEventTargetRectsAreDirty = false;
153 }
154
155 FrameView* frameView = m_page->mainFrame()->view();
156 bool frameIsScrollable = frameView && frameView->isScrollable();
157 if (m_wasFrameScrollable != frameIsScrollable)
158 updateShouldUpdateScrollLayerPositionOnMainThread();
159 m_wasFrameScrollable = frameIsScrollable;
160
161 const FrameTree& tree = m_page->mainFrame()->tree();
162 for (const Frame* child = tree.firstChild(); child; child = child->tree().nextSibling()) {
163 if (WebLayer* scrollLayer = scrollingWebLayerForScrollableArea(child->view()))
164 scrollLayer->setBounds(child->view()->contentsSize());
165 }
166 }
167
setLayerIsContainerForFixedPositionLayers(GraphicsLayer * layer,bool enable)168 void ScrollingCoordinator::setLayerIsContainerForFixedPositionLayers(GraphicsLayer* layer, bool enable)
169 {
170 if (WebLayer* scrollableLayer = scrollingWebLayerForGraphicsLayer(layer))
171 scrollableLayer->setIsContainerForFixedPositionLayers(enable);
172 }
173
clearPositionConstraintExceptForLayer(GraphicsLayer * layer,GraphicsLayer * except)174 static void clearPositionConstraintExceptForLayer(GraphicsLayer* layer, GraphicsLayer* except)
175 {
176 if (layer && layer != except && scrollingWebLayerForGraphicsLayer(layer))
177 scrollingWebLayerForGraphicsLayer(layer)->setPositionConstraint(WebLayerPositionConstraint());
178 }
179
computePositionConstraint(const RenderLayer * layer)180 static WebLayerPositionConstraint computePositionConstraint(const RenderLayer* layer)
181 {
182 ASSERT(layer->hasCompositedLayerMapping());
183 do {
184 if (layer->renderer()->style()->position() == FixedPosition) {
185 const RenderObject* fixedPositionObject = layer->renderer();
186 bool fixedToRight = !fixedPositionObject->style()->right().isAuto();
187 bool fixedToBottom = !fixedPositionObject->style()->bottom().isAuto();
188 return WebLayerPositionConstraint::fixedPosition(fixedToRight, fixedToBottom);
189 }
190
191 layer = layer->parent();
192
193 // Composited layers that inherit a fixed position state will be positioned with respect to the nearest compositedLayerMapping's GraphicsLayer.
194 // So, once we find a layer that has its own compositedLayerMapping, we can stop searching for a fixed position RenderObject.
195 } while (layer && !layer->hasCompositedLayerMapping());
196 return WebLayerPositionConstraint();
197 }
198
updateLayerPositionConstraint(RenderLayer * layer)199 void ScrollingCoordinator::updateLayerPositionConstraint(RenderLayer* layer)
200 {
201 ASSERT(layer->hasCompositedLayerMapping());
202 CompositedLayerMappingPtr compositedLayerMapping = layer->compositedLayerMapping();
203 GraphicsLayer* mainLayer = compositedLayerMapping->childForSuperlayers();
204
205 // Avoid unnecessary commits
206 clearPositionConstraintExceptForLayer(compositedLayerMapping->ancestorClippingLayer(), mainLayer);
207 clearPositionConstraintExceptForLayer(compositedLayerMapping->mainGraphicsLayer(), mainLayer);
208
209 if (WebLayer* scrollableLayer = scrollingWebLayerForGraphicsLayer(mainLayer))
210 scrollableLayer->setPositionConstraint(computePositionConstraint(layer));
211 }
212
willDestroyScrollableArea(ScrollableArea * scrollableArea)213 void ScrollingCoordinator::willDestroyScrollableArea(ScrollableArea* scrollableArea)
214 {
215 removeWebScrollbarLayer(scrollableArea, HorizontalScrollbar);
216 removeWebScrollbarLayer(scrollableArea, VerticalScrollbar);
217 }
218
removeWebScrollbarLayer(ScrollableArea * scrollableArea,ScrollbarOrientation orientation)219 void ScrollingCoordinator::removeWebScrollbarLayer(ScrollableArea* scrollableArea, ScrollbarOrientation orientation)
220 {
221 ScrollbarMap& scrollbars = orientation == HorizontalScrollbar ? m_horizontalScrollbars : m_verticalScrollbars;
222 if (OwnPtr<WebScrollbarLayer> scrollbarLayer = scrollbars.take(scrollableArea))
223 GraphicsLayer::unregisterContentsLayer(scrollbarLayer->layer());
224 }
225
createScrollbarLayer(Scrollbar * scrollbar)226 static PassOwnPtr<WebScrollbarLayer> createScrollbarLayer(Scrollbar* scrollbar)
227 {
228 ScrollbarTheme* theme = scrollbar->theme();
229 blink::WebScrollbarThemePainter painter(theme, scrollbar);
230 OwnPtr<blink::WebScrollbarThemeGeometry> geometry(blink::WebScrollbarThemeGeometryNative::create(theme));
231
232 OwnPtr<WebScrollbarLayer> scrollbarLayer = adoptPtr(blink::Platform::current()->compositorSupport()->createScrollbarLayer(new blink::WebScrollbarImpl(scrollbar), painter, geometry.leakPtr()));
233 GraphicsLayer::registerContentsLayer(scrollbarLayer->layer());
234 return scrollbarLayer.release();
235 }
236
createSolidColorScrollbarLayer(ScrollbarOrientation orientation,int thumbThickness,bool isLeftSideVerticalScrollbar)237 PassOwnPtr<WebScrollbarLayer> ScrollingCoordinator::createSolidColorScrollbarLayer(ScrollbarOrientation orientation, int thumbThickness, bool isLeftSideVerticalScrollbar)
238 {
239 blink::WebScrollbar::Orientation webOrientation = (orientation == HorizontalScrollbar) ? blink::WebScrollbar::Horizontal : blink::WebScrollbar::Vertical;
240 OwnPtr<WebScrollbarLayer> scrollbarLayer = adoptPtr(blink::Platform::current()->compositorSupport()->createSolidColorScrollbarLayer(webOrientation, thumbThickness, isLeftSideVerticalScrollbar));
241 GraphicsLayer::registerContentsLayer(scrollbarLayer->layer());
242 return scrollbarLayer.release();
243 }
244
detachScrollbarLayer(GraphicsLayer * scrollbarGraphicsLayer)245 static void detachScrollbarLayer(GraphicsLayer* scrollbarGraphicsLayer)
246 {
247 ASSERT(scrollbarGraphicsLayer);
248
249 scrollbarGraphicsLayer->setContentsToPlatformLayer(0);
250 scrollbarGraphicsLayer->setDrawsContent(true);
251 }
252
setupScrollbarLayer(GraphicsLayer * scrollbarGraphicsLayer,WebScrollbarLayer * scrollbarLayer,WebLayer * scrollLayer)253 static void setupScrollbarLayer(GraphicsLayer* scrollbarGraphicsLayer, WebScrollbarLayer* scrollbarLayer, WebLayer* scrollLayer)
254 {
255 ASSERT(scrollbarGraphicsLayer);
256 ASSERT(scrollbarLayer);
257
258 if (!scrollLayer) {
259 detachScrollbarLayer(scrollbarGraphicsLayer);
260 return;
261 }
262 scrollbarLayer->setScrollLayer(scrollLayer);
263 scrollbarGraphicsLayer->setContentsToPlatformLayer(scrollbarLayer->layer());
264 scrollbarGraphicsLayer->setDrawsContent(false);
265 }
266
addWebScrollbarLayer(ScrollableArea * scrollableArea,ScrollbarOrientation orientation,PassOwnPtr<blink::WebScrollbarLayer> scrollbarLayer)267 WebScrollbarLayer* ScrollingCoordinator::addWebScrollbarLayer(ScrollableArea* scrollableArea, ScrollbarOrientation orientation, PassOwnPtr<blink::WebScrollbarLayer> scrollbarLayer)
268 {
269 ScrollbarMap& scrollbars = orientation == HorizontalScrollbar ? m_horizontalScrollbars : m_verticalScrollbars;
270 return scrollbars.add(scrollableArea, scrollbarLayer).iterator->value.get();
271 }
272
getWebScrollbarLayer(ScrollableArea * scrollableArea,ScrollbarOrientation orientation)273 WebScrollbarLayer* ScrollingCoordinator::getWebScrollbarLayer(ScrollableArea* scrollableArea, ScrollbarOrientation orientation)
274 {
275 ScrollbarMap& scrollbars = orientation == HorizontalScrollbar ? m_horizontalScrollbars : m_verticalScrollbars;
276 return scrollbars.get(scrollableArea);
277 }
278
scrollableAreaScrollbarLayerDidChange(ScrollableArea * scrollableArea,ScrollbarOrientation orientation)279 void ScrollingCoordinator::scrollableAreaScrollbarLayerDidChange(ScrollableArea* scrollableArea, ScrollbarOrientation orientation)
280 {
281 // FIXME: Instead of hardcode here, we should make a setting flag.
282 #if OS(MACOSX)
283 static const bool platformSupportsCoordinatedScrollbar = ScrollAnimatorMac::canUseCoordinatedScrollbar();
284 static const bool platformSupportsMainFrameOnly = false; // Don't care.
285 #elif OS(ANDROID)
286 static const bool platformSupportsCoordinatedScrollbar = true;
287 static const bool platformSupportsMainFrameOnly = false;
288 #else
289 static const bool platformSupportsCoordinatedScrollbar = true;
290 static const bool platformSupportsMainFrameOnly = true;
291 #endif
292 if (!platformSupportsCoordinatedScrollbar)
293 return;
294
295 bool isMainFrame = isForMainFrame(scrollableArea);
296 if (!isMainFrame && platformSupportsMainFrameOnly)
297 return;
298
299 GraphicsLayer* scrollbarGraphicsLayer = orientation == HorizontalScrollbar ? horizontalScrollbarLayerForScrollableArea(scrollableArea) : verticalScrollbarLayerForScrollableArea(scrollableArea);
300 if (scrollbarGraphicsLayer) {
301 Scrollbar* scrollbar = orientation == HorizontalScrollbar ? scrollableArea->horizontalScrollbar() : scrollableArea->verticalScrollbar();
302 if (scrollbar->isCustomScrollbar()) {
303 detachScrollbarLayer(scrollbarGraphicsLayer);
304 return;
305 }
306
307 WebScrollbarLayer* scrollbarLayer = getWebScrollbarLayer(scrollableArea, orientation);
308 if (!scrollbarLayer) {
309 Settings* settings = m_page->mainFrame()->document()->settings();
310
311 OwnPtr<WebScrollbarLayer> webScrollbarLayer;
312 if (settings->useSolidColorScrollbars()) {
313 ASSERT(RuntimeEnabledFeatures::overlayScrollbarsEnabled());
314 webScrollbarLayer = createSolidColorScrollbarLayer(orientation, scrollbar->theme()->thumbThickness(scrollbar), scrollableArea->shouldPlaceVerticalScrollbarOnLeft());
315 } else {
316 webScrollbarLayer = createScrollbarLayer(scrollbar);
317 }
318 scrollbarLayer = addWebScrollbarLayer(scrollableArea, orientation, webScrollbarLayer.release());
319 }
320
321 // Root layer non-overlay scrollbars should be marked opaque to disable
322 // blending.
323 bool isOpaqueScrollbar = !scrollbar->isOverlayScrollbar();
324 if (!scrollbarGraphicsLayer->contentsOpaque())
325 scrollbarGraphicsLayer->setContentsOpaque(isMainFrame && isOpaqueScrollbar);
326 scrollbarLayer->layer()->setOpaque(scrollbarGraphicsLayer->contentsOpaque());
327
328 setupScrollbarLayer(scrollbarGraphicsLayer, scrollbarLayer, scrollingWebLayerForScrollableArea(scrollableArea));
329 } else
330 removeWebScrollbarLayer(scrollableArea, orientation);
331 }
332
scrollableAreaScrollLayerDidChange(ScrollableArea * scrollableArea)333 bool ScrollingCoordinator::scrollableAreaScrollLayerDidChange(ScrollableArea* scrollableArea)
334 {
335 GraphicsLayer* scrollLayer = scrollLayerForScrollableArea(scrollableArea);
336 if (scrollLayer) {
337 bool isMainFrame = isForMainFrame(scrollableArea);
338 scrollLayer->setScrollableArea(scrollableArea, isMainFrame);
339 }
340
341 WebLayer* webLayer = scrollingWebLayerForScrollableArea(scrollableArea);
342 if (webLayer) {
343 webLayer->setScrollable(true);
344 webLayer->setScrollPosition(IntPoint(scrollableArea->scrollPosition() - scrollableArea->minimumScrollPosition()));
345 webLayer->setMaxScrollPosition(IntSize(scrollableArea->scrollSize(HorizontalScrollbar), scrollableArea->scrollSize(VerticalScrollbar)));
346 bool canScrollX = scrollableArea->userInputScrollable(HorizontalScrollbar);
347 bool canScrollY = scrollableArea->userInputScrollable(VerticalScrollbar);
348 webLayer->setUserScrollable(canScrollX, canScrollY);
349 }
350 if (WebScrollbarLayer* scrollbarLayer = getWebScrollbarLayer(scrollableArea, HorizontalScrollbar)) {
351 GraphicsLayer* horizontalScrollbarLayer = horizontalScrollbarLayerForScrollableArea(scrollableArea);
352 if (horizontalScrollbarLayer)
353 setupScrollbarLayer(horizontalScrollbarLayer, scrollbarLayer, webLayer);
354 }
355 if (WebScrollbarLayer* scrollbarLayer = getWebScrollbarLayer(scrollableArea, VerticalScrollbar)) {
356 GraphicsLayer* verticalScrollbarLayer = verticalScrollbarLayerForScrollableArea(scrollableArea);
357 if (verticalScrollbarLayer)
358 setupScrollbarLayer(verticalScrollbarLayer, scrollbarLayer, webLayer);
359 }
360
361 return !!webLayer;
362 }
363
364 // In order to do a DFS cross-frame walk of the RenderLayer tree, we need to know which
365 // RenderLayers have child frames inside of them. This computes a mapping for the
366 // current frame which we can consult while walking the layers of that frame.
367 // Whenever we descend into a new frame, a new map will be created.
368 typedef HashMap<const RenderLayer*, Vector<const Frame*> > LayerFrameMap;
makeLayerChildFrameMap(const Frame * currentFrame,LayerFrameMap * map)369 static void makeLayerChildFrameMap(const Frame* currentFrame, LayerFrameMap* map)
370 {
371 map->clear();
372 const FrameTree& tree = currentFrame->tree();
373 for (const Frame* child = tree.firstChild(); child; child = child->tree().nextSibling()) {
374 const RenderLayer* containingLayer = child->ownerRenderer()->enclosingLayer();
375 LayerFrameMap::iterator iter = map->find(containingLayer);
376 if (iter == map->end())
377 iter = map->add(containingLayer, Vector<const Frame*>()).iterator;
378 iter->value.append(child);
379 }
380 }
381
convertLayerRectsToEnclosingCompositedLayerRecursive(const RenderLayer * curLayer,const LayerHitTestRects & layerRects,LayerHitTestRects & compositorRects,RenderGeometryMap & geometryMap,HashSet<const RenderLayer * > & layersWithRects,LayerFrameMap & layerChildFrameMap)382 static void convertLayerRectsToEnclosingCompositedLayerRecursive(
383 const RenderLayer* curLayer,
384 const LayerHitTestRects& layerRects,
385 LayerHitTestRects& compositorRects,
386 RenderGeometryMap& geometryMap,
387 HashSet<const RenderLayer*>& layersWithRects,
388 LayerFrameMap& layerChildFrameMap)
389 {
390 // Project any rects for the current layer
391 LayerHitTestRects::const_iterator layerIter = layerRects.find(curLayer);
392 if (layerIter != layerRects.end()) {
393 // Find the enclosing composited layer when it's in another document (for non-composited iframes).
394 RenderLayer* compositedLayer = 0;
395 for (const RenderLayer* layer = layerIter->key; !compositedLayer;) {
396 compositedLayer = layer->enclosingCompositingLayerForRepaint();
397 if (!compositedLayer) {
398 RenderObject* owner = layer->renderer()->frame()->ownerRenderer();
399 if (!owner)
400 break;
401 layer = owner->enclosingLayer();
402 }
403 }
404 if (!compositedLayer) {
405 // Since this machinery is used only when accelerated compositing is enabled, we expect
406 // that every layer should have an enclosing composited layer.
407 ASSERT_NOT_REACHED();
408 return;
409 }
410
411 LayerHitTestRects::iterator compIter = compositorRects.find(compositedLayer);
412 if (compIter == compositorRects.end())
413 compIter = compositorRects.add(compositedLayer, Vector<LayoutRect>()).iterator;
414 // Transform each rect to the co-ordinate space of it's enclosing composited layer.
415 for (size_t i = 0; i < layerIter->value.size(); ++i) {
416 LayoutRect rect = layerIter->value[i];
417 if (compositedLayer != curLayer) {
418 FloatQuad compositorQuad = geometryMap.mapToContainer(rect, compositedLayer->renderer());
419 rect = LayoutRect(compositorQuad.boundingBox());
420 // If the enclosing composited layer itself is scrolled, we have to undo the subtraction
421 // of its scroll offset since we want the offset relative to the scrolling content, not
422 // the element itself.
423 if (compositedLayer->renderer()->hasOverflowClip())
424 rect.move(compositedLayer->renderBox()->scrolledContentOffset());
425 }
426 compIter->value.append(rect);
427 }
428 }
429
430 // Walk child layers of interest
431 for (const RenderLayer* childLayer = curLayer->firstChild(); childLayer; childLayer = childLayer->nextSibling()) {
432 if (layersWithRects.contains(childLayer)) {
433 geometryMap.pushMappingsToAncestor(childLayer, curLayer);
434 convertLayerRectsToEnclosingCompositedLayerRecursive(childLayer, layerRects, compositorRects, geometryMap, layersWithRects, layerChildFrameMap);
435 geometryMap.popMappingsToAncestor(curLayer);
436 }
437 }
438
439 // If this layer has any frames of interest as a child of it, walk those (with an updated frame map).
440 LayerFrameMap::iterator mapIter = layerChildFrameMap.find(curLayer);
441 if (mapIter != layerChildFrameMap.end()) {
442 for (size_t i = 0; i < mapIter->value.size(); i++) {
443 const Frame* childFrame = mapIter->value[i];
444 const RenderLayer* childLayer = childFrame->view()->renderView()->layer();
445 if (layersWithRects.contains(childLayer)) {
446 LayerFrameMap newLayerChildFrameMap;
447 makeLayerChildFrameMap(childFrame, &newLayerChildFrameMap);
448 geometryMap.pushMappingsToAncestor(childLayer, curLayer);
449 convertLayerRectsToEnclosingCompositedLayerRecursive(childLayer, layerRects, compositorRects, geometryMap, layersWithRects, newLayerChildFrameMap);
450 geometryMap.popMappingsToAncestor(curLayer);
451 }
452 }
453 }
454 }
455
convertLayerRectsToEnclosingCompositedLayer(Frame * mainFrame,const LayerHitTestRects & layerRects,LayerHitTestRects & compositorRects)456 static void convertLayerRectsToEnclosingCompositedLayer(Frame* mainFrame, const LayerHitTestRects& layerRects, LayerHitTestRects& compositorRects)
457 {
458 TRACE_EVENT0("input", "ScrollingCoordinator::convertLayerRectsToEnclosingCompositedLayer");
459 bool touchHandlerInChildFrame = false;
460
461 // We have a set of rects per RenderLayer, we need to map them to their bounding boxes in their
462 // enclosing composited layer. To do this most efficiently we'll walk the RenderLayer tree using
463 // RenderGeometryMap. First record all the branches we should traverse in the tree (including
464 // all documents on the page).
465 HashSet<const RenderLayer*> layersWithRects;
466 for (LayerHitTestRects::const_iterator layerIter = layerRects.begin(); layerIter != layerRects.end(); ++layerIter) {
467 const RenderLayer* layer = layerIter->key;
468 do {
469 if (!layersWithRects.add(layer).isNewEntry)
470 break;
471
472 if (layer->parent()) {
473 layer = layer->parent();
474 } else if (RenderObject* parentDocRenderer = layer->renderer()->frame()->ownerRenderer()) {
475 layer = parentDocRenderer->enclosingLayer();
476 touchHandlerInChildFrame = true;
477 }
478 } while (layer);
479 }
480
481 // Now walk the layer projecting rects while maintaining a RenderGeometryMap
482 MapCoordinatesFlags flags = UseTransforms;
483 if (touchHandlerInChildFrame)
484 flags |= TraverseDocumentBoundaries;
485 RenderGeometryMap geometryMap(flags);
486 LayerFrameMap layerChildFrameMap;
487 makeLayerChildFrameMap(mainFrame, &layerChildFrameMap);
488 convertLayerRectsToEnclosingCompositedLayerRecursive(mainFrame->contentRenderer()->layer(), layerRects, compositorRects, geometryMap, layersWithRects, layerChildFrameMap);
489 }
490
updateTouchEventTargetRectsIfNeeded()491 void ScrollingCoordinator::updateTouchEventTargetRectsIfNeeded()
492 {
493 TRACE_EVENT0("input", "ScrollingCoordinator::updateTouchEventTargetRectsIfNeeded");
494
495 if (!touchHitTestingEnabled())
496 return;
497
498 LayerHitTestRects touchEventTargetRects;
499 computeTouchEventTargetRects(touchEventTargetRects);
500 setTouchEventTargetRects(touchEventTargetRects);
501 }
502
reset()503 void ScrollingCoordinator::reset()
504 {
505 m_horizontalScrollbars.clear();
506 m_verticalScrollbars.clear();
507 m_layersWithTouchRects.clear();
508 m_wasFrameScrollable = false;
509
510 // This is retained for testing.
511 m_lastMainThreadScrollingReasons = 0;
512 setShouldUpdateScrollLayerPositionOnMainThread(m_lastMainThreadScrollingReasons);
513 }
514
515 // Note that in principle this could be called more often than computeTouchEventTargetRects, for
516 // example during a non-composited scroll (although that's not yet implemented - crbug.com/261307).
setTouchEventTargetRects(const LayerHitTestRects & layerRects)517 void ScrollingCoordinator::setTouchEventTargetRects(const LayerHitTestRects& layerRects)
518 {
519 TRACE_EVENT0("input", "ScrollingCoordinator::setTouchEventTargetRects");
520
521 LayerHitTestRects compositorRects;
522 convertLayerRectsToEnclosingCompositedLayer(m_page->mainFrame(), layerRects, compositorRects);
523
524 HashSet<const RenderLayer*> oldLayersWithTouchRects;
525 m_layersWithTouchRects.swap(oldLayersWithTouchRects);
526
527 for (LayerHitTestRects::const_iterator iter = compositorRects.begin(); iter != compositorRects.end(); ++iter) {
528 const RenderLayer* layer = iter->key;
529 WebVector<WebRect> webRects(iter->value.size());
530 for (size_t i = 0; i < iter->value.size(); ++i)
531 webRects[i] = enclosingIntRect(iter->value[i]);
532 // This should be ensured by convertLayerRectsToEnclosingCompositedLayer above.
533 ASSERT(layer->hasCompositedLayerMapping());
534 CompositedLayerMappingPtr compositedLayerMapping = layer->compositedLayerMapping();
535 // If the layer is using composited scrolling, then it's the contents that these
536 // rects apply to.
537 GraphicsLayer* graphicsLayer = compositedLayerMapping->scrollingContentsLayer();
538 if (!graphicsLayer)
539 graphicsLayer = compositedLayerMapping->mainGraphicsLayer();
540 graphicsLayer->platformLayer()->setTouchEventHandlerRegion(webRects);
541 oldLayersWithTouchRects.remove(layer);
542 m_layersWithTouchRects.add(layer);
543 }
544
545 // If there are any layers left that we haven't updated, clear them out.
546 for (HashSet<const RenderLayer*>::iterator it = oldLayersWithTouchRects.begin(); it != oldLayersWithTouchRects.end(); ++it) {
547 // FIXME: This is a bug. What's happening here is that we're clearing touch regions for
548 // layers that we didn't visit above. That assumes a 1:1 mapping between RenderLayer and
549 // the graphics layer that owns the touch rects. This is false in the case of
550 // HasOwnBackingButPaintsIntoAncestor and will be extra-false in the world of squashing.
551 if ((*it)->hasCompositedLayerMapping()) {
552 GraphicsLayer* graphicsLayer = (*it)->compositedLayerMapping()->scrollingContentsLayer();
553 if (!graphicsLayer)
554 graphicsLayer = (*it)->compositedLayerMapping()->mainGraphicsLayer();
555 graphicsLayer->platformLayer()->setTouchEventHandlerRegion(WebVector<WebRect>());
556 }
557 }
558 }
559
touchEventTargetRectsDidChange(const Document *)560 void ScrollingCoordinator::touchEventTargetRectsDidChange(const Document*)
561 {
562 if (!touchHitTestingEnabled())
563 return;
564
565 // Wait until after layout to update.
566 if (m_page->mainFrame()->view()->needsLayout())
567 return;
568
569 // FIXME: scheduleAnimation() is just a method of forcing the compositor to realize that it
570 // needs to commit here. We should expose a cleaner API for this.
571 RenderView* renderView = m_page->mainFrame()->contentRenderer();
572 if (renderView && renderView->compositor() && renderView->compositor()->inCompositingMode())
573 m_page->mainFrame()->view()->scheduleAnimation();
574
575 m_touchEventTargetRectsAreDirty = true;
576 }
577
updateScrollParentForGraphicsLayer(GraphicsLayer * child,RenderLayer * parent)578 void ScrollingCoordinator::updateScrollParentForGraphicsLayer(GraphicsLayer* child, RenderLayer* parent)
579 {
580 WebLayer* scrollParentWebLayer = 0;
581 if (parent && parent->hasCompositedLayerMapping())
582 scrollParentWebLayer = scrollingWebLayerForGraphicsLayer(parent->compositedLayerMapping()->parentForSublayers());
583
584 child->setScrollParent(scrollParentWebLayer);
585 }
586
updateClipParentForGraphicsLayer(GraphicsLayer * child,RenderLayer * parent)587 void ScrollingCoordinator::updateClipParentForGraphicsLayer(GraphicsLayer* child, RenderLayer* parent)
588 {
589 WebLayer* clipParentWebLayer = 0;
590 if (parent && parent->hasCompositedLayerMapping())
591 clipParentWebLayer = scrollingWebLayerForGraphicsLayer(parent->compositedLayerMapping()->parentForSublayers());
592
593 child->setClipParent(clipParentWebLayer);
594 }
595
willDestroyRenderLayer(RenderLayer * layer)596 void ScrollingCoordinator::willDestroyRenderLayer(RenderLayer* layer)
597 {
598 m_layersWithTouchRects.remove(layer);
599 }
600
setWheelEventHandlerCount(unsigned count)601 void ScrollingCoordinator::setWheelEventHandlerCount(unsigned count)
602 {
603 if (WebLayer* scrollLayer = scrollingWebLayerForScrollableArea(m_page->mainFrame()->view()))
604 scrollLayer->setHaveWheelEventHandlers(count > 0);
605 }
606
recomputeWheelEventHandlerCountForFrameView(FrameView *)607 void ScrollingCoordinator::recomputeWheelEventHandlerCountForFrameView(FrameView*)
608 {
609 setWheelEventHandlerCount(computeCurrentWheelEventHandlerCount());
610 }
611
setShouldUpdateScrollLayerPositionOnMainThread(MainThreadScrollingReasons reasons)612 void ScrollingCoordinator::setShouldUpdateScrollLayerPositionOnMainThread(MainThreadScrollingReasons reasons)
613 {
614 if (WebLayer* scrollLayer = scrollingWebLayerForScrollableArea(m_page->mainFrame()->view())) {
615 m_lastMainThreadScrollingReasons = reasons;
616 scrollLayer->setShouldScrollOnMainThread(reasons);
617 }
618 }
619
pageDestroyed()620 void ScrollingCoordinator::pageDestroyed()
621 {
622 ASSERT(m_page);
623 m_page = 0;
624 }
625
coordinatesScrollingForFrameView(FrameView * frameView) const626 bool ScrollingCoordinator::coordinatesScrollingForFrameView(FrameView* frameView) const
627 {
628 ASSERT(isMainThread());
629 ASSERT(m_page);
630
631 // We currently only handle the main frame.
632 if (&frameView->frame() != m_page->mainFrame())
633 return false;
634
635 // We currently only support composited mode.
636 RenderView* renderView = m_page->mainFrame()->contentRenderer();
637 if (!renderView)
638 return false;
639 return renderView->usesCompositing();
640 }
641
computeShouldHandleScrollGestureOnMainThreadRegion(const Frame * frame,const IntPoint & frameLocation) const642 Region ScrollingCoordinator::computeShouldHandleScrollGestureOnMainThreadRegion(const Frame* frame, const IntPoint& frameLocation) const
643 {
644 Region shouldHandleScrollGestureOnMainThreadRegion;
645 FrameView* frameView = frame->view();
646 if (!frameView)
647 return shouldHandleScrollGestureOnMainThreadRegion;
648
649 IntPoint offset = frameLocation;
650 offset.moveBy(frameView->frameRect().location());
651
652 if (const FrameView::ScrollableAreaSet* scrollableAreas = frameView->scrollableAreas()) {
653 for (FrameView::ScrollableAreaSet::const_iterator it = scrollableAreas->begin(), end = scrollableAreas->end(); it != end; ++it) {
654 ScrollableArea* scrollableArea = *it;
655 // Composited scrollable areas can be scrolled off the main thread.
656 if (scrollableArea->usesCompositedScrolling())
657 continue;
658 IntRect box = scrollableArea->scrollableAreaBoundingBox();
659 box.moveBy(offset);
660 shouldHandleScrollGestureOnMainThreadRegion.unite(box);
661 }
662 }
663
664 // We use GestureScrollBegin/Update/End for moving the resizer handle. So we mark these
665 // small resizer areas as non-fast-scrollable to allow the scroll gestures to be passed to
666 // main thread if they are targeting the resizer area. (Resizing is done in EventHandler.cpp
667 // on main thread).
668 if (const FrameView::ResizerAreaSet* resizerAreas = frameView->resizerAreas()) {
669 for (FrameView::ResizerAreaSet::const_iterator it = resizerAreas->begin(), end = resizerAreas->end(); it != end; ++it) {
670 RenderBox* box = *it;
671 IntRect bounds = box->absoluteBoundingBoxRect();
672 IntRect corner = box->layer()->scrollableArea()->touchResizerCornerRect(bounds);
673 corner.moveBy(offset);
674 shouldHandleScrollGestureOnMainThreadRegion.unite(corner);
675 }
676 }
677
678 if (const HashSet<RefPtr<Widget> >* children = frameView->children()) {
679 for (HashSet<RefPtr<Widget> >::const_iterator it = children->begin(), end = children->end(); it != end; ++it) {
680 if (!(*it)->isPluginView())
681 continue;
682
683 PluginView* pluginView = toPluginView((*it).get());
684 if (pluginView->wantsWheelEvents())
685 shouldHandleScrollGestureOnMainThreadRegion.unite(pluginView->frameRect());
686 }
687 }
688
689 const FrameTree& tree = frame->tree();
690 for (Frame* subFrame = tree.firstChild(); subFrame; subFrame = subFrame->tree().nextSibling())
691 shouldHandleScrollGestureOnMainThreadRegion.unite(computeShouldHandleScrollGestureOnMainThreadRegion(subFrame, offset));
692
693 return shouldHandleScrollGestureOnMainThreadRegion;
694 }
695
accumulateDocumentTouchEventTargetRects(LayerHitTestRects & rects,const Document * document)696 static void accumulateDocumentTouchEventTargetRects(LayerHitTestRects& rects, const Document* document)
697 {
698 ASSERT(document);
699 if (!document->touchEventTargets())
700 return;
701
702 const TouchEventTargetSet* targets = document->touchEventTargets();
703
704 // If there's a handler on the document, html or body element (fairly common in practice),
705 // then we can quickly mark the entire document and skip looking at any other handlers.
706 // Note that technically a handler on the body doesn't cover the whole document, but it's
707 // reasonable to be conservative and report the whole document anyway.
708 for (TouchEventTargetSet::const_iterator iter = targets->begin(); iter != targets->end(); ++iter) {
709 Node* target = iter->key;
710 if (target == document || target == document->documentElement() || target == document->body()) {
711 if (RenderObject* renderer = document->renderer()) {
712 renderer->computeLayerHitTestRects(rects);
713 }
714 return;
715 }
716 }
717
718 for (TouchEventTargetSet::const_iterator iter = targets->begin(); iter != targets->end(); ++iter) {
719 const Node* target = iter->key;
720 if (!target->inDocument())
721 continue;
722
723 if (target->isDocumentNode()) {
724 ASSERT(target != document);
725 accumulateDocumentTouchEventTargetRects(rects, toDocument(target));
726 } else if (RenderObject* renderer = target->renderer()) {
727 // If the set also contains one of our ancestor nodes then processing
728 // this node would be redundant.
729 bool hasTouchEventTargetAncestor = false;
730 for (Node* ancestor = target->parentNode(); ancestor && !hasTouchEventTargetAncestor; ancestor = ancestor->parentNode()) {
731 if (targets->contains(ancestor))
732 hasTouchEventTargetAncestor = true;
733 }
734 if (!hasTouchEventTargetAncestor)
735 renderer->computeLayerHitTestRects(rects);
736 }
737 }
738
739 }
740
computeTouchEventTargetRects(LayerHitTestRects & rects)741 void ScrollingCoordinator::computeTouchEventTargetRects(LayerHitTestRects& rects)
742 {
743 TRACE_EVENT0("input", "ScrollingCoordinator::computeTouchEventTargetRects");
744 ASSERT(touchHitTestingEnabled());
745
746 Document* document = m_page->mainFrame()->document();
747 if (!document || !document->view())
748 return;
749
750 accumulateDocumentTouchEventTargetRects(rects, document);
751 }
752
computeCurrentWheelEventHandlerCount()753 unsigned ScrollingCoordinator::computeCurrentWheelEventHandlerCount()
754 {
755 unsigned wheelEventHandlerCount = 0;
756
757 for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree().traverseNext()) {
758 if (frame->document())
759 wheelEventHandlerCount += WheelController::from(frame->document())->wheelEventHandlerCount();
760 }
761
762 return wheelEventHandlerCount;
763 }
764
frameViewWheelEventHandlerCountChanged(FrameView * frameView)765 void ScrollingCoordinator::frameViewWheelEventHandlerCountChanged(FrameView* frameView)
766 {
767 ASSERT(isMainThread());
768 ASSERT(m_page);
769
770 recomputeWheelEventHandlerCountForFrameView(frameView);
771 }
772
frameViewHasSlowRepaintObjectsDidChange(FrameView * frameView)773 void ScrollingCoordinator::frameViewHasSlowRepaintObjectsDidChange(FrameView* frameView)
774 {
775 ASSERT(isMainThread());
776 ASSERT(m_page);
777
778 if (!coordinatesScrollingForFrameView(frameView))
779 return;
780
781 updateShouldUpdateScrollLayerPositionOnMainThread();
782 }
783
frameViewFixedObjectsDidChange(FrameView * frameView)784 void ScrollingCoordinator::frameViewFixedObjectsDidChange(FrameView* frameView)
785 {
786 ASSERT(isMainThread());
787 ASSERT(m_page);
788
789 if (!coordinatesScrollingForFrameView(frameView))
790 return;
791
792 updateShouldUpdateScrollLayerPositionOnMainThread();
793 }
794
scrollLayerForScrollableArea(ScrollableArea * scrollableArea)795 GraphicsLayer* ScrollingCoordinator::scrollLayerForScrollableArea(ScrollableArea* scrollableArea)
796 {
797 return scrollableArea->layerForScrolling();
798 }
799
horizontalScrollbarLayerForScrollableArea(ScrollableArea * scrollableArea)800 GraphicsLayer* ScrollingCoordinator::horizontalScrollbarLayerForScrollableArea(ScrollableArea* scrollableArea)
801 {
802 return scrollableArea->layerForHorizontalScrollbar();
803 }
804
verticalScrollbarLayerForScrollableArea(ScrollableArea * scrollableArea)805 GraphicsLayer* ScrollingCoordinator::verticalScrollbarLayerForScrollableArea(ScrollableArea* scrollableArea)
806 {
807 return scrollableArea->layerForVerticalScrollbar();
808 }
809
isForMainFrame(ScrollableArea * scrollableArea) const810 bool ScrollingCoordinator::isForMainFrame(ScrollableArea* scrollableArea) const
811 {
812 return scrollableArea == m_page->mainFrame()->view();
813 }
814
scrollLayerForFrameView(FrameView * frameView)815 GraphicsLayer* ScrollingCoordinator::scrollLayerForFrameView(FrameView* frameView)
816 {
817 RenderView* renderView = frameView->frame().contentRenderer();
818 if (!renderView)
819 return 0;
820 return renderView->compositor()->scrollLayer();
821 }
822
counterScrollingLayerForFrameView(FrameView *)823 GraphicsLayer* ScrollingCoordinator::counterScrollingLayerForFrameView(FrameView*)
824 {
825 return 0;
826 }
827
frameViewRootLayerDidChange(FrameView * frameView)828 void ScrollingCoordinator::frameViewRootLayerDidChange(FrameView* frameView)
829 {
830 ASSERT(isMainThread());
831 ASSERT(m_page);
832
833 if (!coordinatesScrollingForFrameView(frameView))
834 return;
835
836 notifyLayoutUpdated();
837 recomputeWheelEventHandlerCountForFrameView(frameView);
838 updateShouldUpdateScrollLayerPositionOnMainThread();
839 }
840
841 #if OS(MACOSX)
handleWheelEventPhase(PlatformWheelEventPhase phase)842 void ScrollingCoordinator::handleWheelEventPhase(PlatformWheelEventPhase phase)
843 {
844 ASSERT(isMainThread());
845
846 if (!m_page)
847 return;
848
849 FrameView* frameView = m_page->mainFrame()->view();
850 if (!frameView)
851 return;
852
853 frameView->scrollAnimator()->handleWheelEventPhase(phase);
854 }
855 #endif
856
hasVisibleSlowRepaintViewportConstrainedObjects(FrameView * frameView) const857 bool ScrollingCoordinator::hasVisibleSlowRepaintViewportConstrainedObjects(FrameView* frameView) const
858 {
859 const FrameView::ViewportConstrainedObjectSet* viewportConstrainedObjects = frameView->viewportConstrainedObjects();
860 if (!viewportConstrainedObjects)
861 return false;
862
863 for (FrameView::ViewportConstrainedObjectSet::const_iterator it = viewportConstrainedObjects->begin(), end = viewportConstrainedObjects->end(); it != end; ++it) {
864 RenderObject* viewportConstrainedObject = *it;
865 if (!viewportConstrainedObject->isBoxModelObject() || !viewportConstrainedObject->hasLayer())
866 return true;
867 RenderLayer* layer = toRenderBoxModelObject(viewportConstrainedObject)->layer();
868 // Any explicit reason that a fixed position element is not composited shouldn't cause slow scrolling.
869 if (layer->compositingState() != PaintsIntoOwnBacking && layer->viewportConstrainedNotCompositedReason() == RenderLayer::NoNotCompositedReason)
870 return true;
871
872 // Composited layers that actually paint into their enclosing ancestor
873 // must also force main thread scrolling.
874 if (layer->compositingState() == HasOwnBackingButPaintsIntoAncestor)
875 return true;
876 }
877 return false;
878 }
879
mainThreadScrollingReasons() const880 MainThreadScrollingReasons ScrollingCoordinator::mainThreadScrollingReasons() const
881 {
882 // The main thread scrolling reasons are applicable to scrolls of the main
883 // frame. If it does not exist or if it is not scrollable, there is no
884 // reason to force main thread scrolling.
885 FrameView* frameView = m_page->mainFrame()->view();
886 if (!frameView)
887 return static_cast<MainThreadScrollingReasons>(0);
888
889 MainThreadScrollingReasons mainThreadScrollingReasons = (MainThreadScrollingReasons)0;
890
891 if (frameView->hasSlowRepaintObjects())
892 mainThreadScrollingReasons |= HasSlowRepaintObjects;
893 if (hasVisibleSlowRepaintViewportConstrainedObjects(frameView))
894 mainThreadScrollingReasons |= HasNonLayerViewportConstrainedObjects;
895
896 return mainThreadScrollingReasons;
897 }
898
updateShouldUpdateScrollLayerPositionOnMainThread()899 void ScrollingCoordinator::updateShouldUpdateScrollLayerPositionOnMainThread()
900 {
901 setShouldUpdateScrollLayerPositionOnMainThread(mainThreadScrollingReasons());
902 }
903
mainThreadScrollingReasonsAsText(MainThreadScrollingReasons reasons)904 String ScrollingCoordinator::mainThreadScrollingReasonsAsText(MainThreadScrollingReasons reasons)
905 {
906 StringBuilder stringBuilder;
907
908 if (reasons & ScrollingCoordinator::HasSlowRepaintObjects)
909 stringBuilder.append("Has slow repaint objects, ");
910 if (reasons & ScrollingCoordinator::HasViewportConstrainedObjectsWithoutSupportingFixedLayers)
911 stringBuilder.append("Has viewport constrained objects without supporting fixed layers, ");
912 if (reasons & ScrollingCoordinator::HasNonLayerViewportConstrainedObjects)
913 stringBuilder.append("Has non-layer viewport-constrained objects, ");
914
915 if (stringBuilder.length())
916 stringBuilder.resize(stringBuilder.length() - 2);
917 return stringBuilder.toString();
918 }
919
mainThreadScrollingReasonsAsText() const920 String ScrollingCoordinator::mainThreadScrollingReasonsAsText() const
921 {
922 return mainThreadScrollingReasonsAsText(m_lastMainThreadScrollingReasons);
923 }
924
frameViewIsScrollableIsDirty() const925 bool ScrollingCoordinator::frameViewIsScrollableIsDirty() const
926 {
927 FrameView* frameView = m_page->mainFrame()->view();
928 bool frameIsScrollable = frameView && frameView->isScrollable();
929 return frameIsScrollable != m_wasFrameScrollable;
930 }
931
932 } // namespace WebCore
933