Allow UI clients to handle vertical wheel events.
[WebKit-https.git] / Source / WebCore / page / scrolling / mac / ScrollingCoordinatorMac.mm
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 #if ENABLE(THREADED_SCROLLING)
29
30 #import "ScrollingCoordinatorMac.h"
31
32 #include "GraphicsLayer.h"
33 #include "Frame.h"
34 #include "FrameView.h"
35 #include "IntRect.h"
36 #include "Page.h"
37 #include "PlatformWheelEvent.h"
38 #include "PluginViewBase.h"
39 #include "Region.h"
40 #include "RenderLayerCompositor.h"
41 #include "RenderView.h"
42 #include "ScrollAnimator.h"
43 #include "ScrollingConstraints.h"
44 #include "ScrollingStateFixedNode.h"
45 #include "ScrollingStateScrollingNode.h"
46 #include "ScrollingStateStickyNode.h"
47 #include "ScrollingStateTree.h"
48 #include "ScrollingThread.h"
49 #include "ScrollingTree.h"
50 #include "TiledBacking.h"
51
52 #include <wtf/Functional.h>
53 #include <wtf/MainThread.h>
54 #include <wtf/PassRefPtr.h>
55
56
57 namespace WebCore {
58
59 class ScrollingCoordinatorPrivate {
60 };
61
62 ScrollingCoordinatorMac::ScrollingCoordinatorMac(Page* page)
63     : ScrollingCoordinator(page)
64     , m_scrollingStateTree(ScrollingStateTree::create())
65     , m_scrollingTree(ScrollingTree::create(this))
66     , m_scrollingStateTreeCommitterTimer(this, &ScrollingCoordinatorMac::scrollingStateTreeCommitterTimerFired)
67 {
68 }
69
70 ScrollingCoordinatorMac::~ScrollingCoordinatorMac()
71 {
72     ASSERT(!m_scrollingTree);
73 }
74
75 void ScrollingCoordinatorMac::pageDestroyed()
76 {
77     ScrollingCoordinator::pageDestroyed();
78
79     m_scrollingStateTreeCommitterTimer.stop();
80
81     // Invalidating the scrolling tree will break the reference cycle between the ScrollingCoordinator and ScrollingTree objects.
82     ScrollingThread::dispatch(bind(&ScrollingTree::invalidate, m_scrollingTree.release()));
83 }
84
85 ScrollingTree* ScrollingCoordinatorMac::scrollingTree() const
86 {
87     ASSERT(m_scrollingTree);
88     return m_scrollingTree.get();
89 }
90
91 bool ScrollingCoordinatorMac::isRubberBandInProgress() const
92 {
93     return scrollingTree()->isRubberBandInProgress();
94 }
95
96 bool ScrollingCoordinatorMac::rubberBandsAtBottom() const
97 {
98     return scrollingTree()->rubberBandsAtBottom();
99 }
100
101 void ScrollingCoordinatorMac::setRubberBandsAtBottom(bool rubberBandsAtBottom)
102 {
103     scrollingTree()->setRubberBandsAtBottom(rubberBandsAtBottom);
104 }
105
106 bool ScrollingCoordinatorMac::rubberBandsAtTop() const
107 {
108     return scrollingTree()->rubberBandsAtTop();
109 }
110
111 void ScrollingCoordinatorMac::setRubberBandsAtTop(bool rubberBandsAtTop)
112 {
113     scrollingTree()->setRubberBandsAtTop(rubberBandsAtTop);
114 }
115
116 void ScrollingCoordinatorMac::commitTreeStateIfNeeded()
117 {
118     if (!m_scrollingStateTree->hasChangedProperties())
119         return;
120
121     commitTreeState();
122     m_scrollingStateTreeCommitterTimer.stop();
123 }
124
125 void ScrollingCoordinatorMac::frameViewLayoutUpdated(FrameView* frameView)
126 {
127     ASSERT(isMainThread());
128     ASSERT(m_page);
129
130     // If there isn't a root node yet, don't do anything. We'll be called again after creating one.
131     if (!m_scrollingStateTree->rootStateNode())
132         return;
133
134     // Compute the region of the page that we can't do fast scrolling for. This currently includes
135     // all scrollable areas, such as subframes, overflow divs and list boxes. We need to do this even if the
136     // frame view whose layout was updated is not the main frame.
137     Region nonFastScrollableRegion = computeNonFastScrollableRegion(m_page->mainFrame(), IntPoint());
138
139     // In the future, we may want to have the ability to set non-fast scrolling regions for more than
140     // just the root node. But right now, this concept only applies to the root.
141     setNonFastScrollableRegionForNode(nonFastScrollableRegion, m_scrollingStateTree->rootStateNode());
142
143     if (!coordinatesScrollingForFrameView(frameView))
144         return;
145
146     ScrollingStateScrollingNode* node = toScrollingStateScrollingNode(m_scrollingStateTree->stateNodeForID(frameView->scrollLayerID()));
147     if (!node)
148         return;
149
150     ScrollParameters scrollParameters;
151     scrollParameters.horizontalScrollElasticity = frameView->horizontalScrollElasticity();
152     scrollParameters.verticalScrollElasticity = frameView->verticalScrollElasticity();
153     scrollParameters.hasEnabledHorizontalScrollbar = frameView->horizontalScrollbar() && frameView->horizontalScrollbar()->enabled();
154     scrollParameters.hasEnabledVerticalScrollbar = frameView->verticalScrollbar() && frameView->verticalScrollbar()->enabled();
155     scrollParameters.horizontalScrollbarMode = frameView->horizontalScrollbarMode();
156     scrollParameters.verticalScrollbarMode = frameView->verticalScrollbarMode();
157
158     scrollParameters.scrollOrigin = frameView->scrollOrigin();
159     scrollParameters.viewportRect = IntRect(IntPoint(), frameView->visibleContentRect().size());
160     scrollParameters.contentsSize = frameView->contentsSize();
161     scrollParameters.frameScaleFactor = frameView->frame()->frameScaleFactor();
162
163     setScrollParametersForNode(scrollParameters, node);
164 }
165
166 void ScrollingCoordinatorMac::recomputeWheelEventHandlerCountForFrameView(FrameView* frameView)
167 {
168     ScrollingStateScrollingNode* node = toScrollingStateScrollingNode(m_scrollingStateTree->stateNodeForID(frameView->scrollLayerID()));
169     if (!node)
170         return;
171     setWheelEventHandlerCountForNode(computeCurrentWheelEventHandlerCount(), node);
172 }
173
174 void ScrollingCoordinatorMac::frameViewRootLayerDidChange(FrameView* frameView)
175 {
176     ASSERT(isMainThread());
177     ASSERT(m_page);
178
179     if (!coordinatesScrollingForFrameView(frameView))
180         return;
181
182     // If the root layer does not have a ScrollingStateNode, then we should create one.
183     ensureRootStateNodeForFrameView(frameView);
184     ASSERT(m_scrollingStateTree->rootStateNode());
185
186     ScrollingCoordinator::frameViewRootLayerDidChange(frameView);
187
188     ScrollingStateScrollingNode* node = toScrollingStateScrollingNode(m_scrollingStateTree->stateNodeForID(frameView->scrollLayerID()));
189     setScrollLayerForNode(scrollLayerForFrameView(frameView), node);
190     setCounterScrollingLayerForNode(counterScrollingLayerForFrameView(frameView), node);
191 }
192
193 void ScrollingCoordinatorMac::frameViewHorizontalScrollbarLayerDidChange(FrameView* frameView, GraphicsLayer*)
194 {
195     ASSERT(isMainThread());
196     ASSERT(m_page);
197
198     if (frameView->frame() != m_page->mainFrame())
199         return;
200
201     // FIXME: Implement.
202 }
203
204 void ScrollingCoordinatorMac::frameViewVerticalScrollbarLayerDidChange(FrameView* frameView, GraphicsLayer*)
205 {
206     ASSERT(isMainThread());
207     ASSERT(m_page);
208     
209     if (frameView->frame() != m_page->mainFrame())
210         return;
211
212     // FIXME: Implement.
213 }
214
215 bool ScrollingCoordinatorMac::requestScrollPositionUpdate(FrameView* frameView, const IntPoint& scrollPosition)
216 {
217     ASSERT(isMainThread());
218     ASSERT(m_page);
219
220     if (!coordinatesScrollingForFrameView(frameView))
221         return false;
222
223     if (frameView->inProgrammaticScroll() || frameView->frame()->document()->inPageCache())
224         updateMainFrameScrollPosition(scrollPosition, frameView->inProgrammaticScroll(), SetScrollingLayerPosition);
225
226     // If this frame view's document is being put into the page cache, we don't want to update our
227     // main frame scroll position. Just let the FrameView think that we did.
228     if (frameView->frame()->document()->inPageCache())
229         return true;
230
231     ScrollingStateScrollingNode* stateNode = toScrollingStateScrollingNode(m_scrollingStateTree->stateNodeForID(frameView->scrollLayerID()));
232     if (!stateNode)
233         return false;
234
235     stateNode->setRequestedScrollPosition(scrollPosition, frameView->inProgrammaticScroll());
236     scheduleTreeStateCommit();
237     return true;
238 }
239
240 bool ScrollingCoordinatorMac::handleWheelEvent(FrameView*, const PlatformWheelEvent& wheelEvent)
241 {
242     ASSERT(isMainThread());
243     ASSERT(m_page);
244
245     if (m_scrollingTree->willWheelEventStartSwipeGesture(wheelEvent))
246         return false;
247
248     ScrollingThread::dispatch(bind(&ScrollingTree::handleWheelEvent, m_scrollingTree.get(), wheelEvent));
249
250     return true;
251 }
252
253 ScrollingNodeID ScrollingCoordinatorMac::attachToStateTree(ScrollingNodeType nodeType, ScrollingNodeID newNodeID, ScrollingNodeID parentID)
254 {
255     return m_scrollingStateTree->attachNode(nodeType, newNodeID, parentID);
256 }
257
258 void ScrollingCoordinatorMac::detachFromStateTree(ScrollingNodeID nodeID)
259 {
260     m_scrollingStateTree->detachNode(nodeID);
261 }
262
263 void ScrollingCoordinatorMac::clearStateTree()
264 {
265     m_scrollingStateTree->clear();
266 }
267
268 void ScrollingCoordinatorMac::ensureRootStateNodeForFrameView(FrameView* frameView)
269 {
270     ASSERT(frameView->scrollLayerID());
271     attachToStateTree(ScrollingNode, frameView->scrollLayerID(), 0);
272 }
273
274 void ScrollingCoordinatorMac::setScrollLayerForNode(GraphicsLayer* scrollLayer, ScrollingStateNode* node)
275 {
276     node->setScrollLayer(scrollLayer);
277     scheduleTreeStateCommit();
278 }
279
280 void ScrollingCoordinatorMac::setCounterScrollingLayerForNode(GraphicsLayer* layer, ScrollingStateScrollingNode* node)
281 {
282     node->setCounterScrollingLayer(layer);
283     scheduleTreeStateCommit();
284 }
285
286 void ScrollingCoordinatorMac::setNonFastScrollableRegionForNode(const Region& region, ScrollingStateScrollingNode* node)
287 {
288     node->setNonFastScrollableRegion(region);
289     scheduleTreeStateCommit();
290 }
291
292 void ScrollingCoordinatorMac::setScrollParametersForNode(const ScrollParameters& scrollParameters, ScrollingStateScrollingNode* node)
293 {
294     node->setHorizontalScrollElasticity(scrollParameters.horizontalScrollElasticity);
295     node->setVerticalScrollElasticity(scrollParameters.verticalScrollElasticity);
296     node->setHasEnabledHorizontalScrollbar(scrollParameters.hasEnabledHorizontalScrollbar);
297     node->setHasEnabledVerticalScrollbar(scrollParameters.hasEnabledVerticalScrollbar);
298     node->setHorizontalScrollbarMode(scrollParameters.horizontalScrollbarMode);
299     node->setVerticalScrollbarMode(scrollParameters.verticalScrollbarMode);
300
301     node->setScrollOrigin(scrollParameters.scrollOrigin);
302     node->setViewportRect(scrollParameters.viewportRect);
303     node->setContentsSize(scrollParameters.contentsSize);
304     node->setFrameScaleFactor(scrollParameters.frameScaleFactor);
305
306     scheduleTreeStateCommit();
307 }
308
309 void ScrollingCoordinatorMac::setWheelEventHandlerCountForNode(unsigned wheelEventHandlerCount, ScrollingStateScrollingNode* node)
310 {
311     node->setWheelEventHandlerCount(wheelEventHandlerCount);
312     scheduleTreeStateCommit();
313 }
314
315 void ScrollingCoordinatorMac::setShouldUpdateScrollLayerPositionOnMainThread(MainThreadScrollingReasons reasons)
316 {
317     // The FrameView's GraphicsLayer is likely to be out-of-synch with the PlatformLayer
318     // at this point. So we'll update it before we switch back to main thread scrolling
319     // in order to avoid layer positioning bugs.
320     if (reasons)
321         updateMainFrameScrollLayerPosition();
322     m_scrollingStateTree->rootStateNode()->setShouldUpdateScrollLayerPositionOnMainThread(reasons);
323     scheduleTreeStateCommit();
324 }
325
326 void ScrollingCoordinatorMac::updateMainFrameScrollLayerPosition()
327 {
328     ASSERT(isMainThread());
329
330     if (!m_page)
331         return;
332
333     FrameView* frameView = m_page->mainFrame()->view();
334     if (!frameView)
335         return;
336
337     if (GraphicsLayer* scrollLayer = scrollLayerForFrameView(frameView))
338         scrollLayer->setPosition(-frameView->scrollPosition());
339 }
340
341 void ScrollingCoordinatorMac::syncChildPositions(const LayoutRect& viewportRect)
342 {
343     Vector<OwnPtr<ScrollingStateNode> >* children = m_scrollingStateTree->rootStateNode()->children();
344     if (!children)
345         return;
346
347     // FIXME: We'll have to traverse deeper into the tree at some point.
348     size_t size = children->size();
349     for (size_t i = 0; i < size; ++i) {
350         ScrollingStateNode* child = children->at(i).get();
351         child->syncLayerPositionForViewportRect(viewportRect);
352     }
353 }
354
355 void ScrollingCoordinatorMac::updateScrollingNode(ScrollingNodeID nodeID, GraphicsLayer* scrollLayer, GraphicsLayer* counterScrollingLayer)
356 {
357     ScrollingStateScrollingNode* node = toScrollingStateScrollingNode(m_scrollingStateTree->stateNodeForID(nodeID));
358     ASSERT(node);
359     if (!node)
360         return;
361
362     node->setScrollLayer(scrollLayer);
363     node->setCounterScrollingLayer(counterScrollingLayer);
364     scheduleTreeStateCommit();
365 }
366
367 void ScrollingCoordinatorMac::updateViewportConstrainedNode(ScrollingNodeID nodeID, const ViewportConstraints& constraints, GraphicsLayer* graphicsLayer)
368 {
369     ASSERT(supportsFixedPositionLayers());
370
371     ScrollingStateNode* node = m_scrollingStateTree->stateNodeForID(nodeID);
372     if (!node)
373         return;
374
375     switch (constraints.constraintType()) {
376     case ViewportConstraints::FixedPositionConstaint: {
377         ScrollingStateFixedNode* fixedNode = toScrollingStateFixedNode(node);
378         setScrollLayerForNode(graphicsLayer, fixedNode);
379         fixedNode->updateConstraints((const FixedPositionViewportConstraints&)constraints);
380         break;
381     }
382     case ViewportConstraints::StickyPositionConstraint: {
383         ScrollingStateStickyNode* stickyNode = toScrollingStateStickyNode(node);
384         setScrollLayerForNode(graphicsLayer, stickyNode);
385         stickyNode->updateConstraints((const StickyPositionViewportConstraints&)constraints);
386         break;
387     }
388     }
389     scheduleTreeStateCommit();
390 }
391
392 void ScrollingCoordinatorMac::scheduleTreeStateCommit()
393 {
394     if (m_scrollingStateTreeCommitterTimer.isActive())
395         return;
396
397     if (!m_scrollingStateTree->hasChangedProperties())
398         return;
399
400     m_scrollingStateTreeCommitterTimer.startOneShot(0);
401 }
402
403 void ScrollingCoordinatorMac::scrollingStateTreeCommitterTimerFired(Timer<ScrollingCoordinatorMac>*)
404 {
405     commitTreeState();
406 }
407
408 void ScrollingCoordinatorMac::commitTreeState()
409 {
410     ASSERT(m_scrollingStateTree->hasChangedProperties());
411
412     OwnPtr<ScrollingStateTree> treeState = m_scrollingStateTree->commit();
413     ScrollingThread::dispatch(bind(&ScrollingTree::commitNewTreeState, m_scrollingTree.get(), treeState.release()));
414
415     FrameView* frameView = m_page->mainFrame()->view();
416     if (!frameView)
417         return;
418     
419     TiledBacking* tiledBacking = frameView->tiledBacking();
420     if (!tiledBacking)
421         return;
422
423     ScrollingModeIndication indicatorMode;
424     if (shouldUpdateScrollLayerPositionOnMainThread())
425         indicatorMode = MainThreadScrollingBecauseOfStyleIndictaion;
426     else if (scrollingTree() && scrollingTree()->hasWheelEventHandlers())
427         indicatorMode =  MainThreadScrollingBecauseOfEventHandlersIndication;
428     else
429         indicatorMode = ThreadedScrollingIndication;
430     
431     tiledBacking->setScrollingModeIndication(indicatorMode);
432 }
433
434 String ScrollingCoordinatorMac::scrollingStateTreeAsText() const
435 {
436     if (m_scrollingStateTree->rootStateNode())
437         return m_scrollingStateTree->rootStateNode()->scrollingStateTreeAsText();
438
439     return String();
440 }
441
442 } // namespace WebCore
443
444 #endif // ENABLE(THREADED_SCROLLING)