Fix repaint issues when resizing a window with centered content, for platforms with...
[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 "Frame.h"
31 #include "FrameView.h"
32 #include "GraphicsLayer.h"
33 #include "IntRect.h"
34 #include "Page.h"
35 #include "PlatformWheelEvent.h"
36 #include "PluginViewBase.h"
37 #include "Region.h"
38 #include "RenderView.h"
39 #include "ScrollAnimator.h"
40 #include <wtf/MainThread.h>
41
42 #if USE(ACCELERATED_COMPOSITING)
43 #include "RenderLayerCompositor.h"
44 #endif
45
46 #if ENABLE(THREADED_SCROLLING)
47 #include "ScrollingCoordinatorMac.h"
48 #endif
49
50 #if PLATFORM(CHROMIUM)
51 #include "ScrollingCoordinatorChromium.h"
52 #endif
53
54 namespace WebCore {
55
56 PassRefPtr<ScrollingCoordinator> ScrollingCoordinator::create(Page* page)
57 {
58 #if USE(ACCELERATED_COMPOSITING) && ENABLE(THREADED_SCROLLING)
59     return adoptRef(new ScrollingCoordinatorMac(page));
60 #endif
61
62 #if PLATFORM(CHROMIUM)
63     return adoptRef(new ScrollingCoordinatorChromium(page));
64 #endif
65
66     return adoptRef(new ScrollingCoordinator(page));
67 }
68
69 static int fixedPositionScrollOffset(int visibleContentSize, int contentsSize, int scrollPosition, int scrollOrigin, float frameScaleFactor, bool fixedElementsLayoutRelativeToFrame)
70 {
71     int maxValue = contentsSize - visibleContentSize;
72     if (maxValue <= 0)
73         return 0;
74
75     if (!scrollOrigin) {
76         if (scrollPosition <= 0)
77             return 0;
78         if (scrollPosition > maxValue)
79             scrollPosition = maxValue;
80     } else {
81         if (scrollPosition >= 0)
82             return 0;
83         if (scrollPosition < -maxValue)
84             scrollPosition = -maxValue;
85     }
86
87     float dragFactor = fixedElementsLayoutRelativeToFrame ? 1 : (contentsSize - visibleContentSize * frameScaleFactor) / maxValue;
88     return scrollPosition * dragFactor / frameScaleFactor;
89 }
90
91 IntSize scrollOffsetForFixedPosition(const IntRect& visibleContentRect, const IntSize& contentsSize, const IntPoint& scrollPosition, const IntPoint& scrollOrigin, float frameScaleFactor, bool fixedElementsLayoutRelativeToFrame)
92 {
93     int x = fixedPositionScrollOffset(visibleContentRect.width(), contentsSize.width(), scrollPosition.x(), scrollOrigin.x(), frameScaleFactor, fixedElementsLayoutRelativeToFrame);
94     int y = fixedPositionScrollOffset(visibleContentRect.height(), contentsSize.height(), scrollPosition.y(), scrollOrigin.y(), frameScaleFactor, fixedElementsLayoutRelativeToFrame);
95     return IntSize(x, y);
96 }
97
98 ScrollingCoordinator::ScrollingCoordinator(Page* page)
99     : m_page(page)
100     , m_updateMainFrameScrollPositionTimer(this, &ScrollingCoordinator::updateMainFrameScrollPositionTimerFired)
101     , m_scheduledUpdateIsProgrammaticScroll(false)
102     , m_scheduledScrollingLayerPositionAction(SyncScrollingLayerPosition)
103     , m_forceMainThreadScrollLayerPositionUpdates(false)
104 {
105 }
106
107 ScrollingCoordinator::~ScrollingCoordinator()
108 {
109     ASSERT(!m_page);
110 }
111
112 void ScrollingCoordinator::pageDestroyed()
113 {
114     ASSERT(m_page);
115     m_page = 0;
116 }
117
118 bool ScrollingCoordinator::coordinatesScrollingForFrameView(FrameView* frameView) const
119 {
120     ASSERT(isMainThread());
121     ASSERT(m_page);
122
123     // We currently only handle the main frame.
124     if (frameView->frame() != m_page->mainFrame())
125         return false;
126
127     // We currently only support composited mode.
128 #if USE(ACCELERATED_COMPOSITING)
129     RenderView* renderView = m_page->mainFrame()->contentRenderer();
130     if (!renderView)
131         return false;
132     return renderView->usesCompositing();
133 #else
134     return false;
135 #endif
136 }
137
138 Region ScrollingCoordinator::computeNonFastScrollableRegion(Frame* frame, const IntPoint& frameLocation)
139 {
140     Region nonFastScrollableRegion;
141     FrameView* frameView = frame->view();
142     if (!frameView)
143         return nonFastScrollableRegion;
144
145     IntPoint offset = frameLocation;
146     offset.moveBy(frameView->frameRect().location());
147
148     if (const FrameView::ScrollableAreaSet* scrollableAreas = frameView->scrollableAreas()) {
149         for (FrameView::ScrollableAreaSet::const_iterator it = scrollableAreas->begin(), end = scrollableAreas->end(); it != end; ++it) {
150             ScrollableArea* scrollableArea = *it;
151 #if USE(ACCELERATED_COMPOSITING)
152             // Composited scrollable areas can be scrolled off the main thread.
153             if (scrollableArea->usesCompositedScrolling())
154                 continue;
155 #endif
156             IntRect box = scrollableArea->scrollableAreaBoundingBox();
157             box.moveBy(offset);
158             nonFastScrollableRegion.unite(box);
159         }
160     }
161
162     if (const HashSet<RefPtr<Widget> >* children = frameView->children()) {
163         for (HashSet<RefPtr<Widget> >::const_iterator it = children->begin(), end = children->end(); it != end; ++it) {
164             if (!(*it)->isPluginViewBase())
165                 continue;
166
167             PluginViewBase* pluginViewBase = static_cast<PluginViewBase*>((*it).get());
168             if (pluginViewBase->wantsWheelEvents())
169                 nonFastScrollableRegion.unite(pluginViewBase->frameRect());
170         }
171     }
172
173     FrameTree* tree = frame->tree();
174     for (Frame* subFrame = tree->firstChild(); subFrame; subFrame = subFrame->tree()->nextSibling())
175         nonFastScrollableRegion.unite(computeNonFastScrollableRegion(subFrame, offset));
176
177     return nonFastScrollableRegion;
178 }
179
180 unsigned ScrollingCoordinator::computeCurrentWheelEventHandlerCount()
181 {
182     unsigned wheelEventHandlerCount = 0;
183
184     for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
185         if (frame->document())
186             wheelEventHandlerCount += frame->document()->wheelEventHandlerCount();
187     }
188
189     return wheelEventHandlerCount;
190 }
191
192 void ScrollingCoordinator::frameViewWheelEventHandlerCountChanged(FrameView* frameView)
193 {
194     ASSERT(isMainThread());
195     ASSERT(m_page);
196
197     recomputeWheelEventHandlerCountForFrameView(frameView);
198 }
199
200 void ScrollingCoordinator::frameViewHasSlowRepaintObjectsDidChange(FrameView* frameView)
201 {
202     ASSERT(isMainThread());
203     ASSERT(m_page);
204
205     if (!coordinatesScrollingForFrameView(frameView))
206         return;
207
208     updateShouldUpdateScrollLayerPositionOnMainThread();
209 }
210
211 void ScrollingCoordinator::frameViewFixedObjectsDidChange(FrameView* frameView)
212 {
213     ASSERT(isMainThread());
214     ASSERT(m_page);
215
216     if (!coordinatesScrollingForFrameView(frameView))
217         return;
218
219     updateShouldUpdateScrollLayerPositionOnMainThread();
220 }
221
222 GraphicsLayer* ScrollingCoordinator::scrollLayerForFrameView(FrameView* frameView)
223 {
224 #if USE(ACCELERATED_COMPOSITING)
225     Frame* frame = frameView->frame();
226     if (!frame)
227         return 0;
228
229     RenderView* renderView = frame->contentRenderer();
230     if (!renderView)
231         return 0;
232     return renderView->compositor()->scrollLayer();
233 #else
234     UNUSED_PARAM(frameView);
235     return 0;
236 #endif
237 }
238
239 void ScrollingCoordinator::frameViewRootLayerDidChange(FrameView* frameView)
240 {
241     ASSERT(isMainThread());
242     ASSERT(m_page);
243
244     if (!coordinatesScrollingForFrameView(frameView))
245         return;
246
247     frameViewLayoutUpdated(frameView);
248     recomputeWheelEventHandlerCountForFrameView(frameView);
249     updateShouldUpdateScrollLayerPositionOnMainThread();
250 }
251
252 void ScrollingCoordinator::scheduleUpdateMainFrameScrollPosition(const IntPoint& scrollPosition, bool programmaticScroll, SetOrSyncScrollingLayerPosition scrollingLayerPositionAction)
253 {
254     if (m_updateMainFrameScrollPositionTimer.isActive()) {
255         if (m_scheduledUpdateIsProgrammaticScroll == programmaticScroll
256             && m_scheduledScrollingLayerPositionAction == scrollingLayerPositionAction) {
257             m_scheduledUpdateScrollPosition = scrollPosition;
258             return;
259         }
260     
261         // If the parameters don't match what was previosly scheduled, dispatch immediately.
262         m_updateMainFrameScrollPositionTimer.stop();
263         updateMainFrameScrollPosition(m_scheduledUpdateScrollPosition, m_scheduledUpdateIsProgrammaticScroll, m_scheduledScrollingLayerPositionAction);
264         updateMainFrameScrollPosition(scrollPosition, programmaticScroll, scrollingLayerPositionAction);
265         return;
266     }
267
268     m_scheduledUpdateScrollPosition = scrollPosition;
269     m_scheduledUpdateIsProgrammaticScroll = programmaticScroll;
270     m_scheduledScrollingLayerPositionAction = scrollingLayerPositionAction;
271     m_updateMainFrameScrollPositionTimer.startOneShot(0);
272 }
273
274 void ScrollingCoordinator::updateMainFrameScrollPositionTimerFired(Timer<ScrollingCoordinator>*)
275 {
276     updateMainFrameScrollPosition(m_scheduledUpdateScrollPosition, m_scheduledUpdateIsProgrammaticScroll, m_scheduledScrollingLayerPositionAction);
277 }
278
279 void ScrollingCoordinator::updateMainFrameScrollPosition(const IntPoint& scrollPosition, bool programmaticScroll, SetOrSyncScrollingLayerPosition scrollingLayerPositionAction)
280 {
281     ASSERT(isMainThread());
282
283     if (!m_page)
284         return;
285
286     FrameView* frameView = m_page->mainFrame()->view();
287     if (!frameView)
288         return;
289
290     bool oldProgrammaticScroll = frameView->inProgrammaticScroll();
291     frameView->setInProgrammaticScroll(programmaticScroll);
292
293     frameView->setConstrainsScrollingToContentEdge(false);
294     frameView->notifyScrollPositionChanged(scrollPosition);
295     frameView->setConstrainsScrollingToContentEdge(true);
296
297     frameView->setInProgrammaticScroll(oldProgrammaticScroll);
298
299 #if USE(ACCELERATED_COMPOSITING)
300     if (GraphicsLayer* scrollLayer = scrollLayerForFrameView(frameView)) {
301         if (programmaticScroll || scrollingLayerPositionAction == SetScrollingLayerPosition)
302             scrollLayer->setPosition(-frameView->scrollPosition());
303         else {
304             scrollLayer->syncPosition(-frameView->scrollPosition());
305             LayoutRect viewportRect = frameView->visibleContentRect();
306             viewportRect.setLocation(toPoint(frameView->scrollOffsetForFixedPosition()));
307             syncChildPositions(viewportRect);
308         }
309     }
310 #else
311     UNUSED_PARAM(scrollingLayerPositionAction);
312 #endif
313 }
314
315 #if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN))
316 void ScrollingCoordinator::handleWheelEventPhase(PlatformWheelEventPhase phase)
317 {
318     ASSERT(isMainThread());
319
320     if (!m_page)
321         return;
322
323     FrameView* frameView = m_page->mainFrame()->view();
324     if (!frameView)
325         return;
326
327     frameView->scrollAnimator()->handleWheelEventPhase(phase);
328 }
329 #endif
330
331 bool ScrollingCoordinator::hasVisibleSlowRepaintFixedObjects(FrameView* frameView) const
332 {
333     const FrameView::ViewportConstrainedObjectSet* viewportConstrainedObjects = frameView->viewportConstrainedObjects();
334     if (!viewportConstrainedObjects)
335         return false;
336
337 #if USE(ACCELERATED_COMPOSITING)
338     for (FrameView::ViewportConstrainedObjectSet::const_iterator it = viewportConstrainedObjects->begin(), end = viewportConstrainedObjects->end(); it != end; ++it) {
339         RenderObject* viewportConstrainedObject = *it;
340         if (!viewportConstrainedObject->isBoxModelObject() || !viewportConstrainedObject->hasLayer())
341             return true;
342         RenderLayer* layer = toRenderBoxModelObject(viewportConstrainedObject)->layer();
343         // Any explicit reason that a fixed position element is not composited shouldn't cause slow scrolling.
344         if (!layer->isComposited() && layer->compositor()->fixedPositionLayerNotCompositedReason(layer) == RenderLayerCompositor::NoReason)
345             return true;
346     }
347     return false;
348 #else
349     return viewportConstrainedObjects->size();
350 #endif
351 }
352
353 MainThreadScrollingReasons ScrollingCoordinator::mainThreadScrollingReasons() const
354 {
355     FrameView* frameView = m_page->mainFrame()->view();
356
357     MainThreadScrollingReasons mainThreadScrollingReasons = (MainThreadScrollingReasons)0;
358
359     if (m_forceMainThreadScrollLayerPositionUpdates)
360         mainThreadScrollingReasons |= ForcedOnMainThread;
361     if (frameView->hasSlowRepaintObjects())
362         mainThreadScrollingReasons |= HasSlowRepaintObjects;
363     if (!supportsFixedPositionLayers() && frameView->hasViewportConstrainedObjects())
364         mainThreadScrollingReasons |= HasViewportConstrainedObjectsWithoutSupportingFixedLayers;
365     if (supportsFixedPositionLayers() && hasVisibleSlowRepaintFixedObjects(frameView))
366         mainThreadScrollingReasons |= HasNonLayerFixedObjects;
367     if (m_page->mainFrame()->document()->isImageDocument())
368         mainThreadScrollingReasons |= IsImageDocument;
369
370     return mainThreadScrollingReasons;
371 }
372
373 void ScrollingCoordinator::updateShouldUpdateScrollLayerPositionOnMainThread()
374 {
375     setShouldUpdateScrollLayerPositionOnMainThread(mainThreadScrollingReasons());
376 }
377
378 void ScrollingCoordinator::setForceMainThreadScrollLayerPositionUpdates(bool forceMainThreadScrollLayerPositionUpdates)
379 {
380     if (m_forceMainThreadScrollLayerPositionUpdates == forceMainThreadScrollLayerPositionUpdates)
381         return;
382
383     m_forceMainThreadScrollLayerPositionUpdates = forceMainThreadScrollLayerPositionUpdates;
384     updateShouldUpdateScrollLayerPositionOnMainThread();
385 }
386
387 ScrollingNodeID ScrollingCoordinator::uniqueScrollLayerID()
388 {
389     static ScrollingNodeID uniqueScrollLayerID = 1;
390     return uniqueScrollLayerID++;
391 }
392
393 String ScrollingCoordinator::scrollingStateTreeAsText() const
394 {
395     return String();
396 }
397
398 String ScrollingCoordinator::mainThreadScrollingReasonsAsText(MainThreadScrollingReasons reasons)
399 {
400     StringBuilder stringBuilder;
401
402     if (reasons & ScrollingCoordinator::ForcedOnMainThread)
403         stringBuilder.append("Forced on main thread, ");
404     if (reasons & ScrollingCoordinator::HasSlowRepaintObjects)
405         stringBuilder.append("Has slow repaint objects, ");
406     if (reasons & ScrollingCoordinator::HasViewportConstrainedObjectsWithoutSupportingFixedLayers)
407         stringBuilder.append("Has viewport constrained objects without supporting fixed layers, ");
408     if (reasons & ScrollingCoordinator::HasNonLayerFixedObjects)
409         stringBuilder.append("Has non-layer fixed objects, ");
410     if (reasons & ScrollingCoordinator::IsImageDocument)
411         stringBuilder.append("Is image document, ");
412
413     if (stringBuilder.length())
414         stringBuilder.resize(stringBuilder.length() - 2);
415     return stringBuilder.toString();
416 }
417
418 String ScrollingCoordinator::mainThreadScrollingReasonsAsText() const
419 {
420     return mainThreadScrollingReasonsAsText(mainThreadScrollingReasons());
421 }
422
423 } // namespace WebCore