2ec7165485e7c1c51acd40f511f6a1a7ea908894
[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 static int fixedPositionScrollOffset(int visibleContentSize, int contentsSize, int scrollPosition, int scrollOrigin, float frameScaleFactor, bool fixedElementsLayoutRelativeToFrame)
79 {
80     int maxValue = contentsSize - visibleContentSize;
81     if (maxValue <= 0)
82         return 0;
83
84     if (!scrollOrigin) {
85         if (scrollPosition <= 0)
86             return 0;
87         if (scrollPosition > maxValue)
88             scrollPosition = maxValue;
89     } else {
90         if (scrollPosition >= 0)
91             return 0;
92         if (scrollPosition < -maxValue)
93             scrollPosition = -maxValue;
94     }
95
96     float dragFactor = fixedElementsLayoutRelativeToFrame ? 1 : (contentsSize - visibleContentSize * frameScaleFactor) / maxValue;
97     return scrollPosition * dragFactor / frameScaleFactor;
98 }
99
100 IntSize scrollOffsetForFixedPosition(const IntRect& visibleContentRect, const IntSize& contentsSize, const IntPoint& scrollPosition, const IntPoint& scrollOrigin, float frameScaleFactor, bool fixedElementsLayoutRelativeToFrame)
101 {
102     int x = fixedPositionScrollOffset(visibleContentRect.width(), contentsSize.width(), scrollPosition.x(), scrollOrigin.x(), frameScaleFactor, fixedElementsLayoutRelativeToFrame);
103     int y = fixedPositionScrollOffset(visibleContentRect.height(), contentsSize.height(), scrollPosition.y(), scrollOrigin.y(), frameScaleFactor, fixedElementsLayoutRelativeToFrame);
104     return IntSize(x, y);
105 }
106
107 ScrollingCoordinator::ScrollingCoordinator(Page* page)
108     : m_page(page)
109     , m_updateMainFrameScrollPositionTimer(this, &ScrollingCoordinator::updateMainFrameScrollPositionTimerFired)
110     , m_scheduledUpdateIsProgrammaticScroll(false)
111     , m_scheduledScrollingLayerPositionAction(SyncScrollingLayerPosition)
112     , m_forceMainThreadScrollLayerPositionUpdates(false)
113 {
114 }
115
116 ScrollingCoordinator::~ScrollingCoordinator()
117 {
118     ASSERT(!m_page);
119 }
120
121 void ScrollingCoordinator::pageDestroyed()
122 {
123     ASSERT(m_page);
124     m_page = 0;
125 }
126
127 bool ScrollingCoordinator::coordinatesScrollingForFrameView(FrameView* frameView) const
128 {
129     ASSERT(isMainThread());
130     ASSERT(m_page);
131
132     // We currently only handle the main frame.
133     if (frameView->frame() != m_page->mainFrame())
134         return false;
135
136     // We currently only support composited mode.
137 #if USE(ACCELERATED_COMPOSITING)
138     RenderView* renderView = m_page->mainFrame()->contentRenderer();
139     if (!renderView)
140         return false;
141     return renderView->usesCompositing();
142 #else
143     return false;
144 #endif
145 }
146
147 Region ScrollingCoordinator::computeNonFastScrollableRegion(const Frame* frame, const IntPoint& frameLocation) const
148 {
149     Region nonFastScrollableRegion;
150     FrameView* frameView = frame->view();
151     if (!frameView)
152         return nonFastScrollableRegion;
153
154     IntPoint offset = frameLocation;
155     offset.moveBy(frameView->frameRect().location());
156
157     if (const FrameView::ScrollableAreaSet* scrollableAreas = frameView->scrollableAreas()) {
158         for (FrameView::ScrollableAreaSet::const_iterator it = scrollableAreas->begin(), end = scrollableAreas->end(); it != end; ++it) {
159             ScrollableArea* scrollableArea = *it;
160 #if USE(ACCELERATED_COMPOSITING)
161             // Composited scrollable areas can be scrolled off the main thread.
162             if (scrollableArea->usesCompositedScrolling())
163                 continue;
164 #endif
165             IntRect box = scrollableArea->scrollableAreaBoundingBox();
166             box.moveBy(offset);
167             nonFastScrollableRegion.unite(box);
168         }
169     }
170
171     if (const HashSet<RefPtr<Widget> >* children = frameView->children()) {
172         for (HashSet<RefPtr<Widget> >::const_iterator it = children->begin(), end = children->end(); it != end; ++it) {
173             if (!(*it)->isPluginViewBase())
174                 continue;
175
176             PluginViewBase* pluginViewBase = static_cast<PluginViewBase*>((*it).get());
177             if (pluginViewBase->wantsWheelEvents())
178                 nonFastScrollableRegion.unite(pluginViewBase->frameRect());
179         }
180     }
181
182     FrameTree* tree = frame->tree();
183     for (Frame* subFrame = tree->firstChild(); subFrame; subFrame = subFrame->tree()->nextSibling())
184         nonFastScrollableRegion.unite(computeNonFastScrollableRegion(subFrame, offset));
185
186     return nonFastScrollableRegion;
187 }
188
189 #if ENABLE(TOUCH_EVENT_TRACKING)
190 static void accumulateRendererTouchEventTargetRects(Vector<IntRect>& rects, const RenderObject* renderer, const IntRect& parentRect = IntRect())
191 {
192     IntRect adjustedParentRect = parentRect;
193     if (parentRect.isEmpty() || renderer->isFloating() || renderer->isPositioned() || renderer->hasTransform()) {
194         // FIXME: This method is O(N^2) as it walks the tree to the root for every renderer. RenderGeometryMap would fix this.
195         IntRect r = enclosingIntRect(renderer->clippedOverflowRectForRepaint(0));
196         if (!r.isEmpty()) {
197             // Convert to the top-level view's coordinates.
198             ASSERT(renderer->document()->view());
199             r = renderer->document()->view()->convertToRootView(r);
200
201             if (!parentRect.contains(r)) {
202                 rects.append(r);
203                 adjustedParentRect = r;
204             }
205         }
206     }
207
208     for (RenderObject* child = renderer->firstChild(); child; child = child->nextSibling())
209         accumulateRendererTouchEventTargetRects(rects, child, adjustedParentRect);
210 }
211
212 static void accumulateDocumentEventTargetRects(Vector<IntRect>& rects, const Document* document)
213 {
214     ASSERT(document);
215     if (!document->touchEventTargets())
216         return;
217
218     const TouchEventTargetSet* targets = document->touchEventTargets();
219     for (TouchEventTargetSet::const_iterator iter = targets->begin(); iter != targets->end(); ++iter) {
220         const Node* touchTarget = iter->key;
221         if (!touchTarget->inDocument())
222             continue;
223
224         if (touchTarget == document) {
225             if (RenderView* view = document->renderView()) {
226                 IntRect r;
227                 if (touchTarget == document->topDocument())
228                     r = view->documentRect();
229                 else
230                     r = enclosingIntRect(view->clippedOverflowRectForRepaint(0));
231
232                 if (!r.isEmpty()) {
233                     ASSERT(view->document()->view());
234                     r = view->document()->view()->convertToRootView(r);
235                     rects.append(r);
236                 }
237             }
238             return;
239         }
240
241         if (touchTarget->isDocumentNode() && touchTarget != document) {
242             accumulateDocumentEventTargetRects(rects, static_cast<const Document*>(touchTarget));
243             continue;
244         }
245
246         if (RenderObject* renderer = touchTarget->renderer())
247             accumulateRendererTouchEventTargetRects(rects, renderer);
248     }
249 }
250
251 void ScrollingCoordinator::computeAbsoluteTouchEventTargetRects(const Document* document, Vector<IntRect>& rects)
252 {
253     ASSERT(document);
254     if (!document->view())
255         return;
256
257     // FIXME: These rects won't be properly updated if the renderers are in a sub-tree that scrolls.
258     accumulateDocumentEventTargetRects(rects, document);
259 }
260 #endif
261
262 unsigned ScrollingCoordinator::computeCurrentWheelEventHandlerCount()
263 {
264     unsigned wheelEventHandlerCount = 0;
265
266     for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
267         if (frame->document())
268             wheelEventHandlerCount += frame->document()->wheelEventHandlerCount();
269     }
270
271     return wheelEventHandlerCount;
272 }
273
274 void ScrollingCoordinator::frameViewWheelEventHandlerCountChanged(FrameView* frameView)
275 {
276     ASSERT(isMainThread());
277     ASSERT(m_page);
278
279     recomputeWheelEventHandlerCountForFrameView(frameView);
280 }
281
282 void ScrollingCoordinator::frameViewHasSlowRepaintObjectsDidChange(FrameView* frameView)
283 {
284     ASSERT(isMainThread());
285     ASSERT(m_page);
286
287     if (!coordinatesScrollingForFrameView(frameView))
288         return;
289
290     updateShouldUpdateScrollLayerPositionOnMainThread();
291 }
292
293 void ScrollingCoordinator::frameViewFixedObjectsDidChange(FrameView* frameView)
294 {
295     ASSERT(isMainThread());
296     ASSERT(m_page);
297
298     if (!coordinatesScrollingForFrameView(frameView))
299         return;
300
301     updateShouldUpdateScrollLayerPositionOnMainThread();
302 }
303
304 GraphicsLayer* ScrollingCoordinator::scrollLayerForFrameView(FrameView* frameView)
305 {
306 #if USE(ACCELERATED_COMPOSITING)
307     Frame* frame = frameView->frame();
308     if (!frame)
309         return 0;
310
311     RenderView* renderView = frame->contentRenderer();
312     if (!renderView)
313         return 0;
314     return renderView->compositor()->scrollLayer();
315 #else
316     UNUSED_PARAM(frameView);
317     return 0;
318 #endif
319 }
320
321 GraphicsLayer* ScrollingCoordinator::counterScrollingLayerForFrameView(FrameView* frameView)
322 {
323 #if USE(ACCELERATED_COMPOSITING)
324     Frame* frame = frameView->frame();
325     if (!frame)
326         return 0;
327
328     RenderView* renderView = frame->contentRenderer();
329     if (!renderView)
330         return 0;
331     return renderView->compositor()->fixedRootBackgroundLayer();
332 #else
333     UNUSED_PARAM(frameView);
334     return 0;
335 #endif
336 }
337
338 void ScrollingCoordinator::frameViewRootLayerDidChange(FrameView* frameView)
339 {
340     ASSERT(isMainThread());
341     ASSERT(m_page);
342
343     if (!coordinatesScrollingForFrameView(frameView))
344         return;
345
346     frameViewLayoutUpdated(frameView);
347     recomputeWheelEventHandlerCountForFrameView(frameView);
348     updateShouldUpdateScrollLayerPositionOnMainThread();
349 }
350
351 void ScrollingCoordinator::scheduleUpdateMainFrameScrollPosition(const IntPoint& scrollPosition, bool programmaticScroll, SetOrSyncScrollingLayerPosition scrollingLayerPositionAction)
352 {
353     if (m_updateMainFrameScrollPositionTimer.isActive()) {
354         if (m_scheduledUpdateIsProgrammaticScroll == programmaticScroll
355             && m_scheduledScrollingLayerPositionAction == scrollingLayerPositionAction) {
356             m_scheduledUpdateScrollPosition = scrollPosition;
357             return;
358         }
359     
360         // If the parameters don't match what was previosly scheduled, dispatch immediately.
361         m_updateMainFrameScrollPositionTimer.stop();
362         updateMainFrameScrollPosition(m_scheduledUpdateScrollPosition, m_scheduledUpdateIsProgrammaticScroll, m_scheduledScrollingLayerPositionAction);
363         updateMainFrameScrollPosition(scrollPosition, programmaticScroll, scrollingLayerPositionAction);
364         return;
365     }
366
367     m_scheduledUpdateScrollPosition = scrollPosition;
368     m_scheduledUpdateIsProgrammaticScroll = programmaticScroll;
369     m_scheduledScrollingLayerPositionAction = scrollingLayerPositionAction;
370     m_updateMainFrameScrollPositionTimer.startOneShot(0);
371 }
372
373 void ScrollingCoordinator::updateMainFrameScrollPositionTimerFired(Timer<ScrollingCoordinator>*)
374 {
375     updateMainFrameScrollPosition(m_scheduledUpdateScrollPosition, m_scheduledUpdateIsProgrammaticScroll, m_scheduledScrollingLayerPositionAction);
376 }
377
378 void ScrollingCoordinator::updateMainFrameScrollPosition(const IntPoint& scrollPosition, bool programmaticScroll, SetOrSyncScrollingLayerPosition scrollingLayerPositionAction)
379 {
380     ASSERT(isMainThread());
381
382     if (!m_page)
383         return;
384
385     FrameView* frameView = m_page->mainFrame()->view();
386     if (!frameView)
387         return;
388
389     bool oldProgrammaticScroll = frameView->inProgrammaticScroll();
390     frameView->setInProgrammaticScroll(programmaticScroll);
391
392     frameView->setConstrainsScrollingToContentEdge(false);
393     frameView->notifyScrollPositionChanged(scrollPosition);
394     frameView->setConstrainsScrollingToContentEdge(true);
395
396     frameView->setInProgrammaticScroll(oldProgrammaticScroll);
397
398 #if USE(ACCELERATED_COMPOSITING)
399     if (GraphicsLayer* scrollLayer = scrollLayerForFrameView(frameView)) {
400         GraphicsLayer* counterScrollingLayer = counterScrollingLayerForFrameView(frameView);
401         if (programmaticScroll || scrollingLayerPositionAction == SetScrollingLayerPosition) {
402             scrollLayer->setPosition(-frameView->scrollPosition());
403             if (counterScrollingLayer)
404                 counterScrollingLayer->setPosition(IntPoint(frameView->scrollOffsetForFixedPosition()));
405         } else {
406             scrollLayer->syncPosition(-frameView->scrollPosition());
407             if (counterScrollingLayer)
408                 counterScrollingLayer->syncPosition(IntPoint(frameView->scrollOffsetForFixedPosition()));
409
410             LayoutRect viewportRect = frameView->viewportConstrainedVisibleContentRect();
411             syncChildPositions(viewportRect);
412         }
413     }
414 #else
415     UNUSED_PARAM(scrollingLayerPositionAction);
416 #endif
417 }
418
419 #if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN))
420 void ScrollingCoordinator::handleWheelEventPhase(PlatformWheelEventPhase phase)
421 {
422     ASSERT(isMainThread());
423
424     if (!m_page)
425         return;
426
427     FrameView* frameView = m_page->mainFrame()->view();
428     if (!frameView)
429         return;
430
431     frameView->scrollAnimator()->handleWheelEventPhase(phase);
432 }
433 #endif
434
435 bool ScrollingCoordinator::hasVisibleSlowRepaintViewportConstrainedObjects(FrameView* frameView) const
436 {
437     const FrameView::ViewportConstrainedObjectSet* viewportConstrainedObjects = frameView->viewportConstrainedObjects();
438     if (!viewportConstrainedObjects)
439         return false;
440
441 #if USE(ACCELERATED_COMPOSITING)
442     for (FrameView::ViewportConstrainedObjectSet::const_iterator it = viewportConstrainedObjects->begin(), end = viewportConstrainedObjects->end(); it != end; ++it) {
443         RenderObject* viewportConstrainedObject = *it;
444         if (!viewportConstrainedObject->isBoxModelObject() || !viewportConstrainedObject->hasLayer())
445             return true;
446         RenderLayer* layer = toRenderBoxModelObject(viewportConstrainedObject)->layer();
447         // Any explicit reason that a fixed position element is not composited shouldn't cause slow scrolling.
448         if (!layer->isComposited() && layer->viewportConstrainedNotCompositedReason() == RenderLayer::NoNotCompositedReason)
449             return true;
450     }
451     return false;
452 #else
453     return viewportConstrainedObjects->size();
454 #endif
455 }
456
457 MainThreadScrollingReasons ScrollingCoordinator::mainThreadScrollingReasons() const
458 {
459     FrameView* frameView = m_page->mainFrame()->view();
460     if (!frameView)
461         return static_cast<MainThreadScrollingReasons>(0);
462
463     MainThreadScrollingReasons mainThreadScrollingReasons = (MainThreadScrollingReasons)0;
464
465     if (m_forceMainThreadScrollLayerPositionUpdates)
466         mainThreadScrollingReasons |= ForcedOnMainThread;
467     if (frameView->hasSlowRepaintObjects())
468         mainThreadScrollingReasons |= HasSlowRepaintObjects;
469     if (!supportsFixedPositionLayers() && frameView->hasViewportConstrainedObjects())
470         mainThreadScrollingReasons |= HasViewportConstrainedObjectsWithoutSupportingFixedLayers;
471     if (supportsFixedPositionLayers() && hasVisibleSlowRepaintViewportConstrainedObjects(frameView))
472         mainThreadScrollingReasons |= HasNonLayerViewportConstrainedObjects;
473     if (m_page->mainFrame()->document()->isImageDocument())
474         mainThreadScrollingReasons |= IsImageDocument;
475
476     return mainThreadScrollingReasons;
477 }
478
479 void ScrollingCoordinator::updateShouldUpdateScrollLayerPositionOnMainThread()
480 {
481     setShouldUpdateScrollLayerPositionOnMainThread(mainThreadScrollingReasons());
482 }
483
484 void ScrollingCoordinator::setForceMainThreadScrollLayerPositionUpdates(bool forceMainThreadScrollLayerPositionUpdates)
485 {
486     if (m_forceMainThreadScrollLayerPositionUpdates == forceMainThreadScrollLayerPositionUpdates)
487         return;
488
489     m_forceMainThreadScrollLayerPositionUpdates = forceMainThreadScrollLayerPositionUpdates;
490     updateShouldUpdateScrollLayerPositionOnMainThread();
491 }
492
493 ScrollingNodeID ScrollingCoordinator::uniqueScrollLayerID()
494 {
495     static ScrollingNodeID uniqueScrollLayerID = 1;
496     return uniqueScrollLayerID++;
497 }
498
499 String ScrollingCoordinator::scrollingStateTreeAsText() const
500 {
501     return String();
502 }
503
504 String ScrollingCoordinator::mainThreadScrollingReasonsAsText(MainThreadScrollingReasons reasons)
505 {
506     StringBuilder stringBuilder;
507
508     if (reasons & ScrollingCoordinator::ForcedOnMainThread)
509         stringBuilder.append("Forced on main thread, ");
510     if (reasons & ScrollingCoordinator::HasSlowRepaintObjects)
511         stringBuilder.append("Has slow repaint objects, ");
512     if (reasons & ScrollingCoordinator::HasViewportConstrainedObjectsWithoutSupportingFixedLayers)
513         stringBuilder.append("Has viewport constrained objects without supporting fixed layers, ");
514     if (reasons & ScrollingCoordinator::HasNonLayerViewportConstrainedObjects)
515         stringBuilder.append("Has non-layer viewport-constrained objects, ");
516     if (reasons & ScrollingCoordinator::IsImageDocument)
517         stringBuilder.append("Is image document, ");
518
519     if (stringBuilder.length())
520         stringBuilder.resize(stringBuilder.length() - 2);
521     return stringBuilder.toString();
522 }
523
524 String ScrollingCoordinator::mainThreadScrollingReasonsAsText() const
525 {
526     return mainThreadScrollingReasonsAsText(mainThreadScrollingReasons());
527 }
528
529 } // namespace WebCore