Expand test infrastructure to support scrolling tests
[WebKit-https.git] / Source / WebCore / page / scrolling / AsyncScrollingCoordinator.cpp
1 /*
2  * Copyright (C) 2014-2015 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 "EditorClient.h"
32 #include "Frame.h"
33 #include "FrameView.h"
34 #include "GraphicsLayer.h"
35 #include "MainFrame.h"
36 #include "Page.h"
37 #include "ScrollAnimator.h"
38 #include "ScrollingConstraints.h"
39 #include "ScrollingStateFixedNode.h"
40 #include "ScrollingStateFrameScrollingNode.h"
41 #include "ScrollingStateOverflowScrollingNode.h"
42 #include "ScrollingStateStickyNode.h"
43 #include "ScrollingStateTree.h"
44 #include "WheelEventTestTrigger.h"
45
46 namespace WebCore {
47
48 AsyncScrollingCoordinator::AsyncScrollingCoordinator(Page* page)
49     : ScrollingCoordinator(page)
50     , m_updateNodeScrollPositionTimer(*this, &AsyncScrollingCoordinator::updateScrollPositionAfterAsyncScrollTimerFired)
51     , m_scrollingStateTree(std::make_unique<ScrollingStateTree>(this))
52 {
53 }
54
55 AsyncScrollingCoordinator::~AsyncScrollingCoordinator()
56 {
57 }
58
59 void AsyncScrollingCoordinator::scrollingStateTreePropertiesChanged()
60 {
61     scheduleTreeStateCommit();
62 }
63
64 static inline void setStateScrollingNodeSnapOffsetsAsFloat(ScrollingStateScrollingNode& node, ScrollEventAxis axis, const Vector<LayoutUnit>& snapOffsets, float deviceScaleFactor)
65 {
66     // FIXME: Incorporate current page scale factor in snapping to device pixel. Perhaps we should just convert to float here and let UI process do the pixel snapping?
67     Vector<float> snapOffsetsAsFloat;
68     snapOffsetsAsFloat.reserveInitialCapacity(snapOffsets.size());
69     for (size_t i = 0; i < snapOffsets.size(); ++i)
70         snapOffsetsAsFloat.append(roundToDevicePixel(snapOffsets[i], deviceScaleFactor, false));
71
72     if (axis == ScrollEventAxis::Horizontal)
73         node.setHorizontalSnapOffsets(snapOffsetsAsFloat);
74     else
75         node.setVerticalSnapOffsets(snapOffsetsAsFloat);
76 }
77
78 void AsyncScrollingCoordinator::setNonFastScrollableRegionDirty()
79 {
80     m_nonFastScrollableRegionDirty = true;
81     // We have to schedule a commit, but the computed non-fast region may not have actually changed.
82     scheduleTreeStateCommit();
83 }
84
85 void AsyncScrollingCoordinator::willCommitTree()
86 {
87     updateNonFastScrollableRegion();
88 }
89
90 void AsyncScrollingCoordinator::updateNonFastScrollableRegion()
91 {
92     if (!m_nonFastScrollableRegionDirty)
93         return;
94
95     if (!m_scrollingStateTree->rootStateNode())
96         return;
97
98     m_scrollingStateTree->rootStateNode()->setNonFastScrollableRegion(computeNonFastScrollableRegion(m_page->mainFrame(), IntPoint()));
99     m_nonFastScrollableRegionDirty = false;
100 }
101
102 void AsyncScrollingCoordinator::frameViewLayoutUpdated(FrameView& frameView)
103 {
104     ASSERT(isMainThread());
105     ASSERT(m_page);
106
107     // If there isn't a root node yet, don't do anything. We'll be called again after creating one.
108     if (!m_scrollingStateTree->rootStateNode())
109         return;
110
111     // Compute the region of the page that we can't do fast scrolling for. This currently includes
112     // all scrollable areas, such as subframes, overflow divs and list boxes. We need to do this even if the
113     // frame view whose layout was updated is not the main frame.
114     // In the future, we may want to have the ability to set non-fast scrolling regions for more than
115     // just the root node. But right now, this concept only applies to the root.
116     m_scrollingStateTree->rootStateNode()->setNonFastScrollableRegion(computeNonFastScrollableRegion(m_page->mainFrame(), IntPoint()));
117     m_nonFastScrollableRegionDirty = false;
118
119     if (!coordinatesScrollingForFrameView(frameView))
120         return;
121
122     ScrollingStateFrameScrollingNode* node = downcast<ScrollingStateFrameScrollingNode>(m_scrollingStateTree->stateNodeForID(frameView.scrollLayerID()));
123     if (!node)
124         return;
125
126     Scrollbar* verticalScrollbar = frameView.verticalScrollbar();
127     Scrollbar* horizontalScrollbar = frameView.horizontalScrollbar();
128     node->setScrollbarPaintersFromScrollbars(verticalScrollbar, horizontalScrollbar);
129     
130     node->setFrameScaleFactor(frameView.frame().frameScaleFactor());
131     node->setHeaderHeight(frameView.headerHeight());
132     node->setFooterHeight(frameView.footerHeight());
133     node->setTopContentInset(frameView.topContentInset());
134
135     node->setScrollOrigin(frameView.scrollOrigin());
136     node->setScrollableAreaSize(frameView.visibleContentRect().size());
137     node->setTotalContentsSize(frameView.totalContentsSize());
138     node->setReachableContentsSize(frameView.totalContentsSize());
139
140 #if ENABLE(CSS_SCROLL_SNAP)
141     frameView.updateSnapOffsets();
142     if (const Vector<LayoutUnit>* horizontalSnapOffsets = frameView.horizontalSnapOffsets())
143         setStateScrollingNodeSnapOffsetsAsFloat(*node, ScrollEventAxis::Horizontal, *horizontalSnapOffsets, m_page->deviceScaleFactor());
144
145     if (const Vector<LayoutUnit>* verticalSnapOffsets = frameView.verticalSnapOffsets())
146         setStateScrollingNodeSnapOffsetsAsFloat(*node, ScrollEventAxis::Vertical, *verticalSnapOffsets, m_page->deviceScaleFactor());
147 #endif
148
149     ScrollableAreaParameters scrollParameters;
150     scrollParameters.horizontalScrollElasticity = frameView.horizontalScrollElasticity();
151     scrollParameters.verticalScrollElasticity = frameView.verticalScrollElasticity();
152     scrollParameters.hasEnabledHorizontalScrollbar = horizontalScrollbar && horizontalScrollbar->enabled();
153     scrollParameters.hasEnabledVerticalScrollbar = verticalScrollbar && verticalScrollbar->enabled();
154     scrollParameters.horizontalScrollbarMode = frameView.horizontalScrollbarMode();
155     scrollParameters.verticalScrollbarMode = frameView.verticalScrollbarMode();
156
157     node->setScrollableAreaParameters(scrollParameters);
158 }
159
160 void AsyncScrollingCoordinator::frameViewNonFastScrollableRegionChanged(FrameView&)
161 {
162     if (!m_scrollingStateTree->rootStateNode())
163         return;
164
165     setNonFastScrollableRegionDirty();
166 }
167
168 void AsyncScrollingCoordinator::frameViewRootLayerDidChange(FrameView& frameView)
169 {
170     ASSERT(isMainThread());
171     ASSERT(m_page);
172
173     if (!coordinatesScrollingForFrameView(frameView))
174         return;
175     
176     // FIXME: In some navigation scenarios, the FrameView has no RenderView or that RenderView has not been composited.
177     // This needs cleaning up: https://bugs.webkit.org/show_bug.cgi?id=132724
178     if (!frameView.scrollLayerID())
179         return;
180     
181     // If the root layer does not have a ScrollingStateNode, then we should create one.
182     ensureRootStateNodeForFrameView(frameView);
183     ASSERT(m_scrollingStateTree->rootStateNode());
184
185     ScrollingCoordinator::frameViewRootLayerDidChange(frameView);
186
187     ScrollingStateFrameScrollingNode* node = downcast<ScrollingStateFrameScrollingNode>(m_scrollingStateTree->stateNodeForID(frameView.scrollLayerID()));
188     node->setLayer(scrollLayerForFrameView(frameView));
189     node->setScrolledContentsLayer(rootContentLayerForFrameView(frameView));
190     node->setCounterScrollingLayer(counterScrollingLayerForFrameView(frameView));
191     node->setInsetClipLayer(insetClipLayerForFrameView(frameView));
192     node->setContentShadowLayer(contentShadowLayerForFrameView(frameView));
193     node->setHeaderLayer(headerLayerForFrameView(frameView));
194     node->setFooterLayer(footerLayerForFrameView(frameView));
195     node->setScrollBehaviorForFixedElements(frameView.scrollBehaviorForFixedElements());
196 }
197
198 bool AsyncScrollingCoordinator::requestScrollPositionUpdate(FrameView& frameView, const IntPoint& scrollPosition)
199 {
200     ASSERT(isMainThread());
201     ASSERT(m_page);
202
203     if (!coordinatesScrollingForFrameView(frameView))
204         return false;
205
206     bool isProgrammaticScroll = frameView.inProgrammaticScroll();
207     if (isProgrammaticScroll || frameView.frame().document()->inPageCache())
208         updateScrollPositionAfterAsyncScroll(frameView.scrollLayerID(), scrollPosition, isProgrammaticScroll, SetScrollingLayerPosition);
209
210     // If this frame view's document is being put into the page cache, we don't want to update our
211     // main frame scroll position. Just let the FrameView think that we did.
212     if (frameView.frame().document()->inPageCache())
213         return true;
214
215     ScrollingStateScrollingNode* stateNode = downcast<ScrollingStateScrollingNode>(m_scrollingStateTree->stateNodeForID(frameView.scrollLayerID()));
216     if (!stateNode)
217         return false;
218
219     stateNode->setRequestedScrollPosition(scrollPosition, isProgrammaticScroll);
220     return true;
221 }
222
223 void AsyncScrollingCoordinator::scheduleUpdateScrollPositionAfterAsyncScroll(ScrollingNodeID nodeID, const FloatPoint& scrollPosition, bool programmaticScroll, SetOrSyncScrollingLayerPosition scrollingLayerPositionAction)
224 {
225     ScheduledScrollUpdate scrollUpdate(nodeID, scrollPosition, programmaticScroll, scrollingLayerPositionAction);
226     
227     // For programmatic scrolls, requestScrollPositionUpdate() has already called updateScrollPositionAfterAsyncScroll().
228     if (programmaticScroll)
229         return;
230
231     if (m_updateNodeScrollPositionTimer.isActive()) {
232         if (m_scheduledScrollUpdate.matchesUpdateType(scrollUpdate)) {
233             m_scheduledScrollUpdate.scrollPosition = scrollPosition;
234             return;
235         }
236     
237         // If the parameters don't match what was previously scheduled, dispatch immediately.
238         m_updateNodeScrollPositionTimer.stop();
239         updateScrollPositionAfterAsyncScroll(m_scheduledScrollUpdate.nodeID, m_scheduledScrollUpdate.scrollPosition, m_scheduledScrollUpdate.isProgrammaticScroll, m_scheduledScrollUpdate.updateLayerPositionAction);
240         updateScrollPositionAfterAsyncScroll(nodeID, scrollPosition, programmaticScroll, scrollingLayerPositionAction);
241         return;
242     }
243
244     m_scheduledScrollUpdate = scrollUpdate;
245     m_updateNodeScrollPositionTimer.startOneShot(0);
246 }
247
248 void AsyncScrollingCoordinator::updateScrollPositionAfterAsyncScrollTimerFired()
249 {
250     updateScrollPositionAfterAsyncScroll(m_scheduledScrollUpdate.nodeID, m_scheduledScrollUpdate.scrollPosition, m_scheduledScrollUpdate.isProgrammaticScroll, m_scheduledScrollUpdate.updateLayerPositionAction);
251 }
252
253 FrameView* AsyncScrollingCoordinator::frameViewForScrollingNode(ScrollingNodeID scrollingNodeID) const
254 {
255     if (!m_scrollingStateTree->rootStateNode())
256         return nullptr;
257     
258     if (scrollingNodeID == m_scrollingStateTree->rootStateNode()->scrollingNodeID())
259         return m_page->mainFrame().view();
260
261     ScrollingStateNode* stateNode = m_scrollingStateTree->stateNodeForID(scrollingNodeID);
262     if (!stateNode)
263         return nullptr;
264
265     // Find the enclosing frame scrolling node.
266     ScrollingStateNode* parentNode = stateNode;
267     while (parentNode && parentNode->nodeType() != FrameScrollingNode)
268         parentNode = parentNode->parent();
269     
270     if (!parentNode)
271         return nullptr;
272     
273     // Walk the frame tree to find the matching FrameView. This is not ideal, but avoids back pointers to FrameViews
274     // from ScrollingTreeStateNodes.
275     for (Frame* frame = &m_page->mainFrame(); frame; frame = frame->tree().traverseNext()) {
276         if (FrameView* view = frame->view()) {
277             if (view->scrollLayerID() == parentNode->scrollingNodeID())
278                 return view;
279         }
280     }
281
282     return nullptr;
283 }
284
285 void AsyncScrollingCoordinator::updateScrollPositionAfterAsyncScroll(ScrollingNodeID scrollingNodeID, const FloatPoint& scrollPosition, bool programmaticScroll, SetOrSyncScrollingLayerPosition scrollingLayerPositionAction)
286 {
287     ASSERT(isMainThread());
288
289     if (!m_page)
290         return;
291
292     FrameView* frameViewPtr = frameViewForScrollingNode(scrollingNodeID);
293     if (!frameViewPtr)
294         return;
295
296     FrameView& frameView = *frameViewPtr;
297
298     if (scrollingNodeID == frameView.scrollLayerID()) {
299         bool oldProgrammaticScroll = frameView.inProgrammaticScroll();
300         frameView.setInProgrammaticScroll(programmaticScroll);
301
302         frameView.setConstrainsScrollingToContentEdge(false);
303         frameView.notifyScrollPositionChanged(roundedIntPoint(scrollPosition));
304         frameView.setConstrainsScrollingToContentEdge(true);
305
306         frameView.setInProgrammaticScroll(oldProgrammaticScroll);
307
308         if (GraphicsLayer* scrollLayer = scrollLayerForFrameView(frameView)) {
309             GraphicsLayer* counterScrollingLayer = counterScrollingLayerForFrameView(frameView);
310             GraphicsLayer* insetClipLayer = insetClipLayerForFrameView(frameView);
311             GraphicsLayer* contentShadowLayer = contentShadowLayerForFrameView(frameView);
312             GraphicsLayer* scrolledContentsLayer = rootContentLayerForFrameView(frameView);
313             GraphicsLayer* headerLayer = headerLayerForFrameView(frameView);
314             GraphicsLayer* footerLayer = footerLayerForFrameView(frameView);
315             LayoutSize scrollOffsetForFixed = frameView.scrollOffsetForFixedPosition();
316
317             float topContentInset = frameView.topContentInset();
318             FloatPoint positionForInsetClipLayer = FloatPoint(0, FrameView::yPositionForInsetClipLayer(scrollPosition, topContentInset));
319             FloatPoint positionForContentsLayer = FloatPoint(scrolledContentsLayer->position().x(),
320                 FrameView::yPositionForRootContentLayer(scrollPosition, topContentInset, frameView.headerHeight()));
321             FloatPoint positionForHeaderLayer = FloatPoint(scrollOffsetForFixed.width(), FrameView::yPositionForHeaderLayer(scrollPosition, topContentInset));
322             FloatPoint positionForFooterLayer = FloatPoint(scrollOffsetForFixed.width(),
323                 FrameView::yPositionForFooterLayer(scrollPosition, topContentInset, frameView.totalContentsSize().height(), frameView.footerHeight()));
324
325             if (programmaticScroll || scrollingLayerPositionAction == SetScrollingLayerPosition) {
326                 scrollLayer->setPosition(-frameView.scrollPosition());
327                 if (counterScrollingLayer)
328                     counterScrollingLayer->setPosition(toLayoutPoint(scrollOffsetForFixed));
329                 if (insetClipLayer)
330                     insetClipLayer->setPosition(positionForInsetClipLayer);
331                 if (contentShadowLayer)
332                     contentShadowLayer->setPosition(positionForContentsLayer);
333                 if (scrolledContentsLayer)
334                     scrolledContentsLayer->setPosition(positionForContentsLayer);
335                 if (headerLayer)
336                     headerLayer->setPosition(positionForHeaderLayer);
337                 if (footerLayer)
338                     footerLayer->setPosition(positionForFooterLayer);
339             } else {
340                 scrollLayer->syncPosition(-frameView.scrollPosition());
341                 if (counterScrollingLayer)
342                     counterScrollingLayer->syncPosition(toLayoutPoint(scrollOffsetForFixed));
343                 if (insetClipLayer)
344                     insetClipLayer->syncPosition(positionForInsetClipLayer);
345                 if (contentShadowLayer)
346                     contentShadowLayer->syncPosition(positionForContentsLayer);
347                 if (scrolledContentsLayer)
348                     scrolledContentsLayer->syncPosition(positionForContentsLayer);
349                 if (headerLayer)
350                     headerLayer->syncPosition(positionForHeaderLayer);
351                 if (footerLayer)
352                     footerLayer->syncPosition(positionForFooterLayer);
353
354                 LayoutRect viewportRect = frameView.viewportConstrainedVisibleContentRect();
355                 syncChildPositions(viewportRect);
356             }
357         }
358
359 #if PLATFORM(COCOA)
360         if (m_page->expectsWheelEventTriggers()) {
361             frameView.scrollAnimator().setWheelEventTestTrigger(m_page->testTrigger());
362             if (const auto& trigger = m_page->testTrigger())
363                 trigger->removeTestDeferralForReason(reinterpret_cast<WheelEventTestTrigger::ScrollableAreaIdentifier>(m_page), WheelEventTestTrigger::ScrollingThreadSyncNeeded);
364         }
365 #endif
366         
367         return;
368     }
369
370     // Overflow-scroll area.
371     if (ScrollableArea* scrollableArea = frameView.scrollableAreaForScrollLayerID(scrollingNodeID)) {
372         scrollableArea->setIsUserScroll(scrollingLayerPositionAction == SyncScrollingLayerPosition);
373         scrollableArea->scrollToOffsetWithoutAnimation(scrollPosition);
374         scrollableArea->setIsUserScroll(false);
375         if (scrollingLayerPositionAction == SetScrollingLayerPosition)
376             m_page->editorClient().overflowScrollPositionChanged();
377
378 #if PLATFORM(COCOA)
379         if (m_page->expectsWheelEventTriggers()) {
380             frameView.scrollAnimator().setWheelEventTestTrigger(m_page->testTrigger());
381             if (const auto& trigger = m_page->testTrigger())
382                 trigger->removeTestDeferralForReason(reinterpret_cast<WheelEventTestTrigger::ScrollableAreaIdentifier>(m_page), WheelEventTestTrigger::ScrollingThreadSyncNeeded);
383         }
384 #endif
385     }
386 }
387
388 void AsyncScrollingCoordinator::scrollableAreaScrollbarLayerDidChange(ScrollableArea& scrollableArea, ScrollbarOrientation orientation)
389 {
390     ASSERT(isMainThread());
391     ASSERT(m_page);
392
393     if (&scrollableArea != static_cast<ScrollableArea*>(m_page->mainFrame().view()))
394         return;
395
396     if (orientation == VerticalScrollbar)
397         scrollableArea.verticalScrollbarLayerDidChange();
398     else
399         scrollableArea.horizontalScrollbarLayerDidChange();
400 }
401
402 ScrollingNodeID AsyncScrollingCoordinator::attachToStateTree(ScrollingNodeType nodeType, ScrollingNodeID newNodeID, ScrollingNodeID parentID)
403 {
404     return m_scrollingStateTree->attachNode(nodeType, newNodeID, parentID);
405 }
406
407 void AsyncScrollingCoordinator::detachFromStateTree(ScrollingNodeID nodeID)
408 {
409     m_scrollingStateTree->detachNode(nodeID);
410 }
411
412 void AsyncScrollingCoordinator::clearStateTree()
413 {
414     m_scrollingStateTree->clear();
415 }
416
417 void AsyncScrollingCoordinator::syncChildPositions(const LayoutRect& viewportRect)
418 {
419     if (!m_scrollingStateTree->rootStateNode())
420         return;
421
422     auto children = m_scrollingStateTree->rootStateNode()->children();
423     if (!children)
424         return;
425
426     // FIXME: We'll have to traverse deeper into the tree at some point.
427     for (auto& child : *children)
428         child->syncLayerPositionForViewportRect(viewportRect);
429 }
430
431 void AsyncScrollingCoordinator::ensureRootStateNodeForFrameView(FrameView& frameView)
432 {
433     ASSERT(frameView.scrollLayerID());
434     attachToStateTree(FrameScrollingNode, frameView.scrollLayerID(), 0);
435 }
436
437 void AsyncScrollingCoordinator::updateFrameScrollingNode(ScrollingNodeID nodeID, GraphicsLayer* layer, GraphicsLayer* scrolledContentsLayer, GraphicsLayer* counterScrollingLayer, GraphicsLayer* insetClipLayer, const ScrollingGeometry* scrollingGeometry)
438 {
439     ScrollingStateFrameScrollingNode* node = downcast<ScrollingStateFrameScrollingNode>(m_scrollingStateTree->stateNodeForID(nodeID));
440     ASSERT(node);
441     if (!node)
442         return;
443
444     node->setLayer(layer);
445     node->setInsetClipLayer(insetClipLayer);
446     node->setScrolledContentsLayer(scrolledContentsLayer);
447     node->setCounterScrollingLayer(counterScrollingLayer);
448
449     if (scrollingGeometry) {
450         node->setScrollOrigin(scrollingGeometry->scrollOrigin);
451         node->setScrollPosition(scrollingGeometry->scrollPosition);
452         node->setTotalContentsSize(scrollingGeometry->contentSize);
453         node->setReachableContentsSize(scrollingGeometry->reachableContentSize);
454         node->setScrollableAreaSize(scrollingGeometry->scrollableAreaSize);
455     }
456 }
457     
458 void AsyncScrollingCoordinator::updateOverflowScrollingNode(ScrollingNodeID nodeID, GraphicsLayer* layer, GraphicsLayer* scrolledContentsLayer, const ScrollingGeometry* scrollingGeometry)
459 {
460     ScrollingStateOverflowScrollingNode* node = downcast<ScrollingStateOverflowScrollingNode>(m_scrollingStateTree->stateNodeForID(nodeID));
461     ASSERT(node);
462     if (!node)
463         return;
464
465     node->setLayer(layer);
466     node->setScrolledContentsLayer(scrolledContentsLayer);
467     
468     if (scrollingGeometry) {
469         node->setScrollOrigin(scrollingGeometry->scrollOrigin);
470         node->setScrollPosition(scrollingGeometry->scrollPosition);
471         node->setTotalContentsSize(scrollingGeometry->contentSize);
472         node->setReachableContentsSize(scrollingGeometry->reachableContentSize);
473         node->setScrollableAreaSize(scrollingGeometry->scrollableAreaSize);
474 #if ENABLE(CSS_SCROLL_SNAP)
475         setStateScrollingNodeSnapOffsetsAsFloat(*node, ScrollEventAxis::Horizontal, scrollingGeometry->horizontalSnapOffsets, m_page->deviceScaleFactor());
476         setStateScrollingNodeSnapOffsetsAsFloat(*node, ScrollEventAxis::Vertical, scrollingGeometry->verticalSnapOffsets, m_page->deviceScaleFactor());
477 #endif
478     }
479 }
480
481 void AsyncScrollingCoordinator::updateViewportConstrainedNode(ScrollingNodeID nodeID, const ViewportConstraints& constraints, GraphicsLayer* graphicsLayer)
482 {
483     ASSERT(supportsFixedPositionLayers());
484
485     ScrollingStateNode* node = m_scrollingStateTree->stateNodeForID(nodeID);
486     if (!node)
487         return;
488
489     switch (constraints.constraintType()) {
490     case ViewportConstraints::FixedPositionConstraint: {
491         ScrollingStateFixedNode& fixedNode = downcast<ScrollingStateFixedNode>(*node);
492         fixedNode.setLayer(graphicsLayer);
493         fixedNode.updateConstraints((const FixedPositionViewportConstraints&)constraints);
494         break;
495     }
496     case ViewportConstraints::StickyPositionConstraint: {
497         ScrollingStateStickyNode& stickyNode = downcast<ScrollingStateStickyNode>(*node);
498         stickyNode.setLayer(graphicsLayer);
499         stickyNode.updateConstraints((const StickyPositionViewportConstraints&)constraints);
500         break;
501     }
502     }
503 }
504
505 void AsyncScrollingCoordinator::setSynchronousScrollingReasons(SynchronousScrollingReasons reasons)
506 {
507     if (!m_scrollingStateTree->rootStateNode())
508         return;
509
510     // The FrameView's GraphicsLayer is likely to be out-of-synch with the PlatformLayer
511     // at this point. So we'll update it before we switch back to main thread scrolling
512     // in order to avoid layer positioning bugs.
513     if (reasons)
514         updateMainFrameScrollLayerPosition();
515     m_scrollingStateTree->rootStateNode()->setSynchronousScrollingReasons(reasons);
516 }
517
518 void AsyncScrollingCoordinator::updateMainFrameScrollLayerPosition()
519 {
520     ASSERT(isMainThread());
521
522     if (!m_page)
523         return;
524
525     FrameView* frameView = m_page->mainFrame().view();
526     if (!frameView)
527         return;
528
529     if (GraphicsLayer* scrollLayer = scrollLayerForFrameView(*frameView))
530         scrollLayer->setPosition(-frameView->scrollPosition());
531 }
532
533 bool AsyncScrollingCoordinator::isRubberBandInProgress() const
534 {
535     return scrollingTree()->isRubberBandInProgress();
536 }
537
538 void AsyncScrollingCoordinator::setScrollPinningBehavior(ScrollPinningBehavior pinning)
539 {
540     scrollingTree()->setScrollPinningBehavior(pinning);
541 }
542
543 String AsyncScrollingCoordinator::scrollingStateTreeAsText() const
544 {
545     if (m_scrollingStateTree->rootStateNode()) {
546         if (m_nonFastScrollableRegionDirty)
547             m_scrollingStateTree->rootStateNode()->setNonFastScrollableRegion(computeNonFastScrollableRegion(m_page->mainFrame(), IntPoint()));
548         return m_scrollingStateTree->rootStateNode()->scrollingStateTreeAsText();
549     }
550
551     return String();
552 }
553
554 } // namespace WebCore
555
556 #endif // ENABLE(ASYNC_SCROLLING)