Make it possible to compute a region for elements on the page that have wheel event...
[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     Region wheelHandlerRegion = frame.document()->absoluteRegionForEventTargets(frame.document()->wheelEventTargets());
158     wheelHandlerRegion.translate(toIntSize(offset));
159     nonFastScrollableRegion.unite(wheelHandlerRegion);
160
161     return nonFastScrollableRegion;
162 #endif
163 }
164
165 unsigned ScrollingCoordinator::computeCurrentWheelEventHandlerCount()
166 {
167     unsigned wheelEventHandlerCount = 0;
168
169     for (Frame* frame = &m_page->mainFrame(); frame; frame = frame->tree().traverseNext()) {
170         if (frame->document())
171             wheelEventHandlerCount += frame->document()->wheelEventHandlerCount();
172     }
173
174     return wheelEventHandlerCount;
175 }
176
177 void ScrollingCoordinator::frameViewWheelEventHandlerCountChanged(FrameView& frameView)
178 {
179     ASSERT(isMainThread());
180     ASSERT(m_page);
181
182     recomputeWheelEventHandlerCountForFrameView(frameView);
183 }
184
185 void ScrollingCoordinator::frameViewHasSlowRepaintObjectsDidChange(FrameView& frameView)
186 {
187     ASSERT(isMainThread());
188     ASSERT(m_page);
189
190     if (!coordinatesScrollingForFrameView(frameView))
191         return;
192
193     updateSynchronousScrollingReasons(frameView);
194 }
195
196 void ScrollingCoordinator::frameViewFixedObjectsDidChange(FrameView& frameView)
197 {
198     ASSERT(isMainThread());
199     ASSERT(m_page);
200
201     if (!coordinatesScrollingForFrameView(frameView))
202         return;
203
204     updateSynchronousScrollingReasons(frameView);
205 }
206
207 GraphicsLayer* ScrollingCoordinator::scrollLayerForScrollableArea(ScrollableArea& scrollableArea)
208 {
209     return scrollableArea.layerForScrolling();
210 }
211
212 GraphicsLayer* ScrollingCoordinator::scrollLayerForFrameView(FrameView& frameView)
213 {
214     if (RenderView* renderView = frameView.frame().contentRenderer())
215         return renderView->compositor().scrollLayer();
216     return nullptr;
217 }
218
219 GraphicsLayer* ScrollingCoordinator::headerLayerForFrameView(FrameView& frameView)
220 {
221 #if ENABLE(RUBBER_BANDING)
222     if (RenderView* renderView = frameView.frame().contentRenderer())
223         return renderView->compositor().headerLayer();
224     return nullptr;
225 #else
226     UNUSED_PARAM(frameView);
227     return nullptr;
228 #endif
229 }
230
231 GraphicsLayer* ScrollingCoordinator::footerLayerForFrameView(FrameView& frameView)
232 {
233 #if ENABLE(RUBBER_BANDING)
234     if (RenderView* renderView = frameView.frame().contentRenderer())
235         return renderView->compositor().footerLayer();
236     return nullptr;
237 #else
238     UNUSED_PARAM(frameView);
239     return nullptr;
240 #endif
241 }
242
243 GraphicsLayer* ScrollingCoordinator::counterScrollingLayerForFrameView(FrameView& frameView)
244 {
245     if (RenderView* renderView = frameView.frame().contentRenderer())
246         return renderView->compositor().fixedRootBackgroundLayer();
247     return nullptr;
248 }
249
250 GraphicsLayer* ScrollingCoordinator::insetClipLayerForFrameView(FrameView& frameView)
251 {
252     if (RenderView* renderView = frameView.frame().contentRenderer())
253         return renderView->compositor().clipLayer();
254     return nullptr;
255 }
256
257 GraphicsLayer* ScrollingCoordinator::contentShadowLayerForFrameView(FrameView& frameView)
258 {
259 #if ENABLE(RUBBER_BANDING)
260     if (RenderView* renderView = frameView.frame().contentRenderer())
261         return renderView->compositor().layerForContentShadow();
262     
263     return nullptr;
264 #else
265     UNUSED_PARAM(frameView);
266     return nullptr;
267 #endif
268 }
269
270 GraphicsLayer* ScrollingCoordinator::rootContentLayerForFrameView(FrameView& frameView)
271 {
272     if (RenderView* renderView = frameView.frame().contentRenderer())
273         return renderView->compositor().rootContentLayer();
274     return nullptr;
275 }
276
277 void ScrollingCoordinator::frameViewRootLayerDidChange(FrameView& frameView)
278 {
279     ASSERT(isMainThread());
280     ASSERT(m_page);
281
282     if (!coordinatesScrollingForFrameView(frameView))
283         return;
284
285     frameViewLayoutUpdated(frameView);
286     recomputeWheelEventHandlerCountForFrameView(frameView);
287     updateSynchronousScrollingReasons(frameView);
288 }
289
290 #if PLATFORM(COCOA)
291 void ScrollingCoordinator::handleWheelEventPhase(PlatformWheelEventPhase phase)
292 {
293     ASSERT(isMainThread());
294
295     if (!m_page)
296         return;
297
298     FrameView* frameView = m_page->mainFrame().view();
299     if (!frameView)
300         return;
301
302     frameView->scrollAnimator().handleWheelEventPhase(phase);
303 }
304 #endif
305
306 bool ScrollingCoordinator::hasVisibleSlowRepaintViewportConstrainedObjects(const FrameView& frameView) const
307 {
308     const FrameView::ViewportConstrainedObjectSet* viewportConstrainedObjects = frameView.viewportConstrainedObjects();
309     if (!viewportConstrainedObjects)
310         return false;
311
312     for (FrameView::ViewportConstrainedObjectSet::const_iterator it = viewportConstrainedObjects->begin(), end = viewportConstrainedObjects->end(); it != end; ++it) {
313         RenderObject& viewportConstrainedObject = **it;
314         if (!is<RenderBoxModelObject>(viewportConstrainedObject) || !viewportConstrainedObject.hasLayer())
315             return true;
316         RenderLayer& layer = *downcast<RenderBoxModelObject>(viewportConstrainedObject).layer();
317         // Any explicit reason that a fixed position element is not composited shouldn't cause slow scrolling.
318         if (!layer.isComposited() && layer.viewportConstrainedNotCompositedReason() == RenderLayer::NoNotCompositedReason)
319             return true;
320     }
321     return false;
322 }
323
324 SynchronousScrollingReasons ScrollingCoordinator::synchronousScrollingReasons(const FrameView& frameView) const
325 {
326     SynchronousScrollingReasons synchronousScrollingReasons = (SynchronousScrollingReasons)0;
327
328     if (m_forceSynchronousScrollLayerPositionUpdates)
329         synchronousScrollingReasons |= ForcedOnMainThread;
330 #if ENABLE(WEB_REPLAY)
331     InputCursor& cursor = m_page->replayController().activeInputCursor();
332     if (cursor.isCapturing() || cursor.isReplaying())
333         synchronousScrollingReasons |= ForcedOnMainThread;
334 #endif
335     if (frameView.hasSlowRepaintObjects())
336         synchronousScrollingReasons |= HasSlowRepaintObjects;
337     if (!supportsFixedPositionLayers() && frameView.hasViewportConstrainedObjects())
338         synchronousScrollingReasons |= HasViewportConstrainedObjectsWithoutSupportingFixedLayers;
339     if (supportsFixedPositionLayers() && hasVisibleSlowRepaintViewportConstrainedObjects(frameView))
340         synchronousScrollingReasons |= HasNonLayerViewportConstrainedObjects;
341     if (frameView.frame().mainFrame().document() && frameView.frame().document()->isImageDocument())
342         synchronousScrollingReasons |= IsImageDocument;
343
344     return synchronousScrollingReasons;
345 }
346
347 void ScrollingCoordinator::updateSynchronousScrollingReasons(FrameView& frameView)
348 {
349     // FIXME: Once we support async scrolling of iframes, we'll have to track the synchronous scrolling
350     // reasons per frame (maybe on scrolling tree nodes).
351     if (!frameView.frame().isMainFrame())
352         return;
353
354     setSynchronousScrollingReasons(synchronousScrollingReasons(frameView));
355 }
356
357 void ScrollingCoordinator::setForceSynchronousScrollLayerPositionUpdates(bool forceSynchronousScrollLayerPositionUpdates)
358 {
359     if (m_forceSynchronousScrollLayerPositionUpdates == forceSynchronousScrollLayerPositionUpdates)
360         return;
361
362     m_forceSynchronousScrollLayerPositionUpdates = forceSynchronousScrollLayerPositionUpdates;
363     if (FrameView* frameView = m_page->mainFrame().view())
364         updateSynchronousScrollingReasons(*frameView);
365 }
366
367 bool ScrollingCoordinator::shouldUpdateScrollLayerPositionSynchronously() const
368 {
369     if (FrameView* frameView = m_page->mainFrame().view())
370         return synchronousScrollingReasons(*frameView);
371     return true;
372 }
373
374 #if ENABLE(WEB_REPLAY)
375 void ScrollingCoordinator::replaySessionStateDidChange()
376 {
377     // FIXME: Once we support async scrolling of iframes, this should go through all subframes.
378     if (FrameView* frameView = m_page->mainFrame().view())
379         updateSynchronousScrollingReasons(*frameView);
380 }
381 #endif
382
383 ScrollingNodeID ScrollingCoordinator::uniqueScrollLayerID()
384 {
385     static ScrollingNodeID uniqueScrollLayerID = 1;
386     return uniqueScrollLayerID++;
387 }
388
389 String ScrollingCoordinator::scrollingStateTreeAsText() const
390 {
391     return String();
392 }
393
394 String ScrollingCoordinator::synchronousScrollingReasonsAsText(SynchronousScrollingReasons reasons)
395 {
396     StringBuilder stringBuilder;
397
398     if (reasons & ScrollingCoordinator::ForcedOnMainThread)
399         stringBuilder.appendLiteral("Forced on main thread, ");
400     if (reasons & ScrollingCoordinator::HasSlowRepaintObjects)
401         stringBuilder.appendLiteral("Has slow repaint objects, ");
402     if (reasons & ScrollingCoordinator::HasViewportConstrainedObjectsWithoutSupportingFixedLayers)
403         stringBuilder.appendLiteral("Has viewport constrained objects without supporting fixed layers, ");
404     if (reasons & ScrollingCoordinator::HasNonLayerViewportConstrainedObjects)
405         stringBuilder.appendLiteral("Has non-layer viewport-constrained objects, ");
406     if (reasons & ScrollingCoordinator::IsImageDocument)
407         stringBuilder.appendLiteral("Is image document, ");
408
409     if (stringBuilder.length())
410         stringBuilder.resize(stringBuilder.length() - 2);
411     return stringBuilder.toString();
412 }
413
414 String ScrollingCoordinator::synchronousScrollingReasonsAsText() const
415 {
416     if (FrameView* frameView = m_page->mainFrame().view())
417         return synchronousScrollingReasonsAsText(synchronousScrollingReasons(*frameView));
418
419     return String();
420 }
421
422 } // namespace WebCore