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