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