Remove scrolling tree dependency on wheel event handler counts, and use fast scrollin...
[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 "FrameView.h"
32 #include "GraphicsLayer.h"
33 #include "IntRect.h"
34 #include "MainFrame.h"
35 #include "Page.h"
36 #include "PlatformWheelEvent.h"
37 #include "PluginViewBase.h"
38 #include "Region.h"
39 #include "RenderLayerCompositor.h"
40 #include "RenderView.h"
41 #include "ScrollAnimator.h"
42 #include "Settings.h"
43 #include <wtf/MainThread.h>
44 #include <wtf/text/StringBuilder.h>
45
46 #if USE(COORDINATED_GRAPHICS)
47 #include "ScrollingCoordinatorCoordinatedGraphics.h"
48 #endif
49
50 #if ENABLE(WEB_REPLAY)
51 #include "ReplayController.h"
52 #include <replay/InputCursor.h>
53 #endif
54
55 namespace WebCore {
56
57 #if !PLATFORM(COCOA)
58 PassRefPtr<ScrollingCoordinator> ScrollingCoordinator::create(Page* page)
59 {
60 #if USE(COORDINATED_GRAPHICS)
61     return adoptRef(new ScrollingCoordinatorCoordinatedGraphics(page));
62 #endif
63
64     return adoptRef(new ScrollingCoordinator(page));
65 }
66 #endif
67
68 ScrollingCoordinator::ScrollingCoordinator(Page* page)
69     : m_page(page)
70     , m_forceSynchronousScrollLayerPositionUpdates(false)
71 {
72 }
73
74 ScrollingCoordinator::~ScrollingCoordinator()
75 {
76     ASSERT(!m_page);
77 }
78
79 void ScrollingCoordinator::pageDestroyed()
80 {
81     ASSERT(m_page);
82     m_page = nullptr;
83 }
84
85 bool ScrollingCoordinator::coordinatesScrollingForFrameView(const FrameView& frameView) const
86 {
87     ASSERT(isMainThread());
88     ASSERT(m_page);
89
90     if (!frameView.frame().isMainFrame() && !m_page->settings().scrollingTreeIncludesFrames())
91         return false;
92
93     RenderView* renderView = m_page->mainFrame().contentRenderer();
94     if (!renderView)
95         return false;
96     return renderView->usesCompositing();
97 }
98
99 Region ScrollingCoordinator::computeNonFastScrollableRegion(const Frame& frame, const IntPoint& frameLocation) const
100 {
101     RenderView* renderView = frame.contentRenderer();
102     if (!renderView || renderView->documentBeingDestroyed())
103         return Region();
104
105 #if ENABLE(IOS_TOUCH_EVENTS)
106     // On iOS, we use nonFastScrollableRegion to represent the region covered by elements with touch event handlers.
107     ASSERT(frame.isMainFrame());
108     UNUSED_PARAM(frameLocation);
109
110     Document* document = frame.document();
111     if (!document)
112         return Region();
113
114     Vector<IntRect> touchRects;
115     document->getTouchRects(touchRects);
116     
117     Region touchRegion;
118     for (const auto& rect : touchRects)
119         touchRegion.unite(rect);
120
121     // FIXME: use absoluteRegionForEventTargets().
122     return touchRegion;
123 #else
124     Region nonFastScrollableRegion;
125     FrameView* frameView = frame.view();
126     if (!frameView)
127         return nonFastScrollableRegion;
128
129     IntPoint offset = frameLocation;
130     offset.moveBy(frameView->frameRect().location());
131     offset.move(0, frameView->topContentInset());
132
133     if (const FrameView::ScrollableAreaSet* scrollableAreas = frameView->scrollableAreas()) {
134         for (FrameView::ScrollableAreaSet::const_iterator it = scrollableAreas->begin(), end = scrollableAreas->end(); it != end; ++it) {
135             ScrollableArea* scrollableArea = *it;
136             // Composited scrollable areas can be scrolled off the main thread.
137             if (scrollableArea->usesCompositedScrolling())
138                 continue;
139             IntRect box = scrollableArea->scrollableAreaBoundingBox();
140             box.moveBy(offset);
141             nonFastScrollableRegion.unite(box);
142         }
143     }
144
145     for (const auto& child : frameView->children()) {
146         if (!is<PluginViewBase>(*child))
147             continue;
148         PluginViewBase& pluginViewBase = downcast<PluginViewBase>(*child);
149         if (pluginViewBase.wantsWheelEvents())
150             nonFastScrollableRegion.unite(pluginViewBase.frameRect());
151     }
152
153     for (Frame* subframe = frame.tree().firstChild(); subframe; subframe = subframe->tree().nextSibling())
154         nonFastScrollableRegion.unite(computeNonFastScrollableRegion(*subframe, offset));
155
156     // Include wheel event handler region for the main frame.
157     Document::RegionFixedPair wheelHandlerRegion = frame.document()->absoluteRegionForEventTargets(frame.document()->wheelEventTargets());
158     bool wheelHandlerInFixedContent = wheelHandlerRegion.second;
159     if (wheelHandlerInFixedContent) {
160         // FIXME: if a fixed element has a wheel event handler, for now just cover the entire document
161         // with the slow-scrolling region. This could be improved.
162         // FIXME: need to handle position:sticky here too.
163         bool inFixed;
164         wheelHandlerRegion.first.unite(enclosingIntRect(frame.document()->absoluteEventHandlerBounds(inFixed)));
165     }
166     wheelHandlerRegion.first.translate(toIntSize(offset));
167     nonFastScrollableRegion.unite(wheelHandlerRegion.first);
168
169     return nonFastScrollableRegion;
170 #endif
171 }
172
173 void ScrollingCoordinator::frameViewHasSlowRepaintObjectsDidChange(FrameView& frameView)
174 {
175     ASSERT(isMainThread());
176     ASSERT(m_page);
177
178     if (!coordinatesScrollingForFrameView(frameView))
179         return;
180
181     updateSynchronousScrollingReasons(frameView);
182 }
183
184 void ScrollingCoordinator::frameViewFixedObjectsDidChange(FrameView& frameView)
185 {
186     ASSERT(isMainThread());
187     ASSERT(m_page);
188
189     if (!coordinatesScrollingForFrameView(frameView))
190         return;
191
192     updateSynchronousScrollingReasons(frameView);
193 }
194
195 GraphicsLayer* ScrollingCoordinator::scrollLayerForScrollableArea(ScrollableArea& scrollableArea)
196 {
197     return scrollableArea.layerForScrolling();
198 }
199
200 GraphicsLayer* ScrollingCoordinator::scrollLayerForFrameView(FrameView& frameView)
201 {
202     if (RenderView* renderView = frameView.frame().contentRenderer())
203         return renderView->compositor().scrollLayer();
204     return nullptr;
205 }
206
207 GraphicsLayer* ScrollingCoordinator::headerLayerForFrameView(FrameView& frameView)
208 {
209 #if ENABLE(RUBBER_BANDING)
210     if (RenderView* renderView = frameView.frame().contentRenderer())
211         return renderView->compositor().headerLayer();
212     return nullptr;
213 #else
214     UNUSED_PARAM(frameView);
215     return nullptr;
216 #endif
217 }
218
219 GraphicsLayer* ScrollingCoordinator::footerLayerForFrameView(FrameView& frameView)
220 {
221 #if ENABLE(RUBBER_BANDING)
222     if (RenderView* renderView = frameView.frame().contentRenderer())
223         return renderView->compositor().footerLayer();
224     return nullptr;
225 #else
226     UNUSED_PARAM(frameView);
227     return nullptr;
228 #endif
229 }
230
231 GraphicsLayer* ScrollingCoordinator::counterScrollingLayerForFrameView(FrameView& frameView)
232 {
233     if (RenderView* renderView = frameView.frame().contentRenderer())
234         return renderView->compositor().fixedRootBackgroundLayer();
235     return nullptr;
236 }
237
238 GraphicsLayer* ScrollingCoordinator::insetClipLayerForFrameView(FrameView& frameView)
239 {
240     if (RenderView* renderView = frameView.frame().contentRenderer())
241         return renderView->compositor().clipLayer();
242     return nullptr;
243 }
244
245 GraphicsLayer* ScrollingCoordinator::contentShadowLayerForFrameView(FrameView& frameView)
246 {
247 #if ENABLE(RUBBER_BANDING)
248     if (RenderView* renderView = frameView.frame().contentRenderer())
249         return renderView->compositor().layerForContentShadow();
250     
251     return nullptr;
252 #else
253     UNUSED_PARAM(frameView);
254     return nullptr;
255 #endif
256 }
257
258 GraphicsLayer* ScrollingCoordinator::rootContentLayerForFrameView(FrameView& frameView)
259 {
260     if (RenderView* renderView = frameView.frame().contentRenderer())
261         return renderView->compositor().rootContentLayer();
262     return nullptr;
263 }
264
265 void ScrollingCoordinator::frameViewRootLayerDidChange(FrameView& frameView)
266 {
267     ASSERT(isMainThread());
268     ASSERT(m_page);
269
270     if (!coordinatesScrollingForFrameView(frameView))
271         return;
272
273     frameViewLayoutUpdated(frameView);
274     updateSynchronousScrollingReasons(frameView);
275 }
276
277 #if PLATFORM(COCOA)
278 void ScrollingCoordinator::handleWheelEventPhase(PlatformWheelEventPhase phase)
279 {
280     ASSERT(isMainThread());
281
282     if (!m_page)
283         return;
284
285     FrameView* frameView = m_page->mainFrame().view();
286     if (!frameView)
287         return;
288
289     frameView->scrollAnimator().handleWheelEventPhase(phase);
290 }
291 #endif
292
293 bool ScrollingCoordinator::hasVisibleSlowRepaintViewportConstrainedObjects(const FrameView& frameView) const
294 {
295     const FrameView::ViewportConstrainedObjectSet* viewportConstrainedObjects = frameView.viewportConstrainedObjects();
296     if (!viewportConstrainedObjects)
297         return false;
298
299     for (FrameView::ViewportConstrainedObjectSet::const_iterator it = viewportConstrainedObjects->begin(), end = viewportConstrainedObjects->end(); it != end; ++it) {
300         RenderObject& viewportConstrainedObject = **it;
301         if (!is<RenderBoxModelObject>(viewportConstrainedObject) || !viewportConstrainedObject.hasLayer())
302             return true;
303         RenderLayer& layer = *downcast<RenderBoxModelObject>(viewportConstrainedObject).layer();
304         // Any explicit reason that a fixed position element is not composited shouldn't cause slow scrolling.
305         if (!layer.isComposited() && layer.viewportConstrainedNotCompositedReason() == RenderLayer::NoNotCompositedReason)
306             return true;
307     }
308     return false;
309 }
310
311 SynchronousScrollingReasons ScrollingCoordinator::synchronousScrollingReasons(const FrameView& frameView) const
312 {
313     SynchronousScrollingReasons synchronousScrollingReasons = (SynchronousScrollingReasons)0;
314
315     if (m_forceSynchronousScrollLayerPositionUpdates)
316         synchronousScrollingReasons |= ForcedOnMainThread;
317 #if ENABLE(WEB_REPLAY)
318     InputCursor& cursor = m_page->replayController().activeInputCursor();
319     if (cursor.isCapturing() || cursor.isReplaying())
320         synchronousScrollingReasons |= ForcedOnMainThread;
321 #endif
322     if (frameView.hasSlowRepaintObjects())
323         synchronousScrollingReasons |= HasSlowRepaintObjects;
324     if (!supportsFixedPositionLayers() && frameView.hasViewportConstrainedObjects())
325         synchronousScrollingReasons |= HasViewportConstrainedObjectsWithoutSupportingFixedLayers;
326     if (supportsFixedPositionLayers() && hasVisibleSlowRepaintViewportConstrainedObjects(frameView))
327         synchronousScrollingReasons |= HasNonLayerViewportConstrainedObjects;
328     if (frameView.frame().mainFrame().document() && frameView.frame().document()->isImageDocument())
329         synchronousScrollingReasons |= IsImageDocument;
330
331     return synchronousScrollingReasons;
332 }
333
334 void ScrollingCoordinator::updateSynchronousScrollingReasons(FrameView& frameView)
335 {
336     // FIXME: Once we support async scrolling of iframes, we'll have to track the synchronous scrolling
337     // reasons per frame (maybe on scrolling tree nodes).
338     if (!frameView.frame().isMainFrame())
339         return;
340
341     setSynchronousScrollingReasons(synchronousScrollingReasons(frameView));
342 }
343
344 void ScrollingCoordinator::setForceSynchronousScrollLayerPositionUpdates(bool forceSynchronousScrollLayerPositionUpdates)
345 {
346     if (m_forceSynchronousScrollLayerPositionUpdates == forceSynchronousScrollLayerPositionUpdates)
347         return;
348
349     m_forceSynchronousScrollLayerPositionUpdates = forceSynchronousScrollLayerPositionUpdates;
350     if (FrameView* frameView = m_page->mainFrame().view())
351         updateSynchronousScrollingReasons(*frameView);
352 }
353
354 bool ScrollingCoordinator::shouldUpdateScrollLayerPositionSynchronously() const
355 {
356     if (FrameView* frameView = m_page->mainFrame().view())
357         return synchronousScrollingReasons(*frameView);
358     return true;
359 }
360
361 #if ENABLE(WEB_REPLAY)
362 void ScrollingCoordinator::replaySessionStateDidChange()
363 {
364     // FIXME: Once we support async scrolling of iframes, this should go through all subframes.
365     if (FrameView* frameView = m_page->mainFrame().view())
366         updateSynchronousScrollingReasons(*frameView);
367 }
368 #endif
369
370 ScrollingNodeID ScrollingCoordinator::uniqueScrollLayerID()
371 {
372     static ScrollingNodeID uniqueScrollLayerID = 1;
373     return uniqueScrollLayerID++;
374 }
375
376 String ScrollingCoordinator::scrollingStateTreeAsText() const
377 {
378     return String();
379 }
380
381 String ScrollingCoordinator::synchronousScrollingReasonsAsText(SynchronousScrollingReasons reasons)
382 {
383     StringBuilder stringBuilder;
384
385     if (reasons & ScrollingCoordinator::ForcedOnMainThread)
386         stringBuilder.appendLiteral("Forced on main thread, ");
387     if (reasons & ScrollingCoordinator::HasSlowRepaintObjects)
388         stringBuilder.appendLiteral("Has slow repaint objects, ");
389     if (reasons & ScrollingCoordinator::HasViewportConstrainedObjectsWithoutSupportingFixedLayers)
390         stringBuilder.appendLiteral("Has viewport constrained objects without supporting fixed layers, ");
391     if (reasons & ScrollingCoordinator::HasNonLayerViewportConstrainedObjects)
392         stringBuilder.appendLiteral("Has non-layer viewport-constrained objects, ");
393     if (reasons & ScrollingCoordinator::IsImageDocument)
394         stringBuilder.appendLiteral("Is image document, ");
395
396     if (stringBuilder.length())
397         stringBuilder.resize(stringBuilder.length() - 2);
398     return stringBuilder.toString();
399 }
400
401 String ScrollingCoordinator::synchronousScrollingReasonsAsText() const
402 {
403     if (FrameView* frameView = m_page->mainFrame().view())
404         return synchronousScrollingReasonsAsText(synchronousScrollingReasons(*frameView));
405
406     return String();
407 }
408
409 } // namespace WebCore