e19657251d48707877bf3c3bbb197426f1661583
[WebKit-https.git] / Source / WebCore / page / scrolling / ScrollingCoordinator.cpp
1 /*
2  * Copyright (C) 2011 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27
28 #include "ScrollingCoordinator.h"
29
30 #include "Document.h"
31 #include "Frame.h"
32 #include "FrameView.h"
33 #include "GraphicsLayer.h"
34 #include "IntRect.h"
35 #include "Page.h"
36 #include "PlatformWheelEvent.h"
37 #include "PluginViewBase.h"
38 #include "Region.h"
39 #include "RenderView.h"
40 #include "ScrollAnimator.h"
41 #include <wtf/MainThread.h>
42
43 #if USE(ACCELERATED_COMPOSITING)
44 #include "RenderLayerCompositor.h"
45 #endif
46
47 #if ENABLE(THREADED_SCROLLING)
48 #include "ScrollingCoordinatorMac.h"
49 #endif
50
51 #if PLATFORM(CHROMIUM)
52 #include "ScrollingCoordinatorChromium.h"
53 #endif
54
55 #if USE(COORDINATED_GRAPHICS)
56 #include "ScrollingCoordinatorCoordinatedGraphics.h"
57 #endif
58
59 namespace WebCore {
60
61 PassRefPtr<ScrollingCoordinator> ScrollingCoordinator::create(Page* page)
62 {
63 #if USE(ACCELERATED_COMPOSITING) && ENABLE(THREADED_SCROLLING)
64     return adoptRef(new ScrollingCoordinatorMac(page));
65 #endif
66
67 #if PLATFORM(CHROMIUM)
68     return adoptRef(new ScrollingCoordinatorChromium(page));
69 #endif
70
71 #if USE(COORDINATED_GRAPHICS)
72     return adoptRef(new ScrollingCoordinatorCoordinatedGraphics(page));
73 #endif
74
75     return adoptRef(new ScrollingCoordinator(page));
76 }
77
78 ScrollingCoordinator::ScrollingCoordinator(Page* page)
79     : m_page(page)
80     , m_updateMainFrameScrollPositionTimer(this, &ScrollingCoordinator::updateMainFrameScrollPositionTimerFired)
81     , m_scheduledUpdateIsProgrammaticScroll(false)
82     , m_scheduledScrollingLayerPositionAction(SyncScrollingLayerPosition)
83     , m_forceMainThreadScrollLayerPositionUpdates(false)
84 {
85 }
86
87 ScrollingCoordinator::~ScrollingCoordinator()
88 {
89     ASSERT(!m_page);
90 }
91
92 void ScrollingCoordinator::pageDestroyed()
93 {
94     ASSERT(m_page);
95     m_page = 0;
96 }
97
98 bool ScrollingCoordinator::coordinatesScrollingForFrameView(FrameView* frameView) const
99 {
100     ASSERT(isMainThread());
101     ASSERT(m_page);
102
103     // We currently only handle the main frame.
104     if (frameView->frame() != m_page->mainFrame())
105         return false;
106
107     // We currently only support composited mode.
108 #if USE(ACCELERATED_COMPOSITING)
109     RenderView* renderView = m_page->mainFrame()->contentRenderer();
110     if (!renderView)
111         return false;
112     return renderView->usesCompositing();
113 #else
114     return false;
115 #endif
116 }
117
118 Region ScrollingCoordinator::computeNonFastScrollableRegion(const Frame* frame, const IntPoint& frameLocation) const
119 {
120     Region nonFastScrollableRegion;
121     FrameView* frameView = frame->view();
122     if (!frameView)
123         return nonFastScrollableRegion;
124
125     IntPoint offset = frameLocation;
126     offset.moveBy(frameView->frameRect().location());
127
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())
134                 continue;
135 #endif
136             IntRect box = scrollableArea->scrollableAreaBoundingBox();
137             box.moveBy(offset);
138             nonFastScrollableRegion.unite(box);
139         }
140     }
141
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())
145                 continue;
146
147             PluginViewBase* pluginViewBase = static_cast<PluginViewBase*>((*it).get());
148             if (pluginViewBase->wantsWheelEvents())
149                 nonFastScrollableRegion.unite(pluginViewBase->frameRect());
150         }
151     }
152
153     FrameTree* tree = frame->tree();
154     for (Frame* subFrame = tree->firstChild(); subFrame; subFrame = subFrame->tree()->nextSibling())
155         nonFastScrollableRegion.unite(computeNonFastScrollableRegion(subFrame, offset));
156
157     return nonFastScrollableRegion;
158 }
159
160 #if ENABLE(TOUCH_EVENT_TRACKING)
161 static void accumulateRendererTouchEventTargetRects(Vector<IntRect>& rects, const RenderObject* renderer, const IntRect& parentRect = IntRect())
162 {
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));
167         if (!r.isEmpty()) {
168             // Convert to the top-level view's coordinates.
169             ASSERT(renderer->document()->view());
170             r = renderer->document()->view()->convertToRootView(r);
171
172             if (!parentRect.contains(r)) {
173                 rects.append(r);
174                 adjustedParentRect = r;
175             }
176         }
177     }
178
179     for (RenderObject* child = renderer->firstChild(); child; child = child->nextSibling())
180         accumulateRendererTouchEventTargetRects(rects, child, adjustedParentRect);
181 }
182
183 static void accumulateDocumentEventTargetRects(Vector<IntRect>& rects, const Document* document)
184 {
185     ASSERT(document);
186     if (!document->touchEventTargets())
187         return;
188
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())
193             continue;
194
195         if (touchTarget == document) {
196             if (RenderView* view = document->renderView()) {
197                 IntRect r;
198                 if (touchTarget == document->topDocument())
199                     r = view->documentRect();
200                 else
201                     r = enclosingIntRect(view->clippedOverflowRectForRepaint(0));
202
203                 if (!r.isEmpty()) {
204                     ASSERT(view->document()->view());
205                     r = view->document()->view()->convertToRootView(r);
206                     rects.append(r);
207                 }
208             }
209             return;
210         }
211
212         if (touchTarget->isDocumentNode() && touchTarget != document) {
213             accumulateDocumentEventTargetRects(rects, static_cast<const Document*>(touchTarget));
214             continue;
215         }
216
217         if (RenderObject* renderer = touchTarget->renderer())
218             accumulateRendererTouchEventTargetRects(rects, renderer);
219     }
220 }
221
222 void ScrollingCoordinator::computeAbsoluteTouchEventTargetRects(const Document* document, Vector<IntRect>& rects)
223 {
224     ASSERT(document);
225     if (!document->view())
226         return;
227
228     // FIXME: These rects won't be properly updated if the renderers are in a sub-tree that scrolls.
229     accumulateDocumentEventTargetRects(rects, document);
230 }
231 #endif
232
233 unsigned ScrollingCoordinator::computeCurrentWheelEventHandlerCount()
234 {
235     unsigned wheelEventHandlerCount = 0;
236
237     for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
238         if (frame->document())
239             wheelEventHandlerCount += frame->document()->wheelEventHandlerCount();
240     }
241
242     return wheelEventHandlerCount;
243 }
244
245 void ScrollingCoordinator::frameViewWheelEventHandlerCountChanged(FrameView* frameView)
246 {
247     ASSERT(isMainThread());
248     ASSERT(m_page);
249
250     recomputeWheelEventHandlerCountForFrameView(frameView);
251 }
252
253 void ScrollingCoordinator::frameViewHasSlowRepaintObjectsDidChange(FrameView* frameView)
254 {
255     ASSERT(isMainThread());
256     ASSERT(m_page);
257
258     if (!coordinatesScrollingForFrameView(frameView))
259         return;
260
261     updateShouldUpdateScrollLayerPositionOnMainThread();
262 }
263
264 void ScrollingCoordinator::frameViewFixedObjectsDidChange(FrameView* frameView)
265 {
266     ASSERT(isMainThread());
267     ASSERT(m_page);
268
269     if (!coordinatesScrollingForFrameView(frameView))
270         return;
271
272     updateShouldUpdateScrollLayerPositionOnMainThread();
273 }
274
275 #if USE(ACCELERATED_COMPOSITING)
276 GraphicsLayer* ScrollingCoordinator::scrollLayerForScrollableArea(ScrollableArea* scrollableArea)
277 {
278     return scrollableArea->layerForScrolling();
279 }
280
281 GraphicsLayer* ScrollingCoordinator::horizontalScrollbarLayerForScrollableArea(ScrollableArea* scrollableArea)
282 {
283     return scrollableArea->layerForHorizontalScrollbar();
284 }
285
286 GraphicsLayer* ScrollingCoordinator::verticalScrollbarLayerForScrollableArea(ScrollableArea* scrollableArea)
287 {
288     return scrollableArea->layerForVerticalScrollbar();
289 }
290 #endif
291
292 GraphicsLayer* ScrollingCoordinator::scrollLayerForFrameView(FrameView* frameView)
293 {
294 #if USE(ACCELERATED_COMPOSITING)
295     Frame* frame = frameView->frame();
296     if (!frame)
297         return 0;
298
299     RenderView* renderView = frame->contentRenderer();
300     if (!renderView)
301         return 0;
302     return renderView->compositor()->scrollLayer();
303 #else
304     UNUSED_PARAM(frameView);
305     return 0;
306 #endif
307 }
308
309 GraphicsLayer* ScrollingCoordinator::counterScrollingLayerForFrameView(FrameView* frameView)
310 {
311 #if USE(ACCELERATED_COMPOSITING)
312     Frame* frame = frameView->frame();
313     if (!frame)
314         return 0;
315
316     RenderView* renderView = frame->contentRenderer();
317     if (!renderView)
318         return 0;
319     return renderView->compositor()->fixedRootBackgroundLayer();
320 #else
321     UNUSED_PARAM(frameView);
322     return 0;
323 #endif
324 }
325
326 void ScrollingCoordinator::frameViewRootLayerDidChange(FrameView* frameView)
327 {
328     ASSERT(isMainThread());
329     ASSERT(m_page);
330
331     if (!coordinatesScrollingForFrameView(frameView))
332         return;
333
334     frameViewLayoutUpdated(frameView);
335     recomputeWheelEventHandlerCountForFrameView(frameView);
336     updateShouldUpdateScrollLayerPositionOnMainThread();
337 }
338
339 void ScrollingCoordinator::scheduleUpdateMainFrameScrollPosition(const IntPoint& scrollPosition, bool programmaticScroll, SetOrSyncScrollingLayerPosition scrollingLayerPositionAction)
340 {
341     if (m_updateMainFrameScrollPositionTimer.isActive()) {
342         if (m_scheduledUpdateIsProgrammaticScroll == programmaticScroll
343             && m_scheduledScrollingLayerPositionAction == scrollingLayerPositionAction) {
344             m_scheduledUpdateScrollPosition = scrollPosition;
345             return;
346         }
347     
348         // If the parameters don't match what was previosly scheduled, dispatch immediately.
349         m_updateMainFrameScrollPositionTimer.stop();
350         updateMainFrameScrollPosition(m_scheduledUpdateScrollPosition, m_scheduledUpdateIsProgrammaticScroll, m_scheduledScrollingLayerPositionAction);
351         updateMainFrameScrollPosition(scrollPosition, programmaticScroll, scrollingLayerPositionAction);
352         return;
353     }
354
355     m_scheduledUpdateScrollPosition = scrollPosition;
356     m_scheduledUpdateIsProgrammaticScroll = programmaticScroll;
357     m_scheduledScrollingLayerPositionAction = scrollingLayerPositionAction;
358     m_updateMainFrameScrollPositionTimer.startOneShot(0);
359 }
360
361 void ScrollingCoordinator::updateMainFrameScrollPositionTimerFired(Timer<ScrollingCoordinator>*)
362 {
363     updateMainFrameScrollPosition(m_scheduledUpdateScrollPosition, m_scheduledUpdateIsProgrammaticScroll, m_scheduledScrollingLayerPositionAction);
364 }
365
366 void ScrollingCoordinator::updateMainFrameScrollPosition(const IntPoint& scrollPosition, bool programmaticScroll, SetOrSyncScrollingLayerPosition scrollingLayerPositionAction)
367 {
368     ASSERT(isMainThread());
369
370     if (!m_page)
371         return;
372
373     FrameView* frameView = m_page->mainFrame()->view();
374     if (!frameView)
375         return;
376
377     bool oldProgrammaticScroll = frameView->inProgrammaticScroll();
378     frameView->setInProgrammaticScroll(programmaticScroll);
379
380     frameView->setConstrainsScrollingToContentEdge(false);
381     frameView->notifyScrollPositionChanged(scrollPosition);
382     frameView->setConstrainsScrollingToContentEdge(true);
383
384     frameView->setInProgrammaticScroll(oldProgrammaticScroll);
385
386 #if USE(ACCELERATED_COMPOSITING)
387     if (GraphicsLayer* scrollLayer = scrollLayerForFrameView(frameView)) {
388         GraphicsLayer* counterScrollingLayer = counterScrollingLayerForFrameView(frameView);
389         if (programmaticScroll || scrollingLayerPositionAction == SetScrollingLayerPosition) {
390             scrollLayer->setPosition(-frameView->scrollPosition());
391             if (counterScrollingLayer)
392                 counterScrollingLayer->setPosition(IntPoint(frameView->scrollOffsetForFixedPosition()));
393         } else {
394             scrollLayer->syncPosition(-frameView->scrollPosition());
395             if (counterScrollingLayer)
396                 counterScrollingLayer->syncPosition(IntPoint(frameView->scrollOffsetForFixedPosition()));
397
398             LayoutRect viewportRect = frameView->viewportConstrainedVisibleContentRect();
399             syncChildPositions(viewportRect);
400         }
401     }
402 #else
403     UNUSED_PARAM(scrollingLayerPositionAction);
404 #endif
405 }
406
407 #if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN))
408 void ScrollingCoordinator::handleWheelEventPhase(PlatformWheelEventPhase phase)
409 {
410     ASSERT(isMainThread());
411
412     if (!m_page)
413         return;
414
415     FrameView* frameView = m_page->mainFrame()->view();
416     if (!frameView)
417         return;
418
419     frameView->scrollAnimator()->handleWheelEventPhase(phase);
420 }
421 #endif
422
423 bool ScrollingCoordinator::hasVisibleSlowRepaintViewportConstrainedObjects(FrameView* frameView) const
424 {
425     const FrameView::ViewportConstrainedObjectSet* viewportConstrainedObjects = frameView->viewportConstrainedObjects();
426     if (!viewportConstrainedObjects)
427         return false;
428
429 #if USE(ACCELERATED_COMPOSITING)
430     for (FrameView::ViewportConstrainedObjectSet::const_iterator it = viewportConstrainedObjects->begin(), end = viewportConstrainedObjects->end(); it != end; ++it) {
431         RenderObject* viewportConstrainedObject = *it;
432         if (!viewportConstrainedObject->isBoxModelObject() || !viewportConstrainedObject->hasLayer())
433             return true;
434         RenderLayer* layer = toRenderBoxModelObject(viewportConstrainedObject)->layer();
435         // Any explicit reason that a fixed position element is not composited shouldn't cause slow scrolling.
436         if (!layer->isComposited() && layer->viewportConstrainedNotCompositedReason() == RenderLayer::NoNotCompositedReason)
437             return true;
438     }
439     return false;
440 #else
441     return viewportConstrainedObjects->size();
442 #endif
443 }
444
445 MainThreadScrollingReasons ScrollingCoordinator::mainThreadScrollingReasons() const
446 {
447     FrameView* frameView = m_page->mainFrame()->view();
448     if (!frameView)
449         return static_cast<MainThreadScrollingReasons>(0);
450
451     MainThreadScrollingReasons mainThreadScrollingReasons = (MainThreadScrollingReasons)0;
452
453     if (m_forceMainThreadScrollLayerPositionUpdates)
454         mainThreadScrollingReasons |= ForcedOnMainThread;
455     if (frameView->hasSlowRepaintObjects())
456         mainThreadScrollingReasons |= HasSlowRepaintObjects;
457     if (!supportsFixedPositionLayers() && frameView->hasViewportConstrainedObjects())
458         mainThreadScrollingReasons |= HasViewportConstrainedObjectsWithoutSupportingFixedLayers;
459     if (supportsFixedPositionLayers() && hasVisibleSlowRepaintViewportConstrainedObjects(frameView))
460         mainThreadScrollingReasons |= HasNonLayerViewportConstrainedObjects;
461     if (m_page->mainFrame()->document()->isImageDocument())
462         mainThreadScrollingReasons |= IsImageDocument;
463
464     return mainThreadScrollingReasons;
465 }
466
467 void ScrollingCoordinator::updateShouldUpdateScrollLayerPositionOnMainThread()
468 {
469     setShouldUpdateScrollLayerPositionOnMainThread(mainThreadScrollingReasons());
470 }
471
472 void ScrollingCoordinator::setForceMainThreadScrollLayerPositionUpdates(bool forceMainThreadScrollLayerPositionUpdates)
473 {
474     if (m_forceMainThreadScrollLayerPositionUpdates == forceMainThreadScrollLayerPositionUpdates)
475         return;
476
477     m_forceMainThreadScrollLayerPositionUpdates = forceMainThreadScrollLayerPositionUpdates;
478     updateShouldUpdateScrollLayerPositionOnMainThread();
479 }
480
481 ScrollingNodeID ScrollingCoordinator::uniqueScrollLayerID()
482 {
483     static ScrollingNodeID uniqueScrollLayerID = 1;
484     return uniqueScrollLayerID++;
485 }
486
487 String ScrollingCoordinator::scrollingStateTreeAsText() const
488 {
489     return String();
490 }
491
492 String ScrollingCoordinator::mainThreadScrollingReasonsAsText(MainThreadScrollingReasons reasons)
493 {
494     StringBuilder stringBuilder;
495
496     if (reasons & ScrollingCoordinator::ForcedOnMainThread)
497         stringBuilder.append("Forced on main thread, ");
498     if (reasons & ScrollingCoordinator::HasSlowRepaintObjects)
499         stringBuilder.append("Has slow repaint objects, ");
500     if (reasons & ScrollingCoordinator::HasViewportConstrainedObjectsWithoutSupportingFixedLayers)
501         stringBuilder.append("Has viewport constrained objects without supporting fixed layers, ");
502     if (reasons & ScrollingCoordinator::HasNonLayerViewportConstrainedObjects)
503         stringBuilder.append("Has non-layer viewport-constrained objects, ");
504     if (reasons & ScrollingCoordinator::IsImageDocument)
505         stringBuilder.append("Is image document, ");
506
507     if (stringBuilder.length())
508         stringBuilder.resize(stringBuilder.length() - 2);
509     return stringBuilder.toString();
510 }
511
512 String ScrollingCoordinator::mainThreadScrollingReasonsAsText() const
513 {
514     return mainThreadScrollingReasonsAsText(mainThreadScrollingReasons());
515 }
516
517 } // namespace WebCore