2 * Copyright (C) 2011 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
28 #include "ScrollingCoordinator.h"
32 #include "FrameView.h"
33 #include "GraphicsLayer.h"
36 #include "PlatformWheelEvent.h"
37 #include "PluginViewBase.h"
39 #include "RenderView.h"
40 #include "ScrollAnimator.h"
41 #include <wtf/MainThread.h>
43 #if USE(ACCELERATED_COMPOSITING)
44 #include "RenderLayerCompositor.h"
47 #if ENABLE(THREADED_SCROLLING)
48 #include "ScrollingCoordinatorMac.h"
51 #if PLATFORM(CHROMIUM)
52 #include "ScrollingCoordinatorChromium.h"
55 #if USE(COORDINATED_GRAPHICS)
56 #include "ScrollingCoordinatorCoordinatedGraphics.h"
61 PassRefPtr<ScrollingCoordinator> ScrollingCoordinator::create(Page* page)
63 #if USE(ACCELERATED_COMPOSITING) && ENABLE(THREADED_SCROLLING)
64 return adoptRef(new ScrollingCoordinatorMac(page));
67 #if PLATFORM(CHROMIUM)
68 return adoptRef(new ScrollingCoordinatorChromium(page));
71 #if USE(COORDINATED_GRAPHICS)
72 return adoptRef(new ScrollingCoordinatorCoordinatedGraphics(page));
75 return adoptRef(new ScrollingCoordinator(page));
78 ScrollingCoordinator::ScrollingCoordinator(Page* page)
80 , m_updateMainFrameScrollPositionTimer(this, &ScrollingCoordinator::updateMainFrameScrollPositionTimerFired)
81 , m_scheduledUpdateIsProgrammaticScroll(false)
82 , m_scheduledScrollingLayerPositionAction(SyncScrollingLayerPosition)
83 , m_forceMainThreadScrollLayerPositionUpdates(false)
87 ScrollingCoordinator::~ScrollingCoordinator()
92 void ScrollingCoordinator::pageDestroyed()
98 bool ScrollingCoordinator::coordinatesScrollingForFrameView(FrameView* frameView) const
100 ASSERT(isMainThread());
103 // We currently only handle the main frame.
104 if (frameView->frame() != m_page->mainFrame())
107 // We currently only support composited mode.
108 #if USE(ACCELERATED_COMPOSITING)
109 RenderView* renderView = m_page->mainFrame()->contentRenderer();
112 return renderView->usesCompositing();
118 Region ScrollingCoordinator::computeNonFastScrollableRegion(const Frame* frame, const IntPoint& frameLocation) const
120 Region nonFastScrollableRegion;
121 FrameView* frameView = frame->view();
123 return nonFastScrollableRegion;
125 IntPoint offset = frameLocation;
126 offset.moveBy(frameView->frameRect().location());
128 if (const FrameView::ScrollableAreaSet* scrollableAreas = frameView->scrollableAreas()) {
129 for (FrameView::ScrollableAreaSet::const_iterator it = scrollableAreas->begin(), end = scrollableAreas->end(); it != end; ++it) {
130 ScrollableArea* scrollableArea = *it;
131 #if USE(ACCELERATED_COMPOSITING)
132 // Composited scrollable areas can be scrolled off the main thread.
133 if (scrollableArea->usesCompositedScrolling())
136 IntRect box = scrollableArea->scrollableAreaBoundingBox();
138 nonFastScrollableRegion.unite(box);
142 if (const HashSet<RefPtr<Widget> >* children = frameView->children()) {
143 for (HashSet<RefPtr<Widget> >::const_iterator it = children->begin(), end = children->end(); it != end; ++it) {
144 if (!(*it)->isPluginViewBase())
147 PluginViewBase* pluginViewBase = static_cast<PluginViewBase*>((*it).get());
148 if (pluginViewBase->wantsWheelEvents())
149 nonFastScrollableRegion.unite(pluginViewBase->frameRect());
153 FrameTree* tree = frame->tree();
154 for (Frame* subFrame = tree->firstChild(); subFrame; subFrame = subFrame->tree()->nextSibling())
155 nonFastScrollableRegion.unite(computeNonFastScrollableRegion(subFrame, offset));
157 return nonFastScrollableRegion;
160 #if ENABLE(TOUCH_EVENT_TRACKING)
161 static void accumulateRendererTouchEventTargetRects(Vector<IntRect>& rects, const RenderObject* renderer, const IntRect& parentRect = IntRect())
163 IntRect adjustedParentRect = parentRect;
164 if (parentRect.isEmpty() || renderer->isFloating() || renderer->isPositioned() || renderer->hasTransform()) {
165 // FIXME: This method is O(N^2) as it walks the tree to the root for every renderer. RenderGeometryMap would fix this.
166 IntRect r = enclosingIntRect(renderer->clippedOverflowRectForRepaint(0));
168 // Convert to the top-level view's coordinates.
169 ASSERT(renderer->document()->view());
170 r = renderer->document()->view()->convertToRootView(r);
172 if (!parentRect.contains(r)) {
174 adjustedParentRect = r;
179 for (RenderObject* child = renderer->firstChild(); child; child = child->nextSibling())
180 accumulateRendererTouchEventTargetRects(rects, child, adjustedParentRect);
183 static void accumulateDocumentEventTargetRects(Vector<IntRect>& rects, const Document* document)
186 if (!document->touchEventTargets())
189 const TouchEventTargetSet* targets = document->touchEventTargets();
190 for (TouchEventTargetSet::const_iterator iter = targets->begin(); iter != targets->end(); ++iter) {
191 const Node* touchTarget = iter->key;
192 if (!touchTarget->inDocument())
195 if (touchTarget == document) {
196 if (RenderView* view = document->renderView()) {
198 if (touchTarget == document->topDocument())
199 r = view->documentRect();
201 r = enclosingIntRect(view->clippedOverflowRectForRepaint(0));
204 ASSERT(view->document()->view());
205 r = view->document()->view()->convertToRootView(r);
212 if (touchTarget->isDocumentNode() && touchTarget != document) {
213 accumulateDocumentEventTargetRects(rects, static_cast<const Document*>(touchTarget));
217 if (RenderObject* renderer = touchTarget->renderer())
218 accumulateRendererTouchEventTargetRects(rects, renderer);
222 void ScrollingCoordinator::computeAbsoluteTouchEventTargetRects(const Document* document, Vector<IntRect>& rects)
225 if (!document->view())
228 // FIXME: These rects won't be properly updated if the renderers are in a sub-tree that scrolls.
229 accumulateDocumentEventTargetRects(rects, document);
233 unsigned ScrollingCoordinator::computeCurrentWheelEventHandlerCount()
235 unsigned wheelEventHandlerCount = 0;
237 for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
238 if (frame->document())
239 wheelEventHandlerCount += frame->document()->wheelEventHandlerCount();
242 return wheelEventHandlerCount;
245 void ScrollingCoordinator::frameViewWheelEventHandlerCountChanged(FrameView* frameView)
247 ASSERT(isMainThread());
250 recomputeWheelEventHandlerCountForFrameView(frameView);
253 void ScrollingCoordinator::frameViewHasSlowRepaintObjectsDidChange(FrameView* frameView)
255 ASSERT(isMainThread());
258 if (!coordinatesScrollingForFrameView(frameView))
261 updateShouldUpdateScrollLayerPositionOnMainThread();
264 void ScrollingCoordinator::frameViewFixedObjectsDidChange(FrameView* frameView)
266 ASSERT(isMainThread());
269 if (!coordinatesScrollingForFrameView(frameView))
272 updateShouldUpdateScrollLayerPositionOnMainThread();
275 GraphicsLayer* ScrollingCoordinator::scrollLayerForFrameView(FrameView* frameView)
277 #if USE(ACCELERATED_COMPOSITING)
278 Frame* frame = frameView->frame();
282 RenderView* renderView = frame->contentRenderer();
285 return renderView->compositor()->scrollLayer();
287 UNUSED_PARAM(frameView);
292 GraphicsLayer* ScrollingCoordinator::counterScrollingLayerForFrameView(FrameView* frameView)
294 #if USE(ACCELERATED_COMPOSITING)
295 Frame* frame = frameView->frame();
299 RenderView* renderView = frame->contentRenderer();
302 return renderView->compositor()->fixedRootBackgroundLayer();
304 UNUSED_PARAM(frameView);
309 void ScrollingCoordinator::frameViewRootLayerDidChange(FrameView* frameView)
311 ASSERT(isMainThread());
314 if (!coordinatesScrollingForFrameView(frameView))
317 frameViewLayoutUpdated(frameView);
318 recomputeWheelEventHandlerCountForFrameView(frameView);
319 updateShouldUpdateScrollLayerPositionOnMainThread();
322 void ScrollingCoordinator::scheduleUpdateMainFrameScrollPosition(const IntPoint& scrollPosition, bool programmaticScroll, SetOrSyncScrollingLayerPosition scrollingLayerPositionAction)
324 if (m_updateMainFrameScrollPositionTimer.isActive()) {
325 if (m_scheduledUpdateIsProgrammaticScroll == programmaticScroll
326 && m_scheduledScrollingLayerPositionAction == scrollingLayerPositionAction) {
327 m_scheduledUpdateScrollPosition = scrollPosition;
331 // If the parameters don't match what was previosly scheduled, dispatch immediately.
332 m_updateMainFrameScrollPositionTimer.stop();
333 updateMainFrameScrollPosition(m_scheduledUpdateScrollPosition, m_scheduledUpdateIsProgrammaticScroll, m_scheduledScrollingLayerPositionAction);
334 updateMainFrameScrollPosition(scrollPosition, programmaticScroll, scrollingLayerPositionAction);
338 m_scheduledUpdateScrollPosition = scrollPosition;
339 m_scheduledUpdateIsProgrammaticScroll = programmaticScroll;
340 m_scheduledScrollingLayerPositionAction = scrollingLayerPositionAction;
341 m_updateMainFrameScrollPositionTimer.startOneShot(0);
344 void ScrollingCoordinator::updateMainFrameScrollPositionTimerFired(Timer<ScrollingCoordinator>*)
346 updateMainFrameScrollPosition(m_scheduledUpdateScrollPosition, m_scheduledUpdateIsProgrammaticScroll, m_scheduledScrollingLayerPositionAction);
349 void ScrollingCoordinator::updateMainFrameScrollPosition(const IntPoint& scrollPosition, bool programmaticScroll, SetOrSyncScrollingLayerPosition scrollingLayerPositionAction)
351 ASSERT(isMainThread());
356 FrameView* frameView = m_page->mainFrame()->view();
360 bool oldProgrammaticScroll = frameView->inProgrammaticScroll();
361 frameView->setInProgrammaticScroll(programmaticScroll);
363 frameView->setConstrainsScrollingToContentEdge(false);
364 frameView->notifyScrollPositionChanged(scrollPosition);
365 frameView->setConstrainsScrollingToContentEdge(true);
367 frameView->setInProgrammaticScroll(oldProgrammaticScroll);
369 #if USE(ACCELERATED_COMPOSITING)
370 if (GraphicsLayer* scrollLayer = scrollLayerForFrameView(frameView)) {
371 GraphicsLayer* counterScrollingLayer = counterScrollingLayerForFrameView(frameView);
372 if (programmaticScroll || scrollingLayerPositionAction == SetScrollingLayerPosition) {
373 scrollLayer->setPosition(-frameView->scrollPosition());
374 if (counterScrollingLayer)
375 counterScrollingLayer->setPosition(IntPoint(frameView->scrollOffsetForFixedPosition()));
377 scrollLayer->syncPosition(-frameView->scrollPosition());
378 if (counterScrollingLayer)
379 counterScrollingLayer->syncPosition(IntPoint(frameView->scrollOffsetForFixedPosition()));
381 LayoutRect viewportRect = frameView->viewportConstrainedVisibleContentRect();
382 syncChildPositions(viewportRect);
386 UNUSED_PARAM(scrollingLayerPositionAction);
390 #if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN))
391 void ScrollingCoordinator::handleWheelEventPhase(PlatformWheelEventPhase phase)
393 ASSERT(isMainThread());
398 FrameView* frameView = m_page->mainFrame()->view();
402 frameView->scrollAnimator()->handleWheelEventPhase(phase);
406 bool ScrollingCoordinator::hasVisibleSlowRepaintViewportConstrainedObjects(FrameView* frameView) const
408 const FrameView::ViewportConstrainedObjectSet* viewportConstrainedObjects = frameView->viewportConstrainedObjects();
409 if (!viewportConstrainedObjects)
412 #if USE(ACCELERATED_COMPOSITING)
413 for (FrameView::ViewportConstrainedObjectSet::const_iterator it = viewportConstrainedObjects->begin(), end = viewportConstrainedObjects->end(); it != end; ++it) {
414 RenderObject* viewportConstrainedObject = *it;
415 if (!viewportConstrainedObject->isBoxModelObject() || !viewportConstrainedObject->hasLayer())
417 RenderLayer* layer = toRenderBoxModelObject(viewportConstrainedObject)->layer();
418 // Any explicit reason that a fixed position element is not composited shouldn't cause slow scrolling.
419 if (!layer->isComposited() && layer->viewportConstrainedNotCompositedReason() == RenderLayer::NoNotCompositedReason)
424 return viewportConstrainedObjects->size();
428 MainThreadScrollingReasons ScrollingCoordinator::mainThreadScrollingReasons() const
430 FrameView* frameView = m_page->mainFrame()->view();
432 return static_cast<MainThreadScrollingReasons>(0);
434 MainThreadScrollingReasons mainThreadScrollingReasons = (MainThreadScrollingReasons)0;
436 if (m_forceMainThreadScrollLayerPositionUpdates)
437 mainThreadScrollingReasons |= ForcedOnMainThread;
438 if (frameView->hasSlowRepaintObjects())
439 mainThreadScrollingReasons |= HasSlowRepaintObjects;
440 if (!supportsFixedPositionLayers() && frameView->hasViewportConstrainedObjects())
441 mainThreadScrollingReasons |= HasViewportConstrainedObjectsWithoutSupportingFixedLayers;
442 if (supportsFixedPositionLayers() && hasVisibleSlowRepaintViewportConstrainedObjects(frameView))
443 mainThreadScrollingReasons |= HasNonLayerViewportConstrainedObjects;
444 if (m_page->mainFrame()->document()->isImageDocument())
445 mainThreadScrollingReasons |= IsImageDocument;
447 return mainThreadScrollingReasons;
450 void ScrollingCoordinator::updateShouldUpdateScrollLayerPositionOnMainThread()
452 setShouldUpdateScrollLayerPositionOnMainThread(mainThreadScrollingReasons());
455 void ScrollingCoordinator::setForceMainThreadScrollLayerPositionUpdates(bool forceMainThreadScrollLayerPositionUpdates)
457 if (m_forceMainThreadScrollLayerPositionUpdates == forceMainThreadScrollLayerPositionUpdates)
460 m_forceMainThreadScrollLayerPositionUpdates = forceMainThreadScrollLayerPositionUpdates;
461 updateShouldUpdateScrollLayerPositionOnMainThread();
464 ScrollingNodeID ScrollingCoordinator::uniqueScrollLayerID()
466 static ScrollingNodeID uniqueScrollLayerID = 1;
467 return uniqueScrollLayerID++;
470 String ScrollingCoordinator::scrollingStateTreeAsText() const
475 String ScrollingCoordinator::mainThreadScrollingReasonsAsText(MainThreadScrollingReasons reasons)
477 StringBuilder stringBuilder;
479 if (reasons & ScrollingCoordinator::ForcedOnMainThread)
480 stringBuilder.append("Forced on main thread, ");
481 if (reasons & ScrollingCoordinator::HasSlowRepaintObjects)
482 stringBuilder.append("Has slow repaint objects, ");
483 if (reasons & ScrollingCoordinator::HasViewportConstrainedObjectsWithoutSupportingFixedLayers)
484 stringBuilder.append("Has viewport constrained objects without supporting fixed layers, ");
485 if (reasons & ScrollingCoordinator::HasNonLayerViewportConstrainedObjects)
486 stringBuilder.append("Has non-layer viewport-constrained objects, ");
487 if (reasons & ScrollingCoordinator::IsImageDocument)
488 stringBuilder.append("Is image document, ");
490 if (stringBuilder.length())
491 stringBuilder.resize(stringBuilder.length() - 2);
492 return stringBuilder.toString();
495 String ScrollingCoordinator::mainThreadScrollingReasonsAsText() const
497 return mainThreadScrollingReasonsAsText(mainThreadScrollingReasons());
500 } // namespace WebCore