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