Prepare scrolling tree to handle > 1 scrolling node
[WebKit-https.git] / Source / WebCore / page / scrolling / AsyncScrollingCoordinator.cpp
1 /*
2  * Copyright (C) 2014 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 #if ENABLE(ASYNC_SCROLLING)
29 #include "AsyncScrollingCoordinator.h"
30
31 #include "Frame.h"
32 #include "FrameView.h"
33 #include "GraphicsLayer.h"
34 #include "MainFrame.h"
35 #include "Page.h"
36 #include "ScrollingConstraints.h"
37 #include "ScrollingStateFixedNode.h"
38 #include "ScrollingStateScrollingNode.h"
39 #include "ScrollingStateStickyNode.h"
40 #include "ScrollingStateTree.h"
41
42 namespace WebCore {
43
44 AsyncScrollingCoordinator::AsyncScrollingCoordinator(Page* page)
45     : ScrollingCoordinator(page)
46     , m_updateNodeScrollPositionTimer(this, &AsyncScrollingCoordinator::updateScrollPositionAfterAsyncScrollTimerFired)
47     , m_scrollingStateTree(ScrollingStateTree::create(this))
48 {
49 }
50
51 AsyncScrollingCoordinator::~AsyncScrollingCoordinator()
52 {
53 }
54
55 void AsyncScrollingCoordinator::scrollingStateTreePropertiesChanged()
56 {
57     scheduleTreeStateCommit();
58 }
59
60 void AsyncScrollingCoordinator::frameViewLayoutUpdated(FrameView* frameView)
61 {
62     ASSERT(isMainThread());
63     ASSERT(m_page);
64
65     // If there isn't a root node yet, don't do anything. We'll be called again after creating one.
66     if (!m_scrollingStateTree->rootStateNode())
67         return;
68
69     // Compute the region of the page that we can't do fast scrolling for. This currently includes
70     // all scrollable areas, such as subframes, overflow divs and list boxes. We need to do this even if the
71     // frame view whose layout was updated is not the main frame.
72     Region nonFastScrollableRegion = computeNonFastScrollableRegion(&m_page->mainFrame(), IntPoint());
73
74     // In the future, we may want to have the ability to set non-fast scrolling regions for more than
75     // just the root node. But right now, this concept only applies to the root.
76     setNonFastScrollableRegionForNode(nonFastScrollableRegion, m_scrollingStateTree->rootStateNode());
77
78     if (!coordinatesScrollingForFrameView(frameView))
79         return;
80
81     ScrollingStateScrollingNode* node = toScrollingStateScrollingNode(m_scrollingStateTree->stateNodeForID(frameView->scrollLayerID()));
82     if (!node)
83         return;
84
85     Scrollbar* verticalScrollbar = frameView->verticalScrollbar();
86     Scrollbar* horizontalScrollbar = frameView->horizontalScrollbar();
87     setScrollbarPaintersFromScrollbarsForNode(verticalScrollbar, horizontalScrollbar, node);
88
89     node->setFrameScaleFactor(frameView->frame().frameScaleFactor());
90     node->setHeaderHeight(frameView->headerHeight());
91     node->setFooterHeight(frameView->footerHeight());
92
93     node->setScrollOrigin(frameView->scrollOrigin());
94     node->setViewportRect(IntRect(IntPoint(), frameView->visibleContentRect().size()));
95     node->setTotalContentsSize(frameView->totalContentsSize());
96
97     ScrollableAreaParameters scrollParameters;
98     scrollParameters.horizontalScrollElasticity = frameView->horizontalScrollElasticity();
99     scrollParameters.verticalScrollElasticity = frameView->verticalScrollElasticity();
100     scrollParameters.hasEnabledHorizontalScrollbar = horizontalScrollbar && horizontalScrollbar->enabled();
101     scrollParameters.hasEnabledVerticalScrollbar = verticalScrollbar && verticalScrollbar->enabled();
102     scrollParameters.horizontalScrollbarMode = frameView->horizontalScrollbarMode();
103     scrollParameters.verticalScrollbarMode = frameView->verticalScrollbarMode();
104
105     node->setScrollableAreaParameters(scrollParameters);
106 }
107
108 void AsyncScrollingCoordinator::frameViewRootLayerDidChange(FrameView* frameView)
109 {
110     ASSERT(isMainThread());
111     ASSERT(m_page);
112
113     if (!coordinatesScrollingForFrameView(frameView))
114         return;
115
116     // If the root layer does not have a ScrollingStateNode, then we should create one.
117     ensureRootStateNodeForFrameView(frameView);
118     ASSERT(m_scrollingStateTree->rootStateNode());
119
120     ScrollingCoordinator::frameViewRootLayerDidChange(frameView);
121
122     ScrollingStateScrollingNode* node = toScrollingStateScrollingNode(m_scrollingStateTree->stateNodeForID(frameView->scrollLayerID()));
123     setScrollLayerForNode(scrollLayerForFrameView(frameView), node);
124     setCounterScrollingLayerForNode(counterScrollingLayerForFrameView(frameView), node);
125     setHeaderLayerForNode(headerLayerForFrameView(frameView), node);
126     setFooterLayerForNode(footerLayerForFrameView(frameView), node);
127     setScrollBehaviorForFixedElementsForNode(frameView->scrollBehaviorForFixedElements(), node);
128 }
129
130 bool AsyncScrollingCoordinator::requestScrollPositionUpdate(FrameView* frameView, const IntPoint& scrollPosition)
131 {
132     ASSERT(isMainThread());
133     ASSERT(m_page);
134
135     if (!coordinatesScrollingForFrameView(frameView))
136         return false;
137
138     if (frameView->inProgrammaticScroll() || frameView->frame().document()->inPageCache())
139         updateScrollPositionAfterAsyncScroll(frameView->scrollLayerID(), scrollPosition, frameView->inProgrammaticScroll(), SetScrollingLayerPosition);
140
141     // If this frame view's document is being put into the page cache, we don't want to update our
142     // main frame scroll position. Just let the FrameView think that we did.
143     if (frameView->frame().document()->inPageCache())
144         return true;
145
146     ScrollingStateScrollingNode* stateNode = toScrollingStateScrollingNode(m_scrollingStateTree->stateNodeForID(frameView->scrollLayerID()));
147     if (!stateNode)
148         return false;
149
150     stateNode->setRequestedScrollPosition(scrollPosition, frameView->inProgrammaticScroll());
151     return true;
152 }
153
154 void AsyncScrollingCoordinator::scheduleUpdateScrollPositionAfterAsyncScroll(ScrollingNodeID nodeID, const IntPoint& scrollPosition, bool programmaticScroll, SetOrSyncScrollingLayerPosition scrollingLayerPositionAction)
155 {
156     ScheduledScrollUpdate scrollUpdate(nodeID, scrollPosition, programmaticScroll, scrollingLayerPositionAction);
157     
158     if (m_updateNodeScrollPositionTimer.isActive()) {
159         if (m_scheduledScrollUpdate.matchesUpdateType(scrollUpdate)) {
160             m_scheduledScrollUpdate.scrollPosition = scrollPosition;
161             return;
162         }
163     
164         // If the parameters don't match what was previosly scheduled, dispatch immediately.
165         m_updateNodeScrollPositionTimer.stop();
166         updateScrollPositionAfterAsyncScroll(m_scheduledScrollUpdate.nodeID, m_scheduledScrollUpdate.scrollPosition, m_scheduledScrollUpdate.isProgrammaticScroll, m_scheduledScrollUpdate.updateLayerPositionAction);
167         updateScrollPositionAfterAsyncScroll(nodeID, scrollPosition, programmaticScroll, scrollingLayerPositionAction);
168         return;
169     }
170
171     m_scheduledScrollUpdate = scrollUpdate;
172     m_updateNodeScrollPositionTimer.startOneShot(0);
173 }
174
175 void AsyncScrollingCoordinator::updateScrollPositionAfterAsyncScrollTimerFired(Timer<AsyncScrollingCoordinator>*)
176 {
177     updateScrollPositionAfterAsyncScroll(m_scheduledScrollUpdate.nodeID, m_scheduledScrollUpdate.scrollPosition, m_scheduledScrollUpdate.isProgrammaticScroll, m_scheduledScrollUpdate.updateLayerPositionAction);
178 }
179
180 void AsyncScrollingCoordinator::updateScrollPositionAfterAsyncScroll(ScrollingNodeID scrollingNodeID, const IntPoint& scrollPosition, bool programmaticScroll, SetOrSyncScrollingLayerPosition scrollingLayerPositionAction)
181 {
182     ASSERT(isMainThread());
183
184     if (!m_page)
185         return;
186
187     FrameView* frameView = m_page->mainFrame().view();
188     if (!frameView)
189         return;
190
191     bool oldProgrammaticScroll = frameView->inProgrammaticScroll();
192     frameView->setInProgrammaticScroll(programmaticScroll);
193
194     frameView->setConstrainsScrollingToContentEdge(false);
195     frameView->notifyScrollPositionChanged(scrollPosition);
196     frameView->setConstrainsScrollingToContentEdge(true);
197
198     frameView->setInProgrammaticScroll(oldProgrammaticScroll);
199
200 #if USE(ACCELERATED_COMPOSITING)
201     if (scrollingNodeID == frameView->scrollLayerID()) {
202         if (GraphicsLayer* scrollLayer = scrollLayerForFrameView(frameView)) {
203             GraphicsLayer* counterScrollingLayer = counterScrollingLayerForFrameView(frameView);
204             GraphicsLayer* headerLayer = headerLayerForFrameView(frameView);
205             GraphicsLayer* footerLayer = footerLayerForFrameView(frameView);
206             IntSize scrollOffsetForFixed = frameView->scrollOffsetForFixedPosition();
207
208             if (programmaticScroll || scrollingLayerPositionAction == SetScrollingLayerPosition) {
209                 scrollLayer->setPosition(-frameView->scrollPosition());
210                 if (counterScrollingLayer)
211                     counterScrollingLayer->setPosition(IntPoint(scrollOffsetForFixed));
212                 if (headerLayer)
213                     headerLayer->setPosition(FloatPoint(scrollOffsetForFixed.width(), 0));
214                 if (footerLayer)
215                     footerLayer->setPosition(FloatPoint(scrollOffsetForFixed.width(), frameView->totalContentsSize().height() - frameView->footerHeight()));
216             } else {
217                 scrollLayer->syncPosition(-frameView->scrollPosition());
218                 if (counterScrollingLayer)
219                     counterScrollingLayer->syncPosition(IntPoint(scrollOffsetForFixed));
220                 if (headerLayer)
221                     headerLayer->syncPosition(FloatPoint(scrollOffsetForFixed.width(), 0));
222                 if (footerLayer)
223                     footerLayer->syncPosition(FloatPoint(scrollOffsetForFixed.width(), frameView->totalContentsSize().height() - frameView->footerHeight()));
224
225                 LayoutRect viewportRect = frameView->viewportConstrainedVisibleContentRect();
226                 syncChildPositions(viewportRect);
227             }
228         }
229     }
230     // FIXME: handle non-main scrolling nodes.
231 #else
232     UNUSED_PARAM(scrollingLayerPositionAction);
233 #endif
234 }
235
236 void AsyncScrollingCoordinator::scrollableAreaScrollbarLayerDidChange(ScrollableArea* scrollableArea, ScrollbarOrientation orientation)
237 {
238     ASSERT(isMainThread());
239     ASSERT(m_page);
240
241     if (scrollableArea != static_cast<ScrollableArea*>(m_page->mainFrame().view()))
242         return;
243
244     if (orientation == VerticalScrollbar)
245         scrollableArea->verticalScrollbarLayerDidChange();
246     else
247         scrollableArea->horizontalScrollbarLayerDidChange();
248 }
249
250 ScrollingNodeID AsyncScrollingCoordinator::attachToStateTree(ScrollingNodeType nodeType, ScrollingNodeID newNodeID, ScrollingNodeID parentID)
251 {
252     return m_scrollingStateTree->attachNode(nodeType, newNodeID, parentID);
253 }
254
255 void AsyncScrollingCoordinator::detachFromStateTree(ScrollingNodeID nodeID)
256 {
257     m_scrollingStateTree->detachNode(nodeID);
258 }
259
260 void AsyncScrollingCoordinator::clearStateTree()
261 {
262     m_scrollingStateTree->clear();
263 }
264
265 void AsyncScrollingCoordinator::syncChildPositions(const LayoutRect& viewportRect)
266 {
267     if (!m_scrollingStateTree->rootStateNode())
268         return;
269
270     Vector<OwnPtr<ScrollingStateNode>>* children = m_scrollingStateTree->rootStateNode()->children();
271     if (!children)
272         return;
273
274     // FIXME: We'll have to traverse deeper into the tree at some point.
275     size_t size = children->size();
276     for (size_t i = 0; i < size; ++i) {
277         ScrollingStateNode* child = children->at(i).get();
278         child->syncLayerPositionForViewportRect(viewportRect);
279     }
280 }
281
282 void AsyncScrollingCoordinator::ensureRootStateNodeForFrameView(FrameView* frameView)
283 {
284     ASSERT(frameView->scrollLayerID());
285     attachToStateTree(ScrollingNode, frameView->scrollLayerID(), 0);
286 }
287
288 void AsyncScrollingCoordinator::updateScrollingNode(ScrollingNodeID nodeID, GraphicsLayer* scrollLayer, GraphicsLayer* counterScrollingLayer)
289 {
290     ScrollingStateScrollingNode* node = toScrollingStateScrollingNode(m_scrollingStateTree->stateNodeForID(nodeID));
291     ASSERT(node);
292     if (!node)
293         return;
294
295     node->setLayer(scrollLayer);
296     node->setCounterScrollingLayer(counterScrollingLayer);
297 }
298
299 void AsyncScrollingCoordinator::updateViewportConstrainedNode(ScrollingNodeID nodeID, const ViewportConstraints& constraints, GraphicsLayer* graphicsLayer)
300 {
301     ASSERT(supportsFixedPositionLayers());
302
303     ScrollingStateNode* node = m_scrollingStateTree->stateNodeForID(nodeID);
304     if (!node)
305         return;
306
307     switch (constraints.constraintType()) {
308     case ViewportConstraints::FixedPositionConstraint: {
309         ScrollingStateFixedNode* fixedNode = toScrollingStateFixedNode(node);
310         setScrollLayerForNode(graphicsLayer, fixedNode);
311         fixedNode->updateConstraints((const FixedPositionViewportConstraints&)constraints);
312         break;
313     }
314     case ViewportConstraints::StickyPositionConstraint: {
315         ScrollingStateStickyNode* stickyNode = toScrollingStateStickyNode(node);
316         setScrollLayerForNode(graphicsLayer, stickyNode);
317         stickyNode->updateConstraints((const StickyPositionViewportConstraints&)constraints);
318         break;
319     }
320     }
321 }
322
323 void AsyncScrollingCoordinator::setScrollLayerForNode(GraphicsLayer* scrollLayer, ScrollingStateNode* node)
324 {
325     node->setLayer(scrollLayer);
326 }
327
328 void AsyncScrollingCoordinator::setCounterScrollingLayerForNode(GraphicsLayer* layer, ScrollingStateScrollingNode* node)
329 {
330     node->setCounterScrollingLayer(layer);
331 }
332
333 void AsyncScrollingCoordinator::setHeaderLayerForNode(GraphicsLayer* headerLayer, ScrollingStateScrollingNode* node)
334 {
335     // Headers and footers are only supported on the root node.
336     ASSERT(node == m_scrollingStateTree->rootStateNode());
337     node->setHeaderLayer(headerLayer);
338 }
339
340 void AsyncScrollingCoordinator::setFooterLayerForNode(GraphicsLayer* footerLayer, ScrollingStateScrollingNode* node)
341 {
342     // Headers and footers are only supported on the root node.
343     ASSERT(node == m_scrollingStateTree->rootStateNode());
344     node->setFooterLayer(footerLayer);
345 }
346
347 void AsyncScrollingCoordinator::setNonFastScrollableRegionForNode(const Region& region, ScrollingStateScrollingNode* node)
348 {
349     node->setNonFastScrollableRegion(region);
350 }
351
352 void AsyncScrollingCoordinator::setWheelEventHandlerCountForNode(unsigned wheelEventHandlerCount, ScrollingStateScrollingNode* node)
353 {
354     node->setWheelEventHandlerCount(wheelEventHandlerCount);
355 }
356
357 void AsyncScrollingCoordinator::setScrollBehaviorForFixedElementsForNode(ScrollBehaviorForFixedElements behaviorForFixed, ScrollingStateScrollingNode* node)
358 {
359     node->setScrollBehaviorForFixedElements(behaviorForFixed);
360 }
361
362 // FIXME: not sure if this belongs here.
363 void AsyncScrollingCoordinator::setScrollbarPaintersFromScrollbarsForNode(Scrollbar* verticalScrollbar, Scrollbar* horizontalScrollbar, ScrollingStateScrollingNode* node)
364 {
365     node->setScrollbarPaintersFromScrollbars(verticalScrollbar, horizontalScrollbar);
366 }
367
368 void AsyncScrollingCoordinator::setSynchronousScrollingReasons(SynchronousScrollingReasons reasons)
369 {
370     if (!m_scrollingStateTree->rootStateNode())
371         return;
372
373     // The FrameView's GraphicsLayer is likely to be out-of-synch with the PlatformLayer
374     // at this point. So we'll update it before we switch back to main thread scrolling
375     // in order to avoid layer positioning bugs.
376     if (reasons)
377         updateMainFrameScrollLayerPosition();
378     m_scrollingStateTree->rootStateNode()->setSynchronousScrollingReasons(reasons);
379 }
380
381 void AsyncScrollingCoordinator::updateMainFrameScrollLayerPosition()
382 {
383     ASSERT(isMainThread());
384
385     if (!m_page)
386         return;
387
388     FrameView* frameView = m_page->mainFrame().view();
389     if (!frameView)
390         return;
391
392     if (GraphicsLayer* scrollLayer = scrollLayerForFrameView(frameView))
393         scrollLayer->setPosition(-frameView->scrollPosition());
394 }
395
396 void AsyncScrollingCoordinator::recomputeWheelEventHandlerCountForFrameView(FrameView* frameView)
397 {
398     ScrollingStateScrollingNode* node = toScrollingStateScrollingNode(m_scrollingStateTree->stateNodeForID(frameView->scrollLayerID()));
399     if (!node)
400         return;
401     setWheelEventHandlerCountForNode(computeCurrentWheelEventHandlerCount(), node);
402 }
403
404 bool AsyncScrollingCoordinator::isRubberBandInProgress() const
405 {
406     return scrollingTree()->isRubberBandInProgress();
407 }
408
409 void AsyncScrollingCoordinator::setScrollPinningBehavior(ScrollPinningBehavior pinning)
410 {
411     scrollingTree()->setScrollPinningBehavior(pinning);
412 }
413
414 String AsyncScrollingCoordinator::scrollingStateTreeAsText() const
415 {
416     if (m_scrollingStateTree->rootStateNode())
417         return m_scrollingStateTree->rootStateNode()->scrollingStateTreeAsText();
418
419     return String();
420 }
421
422 } // namespace WebCore
423
424 #endif // ENABLE(ASYNC_SCROLLING)