Allow UI clients to handle vertical wheel events.
[WebKit-https.git] / Source / WebCore / page / scrolling / ScrollingTree.cpp
1 /*
2  * Copyright (C) 2012, 2013 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 #include "ScrollingTree.h"
28
29 #if ENABLE(THREADED_SCROLLING)
30
31 #include "PlatformWheelEvent.h"
32 #include "ScrollingCoordinator.h"
33 #include "ScrollingStateTree.h"
34 #include "ScrollingThread.h"
35 #include "ScrollingTreeFixedNode.h"
36 #include "ScrollingTreeNode.h"
37 #include "ScrollingTreeScrollingNode.h"
38 #include "ScrollingTreeStickyNode.h"
39 #include <wtf/MainThread.h>
40 #include <wtf/TemporaryChange.h>
41
42 namespace WebCore {
43
44 PassRefPtr<ScrollingTree> ScrollingTree::create(ScrollingCoordinator* scrollingCoordinator)
45 {
46     return adoptRef(new ScrollingTree(scrollingCoordinator));
47 }
48
49 ScrollingTree::ScrollingTree(ScrollingCoordinator* scrollingCoordinator)
50     : m_scrollingCoordinator(scrollingCoordinator)
51     , m_hasWheelEventHandlers(false)
52     , m_canGoBack(false)
53     , m_canGoForward(false)
54     , m_mainFramePinnedToTheLeft(false)
55     , m_mainFramePinnedToTheRight(false)
56     , m_rubberBandsAtBottom(true)
57     , m_rubberBandsAtTop(true)
58     , m_mainFramePinnedToTheTop(false)
59     , m_mainFramePinnedToTheBottom(false)
60     , m_mainFrameIsRubberBanding(false)
61     , m_scrollingPerformanceLoggingEnabled(false)
62     , m_isHandlingProgrammaticScroll(false)
63 {
64 }
65
66 ScrollingTree::~ScrollingTree()
67 {
68     ASSERT(!m_scrollingCoordinator);
69 }
70
71 ScrollingTree::EventResult ScrollingTree::tryToHandleWheelEvent(const PlatformWheelEvent& wheelEvent)
72 {
73     {
74         MutexLocker lock(m_mutex);
75
76         if (m_hasWheelEventHandlers)
77             return SendToMainThread;
78
79         if (!m_nonFastScrollableRegion.isEmpty()) {
80             // FIXME: This is not correct for non-default scroll origins.
81             IntPoint position = wheelEvent.position();
82             position.moveBy(m_mainFrameScrollPosition);
83             if (m_nonFastScrollableRegion.contains(position))
84                 return SendToMainThread;
85         }
86     }
87
88     if (willWheelEventStartSwipeGesture(wheelEvent))
89         return DidNotHandleEvent;
90
91     ScrollingThread::dispatch(bind(&ScrollingTree::handleWheelEvent, this, wheelEvent));
92     return DidHandleEvent;
93 }
94
95 void ScrollingTree::updateBackForwardState(bool canGoBack, bool canGoForward)
96 {
97     MutexLocker locker(m_swipeStateMutex);
98
99     m_canGoBack = canGoBack;
100     m_canGoForward = canGoForward;
101 }
102
103 void ScrollingTree::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
104 {
105     ASSERT(ScrollingThread::isCurrentThread());
106     
107     if (m_rootNode)
108         m_rootNode->handleWheelEvent(wheelEvent);
109 }
110
111 static void derefScrollingCoordinator(ScrollingCoordinator* scrollingCoordinator)
112 {
113     ASSERT(isMainThread());
114
115     scrollingCoordinator->deref();
116 }
117
118 void ScrollingTree::invalidate()
119 {
120     // Invalidate is dispatched by the ScrollingCoordinator class on the ScrollingThread
121     // to break the reference cycle between ScrollingTree and ScrollingCoordinator when the
122     // ScrollingCoordinator's page is destroyed.
123     ASSERT(ScrollingThread::isCurrentThread());
124
125     // Since this can potentially be the last reference to the scrolling coordinator,
126     // we need to release it on the main thread since it has member variables (such as timers)
127     // that expect to be destroyed from the main thread.
128     callOnMainThread(bind(derefScrollingCoordinator, m_scrollingCoordinator.release().leakRef()));
129 }
130
131 void ScrollingTree::commitNewTreeState(PassOwnPtr<ScrollingStateTree> scrollingStateTree)
132 {
133     ASSERT(ScrollingThread::isCurrentThread());
134
135     ScrollingStateScrollingNode* rootNode = scrollingStateTree->rootStateNode();
136     if (rootNode
137         && (rootNode->hasChangedProperty(ScrollingStateScrollingNode::WheelEventHandlerCount)
138             || rootNode->hasChangedProperty(ScrollingStateScrollingNode::NonFastScrollableRegion)
139             || rootNode->hasChangedProperty(ScrollingStateNode::ScrollLayer))) {
140         MutexLocker lock(m_mutex);
141
142         if (rootNode->hasChangedProperty(ScrollingStateNode::ScrollLayer))
143             m_mainFrameScrollPosition = IntPoint();
144         if (rootNode->hasChangedProperty(ScrollingStateScrollingNode::WheelEventHandlerCount))
145             m_hasWheelEventHandlers = scrollingStateTree->rootStateNode()->wheelEventHandlerCount();
146         if (rootNode->hasChangedProperty(ScrollingStateScrollingNode::NonFastScrollableRegion))
147             m_nonFastScrollableRegion = scrollingStateTree->rootStateNode()->nonFastScrollableRegion();
148     }
149     
150     bool scrollRequestIsProgammatic = rootNode ? rootNode->requestedScrollPositionRepresentsProgrammaticScroll() : false;
151     TemporaryChange<bool> changeHandlingProgrammaticScroll(m_isHandlingProgrammaticScroll, scrollRequestIsProgammatic);
152
153     removeDestroyedNodes(scrollingStateTree.get());
154     updateTreeFromStateNode(rootNode);
155 }
156
157 void ScrollingTree::updateTreeFromStateNode(ScrollingStateNode* stateNode)
158 {
159     if (!stateNode) {
160         m_nodeMap.clear();
161         m_rootNode = nullptr;
162         return;
163     }
164     
165     // This fuction recurses through the ScrollingStateTree and updates the corresponding ScrollingTreeNodes.
166     // Find the ScrollingTreeNode associated with the current stateNode using the shared ID and our HashMap.
167     ScrollingTreeNodeMap::const_iterator it = m_nodeMap.find(stateNode->scrollingNodeID());
168
169     ScrollingTreeNode* node;
170     if (it != m_nodeMap.end()) {
171         node = it->value;
172         node->updateBeforeChildren(stateNode);
173     } else {
174         // If the node isn't found, it's either new and needs to be added to the tree, or there is a new ID for our
175         // root node.
176         ScrollingNodeID nodeID = stateNode->scrollingNodeID();
177         if (!stateNode->parent()) {
178             // This is the root node. Nuke the node map.
179             m_nodeMap.clear();
180
181             m_rootNode = ScrollingTreeScrollingNode::create(this, nodeID);
182             m_nodeMap.set(nodeID, m_rootNode.get());
183             m_rootNode->updateBeforeChildren(stateNode);
184             node = m_rootNode.get();
185         } else {
186             OwnPtr<ScrollingTreeNode> newNode;
187             if (stateNode->isScrollingNode())
188                 newNode = ScrollingTreeScrollingNode::create(this, nodeID);
189             else if (stateNode->isFixedNode())
190                 newNode = ScrollingTreeFixedNode::create(this, nodeID);
191             else if (stateNode->isStickyNode())
192                 newNode = ScrollingTreeStickyNode::create(this, nodeID);
193             else
194                 ASSERT_NOT_REACHED();
195
196             node = newNode.get();
197             m_nodeMap.set(nodeID, node);
198             ScrollingTreeNodeMap::const_iterator it = m_nodeMap.find(stateNode->parent()->scrollingNodeID());
199             ASSERT(it != m_nodeMap.end());
200             if (it != m_nodeMap.end()) {
201                 ScrollingTreeNode* parent = it->value;
202                 newNode->setParent(parent);
203                 parent->appendChild(newNode.release());
204             }
205             node->updateBeforeChildren(stateNode);
206         }
207     }
208
209     // Now update the children if we have any.
210     Vector<OwnPtr<ScrollingStateNode> >* stateNodeChildren = stateNode->children();
211     if (stateNodeChildren) {
212         size_t size = stateNodeChildren->size();
213         for (size_t i = 0; i < size; ++i)
214             updateTreeFromStateNode(stateNodeChildren->at(i).get());
215     }
216     node->updateAfterChildren(stateNode);
217 }
218
219 void ScrollingTree::removeDestroyedNodes(ScrollingStateTree* stateTree)
220 {
221     const Vector<ScrollingNodeID>& removedNodes = stateTree->removedNodes();
222     size_t size = removedNodes.size();
223     for (size_t i = 0; i < size; ++i) {
224         ScrollingTreeNode* node = m_nodeMap.take(removedNodes[i]);
225         // Never destroy the root node. There will be a new root node in the state tree, and we will
226         // associate it with our existing root node in updateTreeFromStateNode().
227         if (node && node->parent())
228             m_rootNode->removeChild(node);
229     }
230 }
231
232 void ScrollingTree::setMainFramePinState(bool pinnedToTheLeft, bool pinnedToTheRight, bool pinnedToTheTop, bool pinnedToTheBottom)
233 {
234     MutexLocker locker(m_swipeStateMutex);
235
236     m_mainFramePinnedToTheLeft = pinnedToTheLeft;
237     m_mainFramePinnedToTheRight = pinnedToTheRight;
238     m_mainFramePinnedToTheTop = pinnedToTheTop;
239     m_mainFramePinnedToTheBottom = pinnedToTheBottom;
240 }
241
242 void ScrollingTree::updateMainFrameScrollPosition(const IntPoint& scrollPosition, SetOrSyncScrollingLayerPosition scrollingLayerPositionAction)
243 {
244     if (!m_scrollingCoordinator)
245         return;
246
247     {
248         MutexLocker lock(m_mutex);
249         m_mainFrameScrollPosition = scrollPosition;
250     }
251
252     callOnMainThread(bind(&ScrollingCoordinator::scheduleUpdateMainFrameScrollPosition, m_scrollingCoordinator.get(), scrollPosition, m_isHandlingProgrammaticScroll, scrollingLayerPositionAction));
253 }
254
255 IntPoint ScrollingTree::mainFrameScrollPosition()
256 {
257     MutexLocker lock(m_mutex);
258     return m_mainFrameScrollPosition;
259 }
260
261 #if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN))
262 void ScrollingTree::handleWheelEventPhase(PlatformWheelEventPhase phase)
263 {
264     if (!m_scrollingCoordinator)
265         return;
266
267     callOnMainThread(bind(&ScrollingCoordinator::handleWheelEventPhase, m_scrollingCoordinator.get(), phase));
268 }
269 #endif
270
271 bool ScrollingTree::canGoBack()
272 {
273     MutexLocker lock(m_swipeStateMutex);
274
275     return m_canGoBack;
276 }
277
278 bool ScrollingTree::canGoForward()
279 {
280     MutexLocker lock(m_swipeStateMutex);
281
282     return m_canGoForward;
283 }
284
285 bool ScrollingTree::isRubberBandInProgress()
286 {
287     MutexLocker lock(m_mutex);    
288
289     return m_mainFrameIsRubberBanding;
290 }
291
292 void ScrollingTree::setMainFrameIsRubberBanding(bool isRubberBanding)
293 {
294     MutexLocker locker(m_mutex);
295
296     m_mainFrameIsRubberBanding = isRubberBanding;
297 }
298
299 bool ScrollingTree::rubberBandsAtBottom()
300 {
301     MutexLocker lock(m_swipeStateMutex);
302
303     return m_rubberBandsAtBottom;
304 }
305
306 void ScrollingTree::setRubberBandsAtBottom(bool rubberBandsAtBottom)
307 {
308     MutexLocker locker(m_swipeStateMutex);
309
310     m_rubberBandsAtBottom = rubberBandsAtBottom;
311 }
312
313 bool ScrollingTree::rubberBandsAtTop()
314 {
315     MutexLocker lock(m_swipeStateMutex);
316
317     return m_rubberBandsAtTop;
318 }
319
320 void ScrollingTree::setRubberBandsAtTop(bool rubberBandsAtTop)
321 {
322     MutexLocker locker(m_swipeStateMutex);
323
324     m_rubberBandsAtTop = rubberBandsAtTop;
325 }
326
327 bool ScrollingTree::willWheelEventStartSwipeGesture(const PlatformWheelEvent& wheelEvent)
328 {
329     if (wheelEvent.phase() != PlatformWheelEventPhaseBegan)
330         return false;
331
332     MutexLocker lock(m_swipeStateMutex);
333
334     if (wheelEvent.deltaX() > 0 && m_mainFramePinnedToTheLeft && m_canGoBack)
335         return true;
336     if (wheelEvent.deltaX() < 0 && m_mainFramePinnedToTheRight && m_canGoForward)
337         return true;
338     if (wheelEvent.deltaY() > 0 && m_mainFramePinnedToTheTop && !m_rubberBandsAtTop)
339         return true;
340     if (wheelEvent.deltaY() < 0 && m_mainFramePinnedToTheBottom && !m_rubberBandsAtBottom)
341         return true;
342
343     return false;
344 }
345
346 void ScrollingTree::setScrollingPerformanceLoggingEnabled(bool flag)
347 {
348     m_scrollingPerformanceLoggingEnabled = flag;
349 }
350
351 bool ScrollingTree::scrollingPerformanceLoggingEnabled()
352 {
353     return m_scrollingPerformanceLoggingEnabled;
354 }
355
356 } // namespace WebCore
357
358 #endif // ENABLE(THREADED_SCROLLING)