blob: 9e7a9c9223885153b701a79af80108e31c92dac3 [file] [log] [blame]
simon.fraser@apple.com6576a4b2014-01-02 19:41:45 +00001/*
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
42namespace WebCore {
43
44AsyncScrollingCoordinator::AsyncScrollingCoordinator(Page* page)
45 : ScrollingCoordinator(page)
simon.fraser@apple.com7ad60ba2014-01-25 00:04:18 +000046 , m_updateNodeScrollPositionTimer(this, &AsyncScrollingCoordinator::updateScrollPositionAfterAsyncScrollTimerFired)
simon.fraser@apple.come5d6ecb2014-01-04 03:55:23 +000047 , m_scrollingStateTree(ScrollingStateTree::create(this))
simon.fraser@apple.com6576a4b2014-01-02 19:41:45 +000048{
49}
50
51AsyncScrollingCoordinator::~AsyncScrollingCoordinator()
52{
53}
54
simon.fraser@apple.come5d6ecb2014-01-04 03:55:23 +000055void AsyncScrollingCoordinator::scrollingStateTreePropertiesChanged()
56{
57 scheduleTreeStateCommit();
58}
59
simon.fraser@apple.com6576a4b2014-01-02 19:41:45 +000060void 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.
simon.fraser@apple.com6576a4b2014-01-02 19:41:45 +000072 // In the future, we may want to have the ability to set non-fast scrolling regions for more than
73 // just the root node. But right now, this concept only applies to the root.
simon.fraser@apple.combb2165c2014-02-14 19:13:54 +000074 m_scrollingStateTree->rootStateNode()->setNonFastScrollableRegion(computeNonFastScrollableRegion(&m_page->mainFrame(), IntPoint()));
simon.fraser@apple.com6576a4b2014-01-02 19:41:45 +000075
76 if (!coordinatesScrollingForFrameView(frameView))
77 return;
78
79 ScrollingStateScrollingNode* node = toScrollingStateScrollingNode(m_scrollingStateTree->stateNodeForID(frameView->scrollLayerID()));
80 if (!node)
81 return;
82
83 Scrollbar* verticalScrollbar = frameView->verticalScrollbar();
84 Scrollbar* horizontalScrollbar = frameView->horizontalScrollbar();
85 setScrollbarPaintersFromScrollbarsForNode(verticalScrollbar, horizontalScrollbar, node);
86
87 node->setFrameScaleFactor(frameView->frame().frameScaleFactor());
88 node->setHeaderHeight(frameView->headerHeight());
89 node->setFooterHeight(frameView->footerHeight());
90
91 node->setScrollOrigin(frameView->scrollOrigin());
simon.fraser@apple.com056ad1c2014-03-12 18:55:53 +000092 node->setViewportSize(frameView->visibleContentRect().size());
simon.fraser@apple.com6576a4b2014-01-02 19:41:45 +000093 node->setTotalContentsSize(frameView->totalContentsSize());
94
95 ScrollableAreaParameters scrollParameters;
96 scrollParameters.horizontalScrollElasticity = frameView->horizontalScrollElasticity();
97 scrollParameters.verticalScrollElasticity = frameView->verticalScrollElasticity();
98 scrollParameters.hasEnabledHorizontalScrollbar = horizontalScrollbar && horizontalScrollbar->enabled();
99 scrollParameters.hasEnabledVerticalScrollbar = verticalScrollbar && verticalScrollbar->enabled();
100 scrollParameters.horizontalScrollbarMode = frameView->horizontalScrollbarMode();
101 scrollParameters.verticalScrollbarMode = frameView->verticalScrollbarMode();
102
103 node->setScrollableAreaParameters(scrollParameters);
simon.fraser@apple.com6576a4b2014-01-02 19:41:45 +0000104}
105
simon.fraser@apple.com734cb1b2014-02-06 06:55:20 +0000106void AsyncScrollingCoordinator::frameViewNonFastScrollableRegionChanged(FrameView*)
107{
108 if (!m_scrollingStateTree->rootStateNode())
109 return;
110
simon.fraser@apple.combb2165c2014-02-14 19:13:54 +0000111 m_scrollingStateTree->rootStateNode()->setNonFastScrollableRegion(computeNonFastScrollableRegion(&m_page->mainFrame(), IntPoint()));
simon.fraser@apple.com734cb1b2014-02-06 06:55:20 +0000112}
113
simon.fraser@apple.com6576a4b2014-01-02 19:41:45 +0000114void AsyncScrollingCoordinator::frameViewRootLayerDidChange(FrameView* frameView)
115{
116 ASSERT(isMainThread());
117 ASSERT(m_page);
118
119 if (!coordinatesScrollingForFrameView(frameView))
120 return;
121
122 // If the root layer does not have a ScrollingStateNode, then we should create one.
123 ensureRootStateNodeForFrameView(frameView);
124 ASSERT(m_scrollingStateTree->rootStateNode());
125
126 ScrollingCoordinator::frameViewRootLayerDidChange(frameView);
127
128 ScrollingStateScrollingNode* node = toScrollingStateScrollingNode(m_scrollingStateTree->stateNodeForID(frameView->scrollLayerID()));
simon.fraser@apple.combb2165c2014-02-14 19:13:54 +0000129 node->setLayer(scrollLayerForFrameView(frameView));
130 node->setCounterScrollingLayer(counterScrollingLayerForFrameView(frameView));
131 node->setHeaderLayer(headerLayerForFrameView(frameView));
132 node->setFooterLayer(footerLayerForFrameView(frameView));
133 node->setScrollBehaviorForFixedElements(frameView->scrollBehaviorForFixedElements());
simon.fraser@apple.com6576a4b2014-01-02 19:41:45 +0000134}
135
136bool AsyncScrollingCoordinator::requestScrollPositionUpdate(FrameView* frameView, const IntPoint& scrollPosition)
137{
138 ASSERT(isMainThread());
139 ASSERT(m_page);
140
141 if (!coordinatesScrollingForFrameView(frameView))
142 return false;
143
144 if (frameView->inProgrammaticScroll() || frameView->frame().document()->inPageCache())
simon.fraser@apple.com7ad60ba2014-01-25 00:04:18 +0000145 updateScrollPositionAfterAsyncScroll(frameView->scrollLayerID(), scrollPosition, frameView->inProgrammaticScroll(), SetScrollingLayerPosition);
simon.fraser@apple.com6576a4b2014-01-02 19:41:45 +0000146
147 // If this frame view's document is being put into the page cache, we don't want to update our
148 // main frame scroll position. Just let the FrameView think that we did.
149 if (frameView->frame().document()->inPageCache())
150 return true;
151
152 ScrollingStateScrollingNode* stateNode = toScrollingStateScrollingNode(m_scrollingStateTree->stateNodeForID(frameView->scrollLayerID()));
153 if (!stateNode)
154 return false;
155
156 stateNode->setRequestedScrollPosition(scrollPosition, frameView->inProgrammaticScroll());
simon.fraser@apple.com6576a4b2014-01-02 19:41:45 +0000157 return true;
158}
159
simon.fraser@apple.com9f794722014-02-01 03:57:48 +0000160void AsyncScrollingCoordinator::scheduleUpdateScrollPositionAfterAsyncScroll(ScrollingNodeID nodeID, const FloatPoint& scrollPosition, bool programmaticScroll, SetOrSyncScrollingLayerPosition scrollingLayerPositionAction)
simon.fraser@apple.com7ad60ba2014-01-25 00:04:18 +0000161{
162 ScheduledScrollUpdate scrollUpdate(nodeID, scrollPosition, programmaticScroll, scrollingLayerPositionAction);
163
164 if (m_updateNodeScrollPositionTimer.isActive()) {
165 if (m_scheduledScrollUpdate.matchesUpdateType(scrollUpdate)) {
166 m_scheduledScrollUpdate.scrollPosition = scrollPosition;
167 return;
168 }
169
170 // If the parameters don't match what was previosly scheduled, dispatch immediately.
171 m_updateNodeScrollPositionTimer.stop();
172 updateScrollPositionAfterAsyncScroll(m_scheduledScrollUpdate.nodeID, m_scheduledScrollUpdate.scrollPosition, m_scheduledScrollUpdate.isProgrammaticScroll, m_scheduledScrollUpdate.updateLayerPositionAction);
173 updateScrollPositionAfterAsyncScroll(nodeID, scrollPosition, programmaticScroll, scrollingLayerPositionAction);
174 return;
175 }
176
177 m_scheduledScrollUpdate = scrollUpdate;
178 m_updateNodeScrollPositionTimer.startOneShot(0);
179}
180
181void AsyncScrollingCoordinator::updateScrollPositionAfterAsyncScrollTimerFired(Timer<AsyncScrollingCoordinator>*)
182{
183 updateScrollPositionAfterAsyncScroll(m_scheduledScrollUpdate.nodeID, m_scheduledScrollUpdate.scrollPosition, m_scheduledScrollUpdate.isProgrammaticScroll, m_scheduledScrollUpdate.updateLayerPositionAction);
184}
185
simon.fraser@apple.com9f794722014-02-01 03:57:48 +0000186void AsyncScrollingCoordinator::updateScrollPositionAfterAsyncScroll(ScrollingNodeID scrollingNodeID, const FloatPoint& scrollPosition, bool programmaticScroll, SetOrSyncScrollingLayerPosition scrollingLayerPositionAction)
simon.fraser@apple.com7ad60ba2014-01-25 00:04:18 +0000187{
188 ASSERT(isMainThread());
189
190 if (!m_page)
191 return;
192
193 FrameView* frameView = m_page->mainFrame().view();
194 if (!frameView)
195 return;
196
simon.fraser@apple.com543255a2014-03-31 21:23:27 +0000197 // Main frame.
simon.fraser@apple.com7ad60ba2014-01-25 00:04:18 +0000198 if (scrollingNodeID == frameView->scrollLayerID()) {
simon.fraser@apple.com543255a2014-03-31 21:23:27 +0000199 bool oldProgrammaticScroll = frameView->inProgrammaticScroll();
200 frameView->setInProgrammaticScroll(programmaticScroll);
201
202 frameView->setConstrainsScrollingToContentEdge(false);
203 frameView->notifyScrollPositionChanged(roundedIntPoint(scrollPosition));
204 frameView->setConstrainsScrollingToContentEdge(true);
205
206 frameView->setInProgrammaticScroll(oldProgrammaticScroll);
207
simon.fraser@apple.com7ad60ba2014-01-25 00:04:18 +0000208 if (GraphicsLayer* scrollLayer = scrollLayerForFrameView(frameView)) {
209 GraphicsLayer* counterScrollingLayer = counterScrollingLayerForFrameView(frameView);
210 GraphicsLayer* headerLayer = headerLayerForFrameView(frameView);
211 GraphicsLayer* footerLayer = footerLayerForFrameView(frameView);
simon.fraser@apple.comeb58fa32014-03-12 18:15:06 +0000212 LayoutSize scrollOffsetForFixed = frameView->scrollOffsetForFixedPosition();
simon.fraser@apple.com7ad60ba2014-01-25 00:04:18 +0000213
214 if (programmaticScroll || scrollingLayerPositionAction == SetScrollingLayerPosition) {
215 scrollLayer->setPosition(-frameView->scrollPosition());
216 if (counterScrollingLayer)
simon.fraser@apple.comeb58fa32014-03-12 18:15:06 +0000217 counterScrollingLayer->setPosition(toLayoutPoint(scrollOffsetForFixed));
simon.fraser@apple.com7ad60ba2014-01-25 00:04:18 +0000218 if (headerLayer)
219 headerLayer->setPosition(FloatPoint(scrollOffsetForFixed.width(), 0));
220 if (footerLayer)
221 footerLayer->setPosition(FloatPoint(scrollOffsetForFixed.width(), frameView->totalContentsSize().height() - frameView->footerHeight()));
222 } else {
223 scrollLayer->syncPosition(-frameView->scrollPosition());
224 if (counterScrollingLayer)
simon.fraser@apple.comeb58fa32014-03-12 18:15:06 +0000225 counterScrollingLayer->syncPosition(toLayoutPoint(scrollOffsetForFixed));
simon.fraser@apple.com7ad60ba2014-01-25 00:04:18 +0000226 if (headerLayer)
227 headerLayer->syncPosition(FloatPoint(scrollOffsetForFixed.width(), 0));
228 if (footerLayer)
229 footerLayer->syncPosition(FloatPoint(scrollOffsetForFixed.width(), frameView->totalContentsSize().height() - frameView->footerHeight()));
230
231 LayoutRect viewportRect = frameView->viewportConstrainedVisibleContentRect();
232 syncChildPositions(viewportRect);
233 }
234 }
simon.fraser@apple.com543255a2014-03-31 21:23:27 +0000235
236 return;
simon.fraser@apple.com7ad60ba2014-01-25 00:04:18 +0000237 }
simon.fraser@apple.com543255a2014-03-31 21:23:27 +0000238
239 // Overflow-scroll area.
240 if (ScrollableArea* scrollableArea = frameView->scrollableAreaForScrollLayerID(scrollingNodeID))
241 scrollableArea->scrollToOffsetWithoutAnimation(scrollPosition);
simon.fraser@apple.com7ad60ba2014-01-25 00:04:18 +0000242}
243
simon.fraser@apple.com6576a4b2014-01-02 19:41:45 +0000244void AsyncScrollingCoordinator::scrollableAreaScrollbarLayerDidChange(ScrollableArea* scrollableArea, ScrollbarOrientation orientation)
245{
246 ASSERT(isMainThread());
247 ASSERT(m_page);
248
249 if (scrollableArea != static_cast<ScrollableArea*>(m_page->mainFrame().view()))
250 return;
251
252 if (orientation == VerticalScrollbar)
253 scrollableArea->verticalScrollbarLayerDidChange();
254 else
255 scrollableArea->horizontalScrollbarLayerDidChange();
256}
257
258ScrollingNodeID AsyncScrollingCoordinator::attachToStateTree(ScrollingNodeType nodeType, ScrollingNodeID newNodeID, ScrollingNodeID parentID)
259{
260 return m_scrollingStateTree->attachNode(nodeType, newNodeID, parentID);
261}
262
263void AsyncScrollingCoordinator::detachFromStateTree(ScrollingNodeID nodeID)
264{
265 m_scrollingStateTree->detachNode(nodeID);
266}
267
268void AsyncScrollingCoordinator::clearStateTree()
269{
270 m_scrollingStateTree->clear();
271}
272
273void AsyncScrollingCoordinator::syncChildPositions(const LayoutRect& viewportRect)
274{
275 if (!m_scrollingStateTree->rootStateNode())
276 return;
277
278 Vector<OwnPtr<ScrollingStateNode>>* children = m_scrollingStateTree->rootStateNode()->children();
279 if (!children)
280 return;
281
282 // FIXME: We'll have to traverse deeper into the tree at some point.
283 size_t size = children->size();
284 for (size_t i = 0; i < size; ++i) {
285 ScrollingStateNode* child = children->at(i).get();
286 child->syncLayerPositionForViewportRect(viewportRect);
287 }
288}
289
290void AsyncScrollingCoordinator::ensureRootStateNodeForFrameView(FrameView* frameView)
291{
292 ASSERT(frameView->scrollLayerID());
simon.fraser@apple.com87165782014-03-26 15:09:03 +0000293 attachToStateTree(FrameScrollingNode, frameView->scrollLayerID(), 0);
simon.fraser@apple.com6576a4b2014-01-02 19:41:45 +0000294}
295
simon.fraser@apple.com6cced982014-03-27 06:50:40 +0000296void AsyncScrollingCoordinator::updateScrollingNode(ScrollingNodeID nodeID, GraphicsLayer* layer, GraphicsLayer* scrolledContentsLayer, GraphicsLayer* counterScrollingLayer, const ScrollingGeometry* scrollingGeometry)
simon.fraser@apple.com6576a4b2014-01-02 19:41:45 +0000297{
298 ScrollingStateScrollingNode* node = toScrollingStateScrollingNode(m_scrollingStateTree->stateNodeForID(nodeID));
299 ASSERT(node);
300 if (!node)
301 return;
302
simon.fraser@apple.combb2165c2014-02-14 19:13:54 +0000303 node->setLayer(layer);
304 node->setScrolledContentsLayer(scrolledContentsLayer);
simon.fraser@apple.com6576a4b2014-01-02 19:41:45 +0000305 node->setCounterScrollingLayer(counterScrollingLayer);
simon.fraser@apple.come2fcc422014-03-27 02:15:48 +0000306
simon.fraser@apple.com6cced982014-03-27 06:50:40 +0000307 if (scrollingGeometry) {
308 node->setScrollOrigin(scrollingGeometry->scrollOrigin);
309 node->setScrollPosition(scrollingGeometry->scrollPosition);
310 node->setTotalContentsSize(scrollingGeometry->contentSize);
311 }
simon.fraser@apple.com6576a4b2014-01-02 19:41:45 +0000312}
313
314void AsyncScrollingCoordinator::updateViewportConstrainedNode(ScrollingNodeID nodeID, const ViewportConstraints& constraints, GraphicsLayer* graphicsLayer)
315{
316 ASSERT(supportsFixedPositionLayers());
317
318 ScrollingStateNode* node = m_scrollingStateTree->stateNodeForID(nodeID);
319 if (!node)
320 return;
321
322 switch (constraints.constraintType()) {
323 case ViewportConstraints::FixedPositionConstraint: {
324 ScrollingStateFixedNode* fixedNode = toScrollingStateFixedNode(node);
simon.fraser@apple.combb2165c2014-02-14 19:13:54 +0000325 fixedNode->setLayer(graphicsLayer);
simon.fraser@apple.com6576a4b2014-01-02 19:41:45 +0000326 fixedNode->updateConstraints((const FixedPositionViewportConstraints&)constraints);
327 break;
328 }
329 case ViewportConstraints::StickyPositionConstraint: {
330 ScrollingStateStickyNode* stickyNode = toScrollingStateStickyNode(node);
simon.fraser@apple.combb2165c2014-02-14 19:13:54 +0000331 stickyNode->setLayer(graphicsLayer);
simon.fraser@apple.com6576a4b2014-01-02 19:41:45 +0000332 stickyNode->updateConstraints((const StickyPositionViewportConstraints&)constraints);
333 break;
334 }
335 }
simon.fraser@apple.com6576a4b2014-01-02 19:41:45 +0000336}
337
simon.fraser@apple.com6576a4b2014-01-02 19:41:45 +0000338// FIXME: not sure if this belongs here.
339void AsyncScrollingCoordinator::setScrollbarPaintersFromScrollbarsForNode(Scrollbar* verticalScrollbar, Scrollbar* horizontalScrollbar, ScrollingStateScrollingNode* node)
340{
341 node->setScrollbarPaintersFromScrollbars(verticalScrollbar, horizontalScrollbar);
simon.fraser@apple.com6576a4b2014-01-02 19:41:45 +0000342}
343
344void AsyncScrollingCoordinator::setSynchronousScrollingReasons(SynchronousScrollingReasons reasons)
345{
346 if (!m_scrollingStateTree->rootStateNode())
347 return;
348
349 // The FrameView's GraphicsLayer is likely to be out-of-synch with the PlatformLayer
350 // at this point. So we'll update it before we switch back to main thread scrolling
351 // in order to avoid layer positioning bugs.
352 if (reasons)
353 updateMainFrameScrollLayerPosition();
354 m_scrollingStateTree->rootStateNode()->setSynchronousScrollingReasons(reasons);
simon.fraser@apple.com6576a4b2014-01-02 19:41:45 +0000355}
356
357void AsyncScrollingCoordinator::updateMainFrameScrollLayerPosition()
358{
359 ASSERT(isMainThread());
360
361 if (!m_page)
362 return;
363
364 FrameView* frameView = m_page->mainFrame().view();
365 if (!frameView)
366 return;
367
368 if (GraphicsLayer* scrollLayer = scrollLayerForFrameView(frameView))
369 scrollLayer->setPosition(-frameView->scrollPosition());
370}
371
372void AsyncScrollingCoordinator::recomputeWheelEventHandlerCountForFrameView(FrameView* frameView)
373{
374 ScrollingStateScrollingNode* node = toScrollingStateScrollingNode(m_scrollingStateTree->stateNodeForID(frameView->scrollLayerID()));
375 if (!node)
376 return;
simon.fraser@apple.combb2165c2014-02-14 19:13:54 +0000377 node->setWheelEventHandlerCount(computeCurrentWheelEventHandlerCount());
simon.fraser@apple.com6576a4b2014-01-02 19:41:45 +0000378}
379
380bool AsyncScrollingCoordinator::isRubberBandInProgress() const
381{
382 return scrollingTree()->isRubberBandInProgress();
383}
384
385void AsyncScrollingCoordinator::setScrollPinningBehavior(ScrollPinningBehavior pinning)
386{
387 scrollingTree()->setScrollPinningBehavior(pinning);
388}
389
390String AsyncScrollingCoordinator::scrollingStateTreeAsText() const
391{
392 if (m_scrollingStateTree->rootStateNode())
393 return m_scrollingStateTree->rootStateNode()->scrollingStateTreeAsText();
394
395 return String();
396}
397
398} // namespace WebCore
399
400#endif // ENABLE(ASYNC_SCROLLING)