261d88486c31b42e06cdd0d8965e134aa9d1240d
[WebKit-https.git] / Source / WebCore / page / scrolling / ScrollingTree.cpp
1 /*
2  * Copyright (C) 2012 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_mainFrameIsRubberBanding(false)
57     , m_scrollingPerformanceLoggingEnabled(false)
58     , m_isHandlingProgrammaticScroll(false)
59 {
60 }
61
62 ScrollingTree::~ScrollingTree()
63 {
64     ASSERT(!m_scrollingCoordinator);
65 }
66
67 ScrollingTree::EventResult ScrollingTree::tryToHandleWheelEvent(const PlatformWheelEvent& wheelEvent)
68 {
69     {
70         MutexLocker lock(m_mutex);
71
72         if (m_hasWheelEventHandlers)
73             return SendToMainThread;
74
75         if (!m_nonFastScrollableRegion.isEmpty()) {
76             // FIXME: This is not correct for non-default scroll origins.
77             IntPoint position = wheelEvent.position();
78             position.moveBy(m_mainFrameScrollPosition);
79             if (m_nonFastScrollableRegion.contains(position))
80                 return SendToMainThread;
81         }
82     }
83
84     if (willWheelEventStartSwipeGesture(wheelEvent))
85         return DidNotHandleEvent;
86
87     ScrollingThread::dispatch(bind(&ScrollingTree::handleWheelEvent, this, wheelEvent));
88     return DidHandleEvent;
89 }
90
91 void ScrollingTree::updateBackForwardState(bool canGoBack, bool canGoForward)
92 {
93     MutexLocker locker(m_swipeStateMutex);
94
95     m_canGoBack = canGoBack;
96     m_canGoForward = canGoForward;
97 }
98
99 void ScrollingTree::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
100 {
101     ASSERT(ScrollingThread::isCurrentThread());
102
103     m_rootNode->handleWheelEvent(wheelEvent);
104 }
105
106 static void derefScrollingCoordinator(ScrollingCoordinator* scrollingCoordinator)
107 {
108     ASSERT(isMainThread());
109
110     scrollingCoordinator->deref();
111 }
112
113 void ScrollingTree::invalidate()
114 {
115     // Invalidate is dispatched by the ScrollingCoordinator class on the ScrollingThread
116     // to break the reference cycle between ScrollingTree and ScrollingCoordinator when the
117     // ScrollingCoordinator's page is destroyed.
118     ASSERT(ScrollingThread::isCurrentThread());
119
120     // Since this can potentially be the last reference to the scrolling coordinator,
121     // we need to release it on the main thread since it has member variables (such as timers)
122     // that expect to be destroyed from the main thread.
123     callOnMainThread(bind(derefScrollingCoordinator, m_scrollingCoordinator.release().leakRef()));
124 }
125
126 void ScrollingTree::commitNewTreeState(PassOwnPtr<ScrollingStateTree> scrollingStateTree)
127 {
128     ASSERT(ScrollingThread::isCurrentThread());
129
130     if (scrollingStateTree->rootStateNode()->changedProperties() & (ScrollingStateScrollingNode::WheelEventHandlerCount | ScrollingStateScrollingNode::NonFastScrollableRegion) || scrollingStateTree->rootStateNode()->scrollLayerDidChange()) {
131         MutexLocker lock(m_mutex);
132
133         if (scrollingStateTree->rootStateNode()->scrollLayerDidChange())
134             m_mainFrameScrollPosition = IntPoint();
135         if (scrollingStateTree->rootStateNode()->changedProperties() & ScrollingStateScrollingNode::WheelEventHandlerCount)
136             m_hasWheelEventHandlers = scrollingStateTree->rootStateNode()->wheelEventHandlerCount();
137         if (scrollingStateTree->rootStateNode()->changedProperties() & ScrollingStateScrollingNode::NonFastScrollableRegion)
138             m_nonFastScrollableRegion = scrollingStateTree->rootStateNode()->nonFastScrollableRegion();
139     }
140     
141     TemporaryChange<bool> changeHandlingProgrammaticScroll(m_isHandlingProgrammaticScroll, scrollingStateTree->rootStateNode()->requestedScrollPositionRepresentsProgrammaticScroll());
142
143     removeDestroyedNodes(scrollingStateTree.get());
144     updateTreeFromStateNode(scrollingStateTree->rootStateNode());
145 }
146
147 void ScrollingTree::updateTreeFromStateNode(ScrollingStateNode* stateNode)
148 {
149     // This fuction recurses through the ScrollingStateTree and updates the corresponding ScrollingTreeNodes.
150     // Find the ScrollingTreeNode associated with the current stateNode using the shared ID and our HashMap.
151     ScrollingTreeNodeMap::const_iterator it = m_nodeMap.find(stateNode->scrollingNodeID());
152
153     if (it != m_nodeMap.end()) {
154         ScrollingTreeNode* node = it->value;
155         node->update(stateNode);
156     } else {
157         // 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
158         // root node.
159         ScrollingNodeID nodeID = stateNode->scrollingNodeID();
160         if (!stateNode->parent()) {
161             // This is the root node.
162             if (!m_rootNode)
163                 m_rootNode = ScrollingTreeScrollingNode::create(this, nodeID);
164
165             m_nodeMap.set(nodeID, m_rootNode.get());
166             m_rootNode->update(stateNode);
167         } else {
168             OwnPtr<ScrollingTreeNode> newNode;
169             if (stateNode->isScrollingNode())
170                 newNode = ScrollingTreeScrollingNode::create(this, nodeID);
171             else if (stateNode->isFixedNode())
172                 newNode = ScrollingTreeFixedNode::create(this, nodeID);
173             else if (stateNode->isStickyNode())
174                 newNode = ScrollingTreeStickyNode::create(this, nodeID);
175             else
176                 ASSERT_NOT_REACHED();
177
178             ScrollingTreeNode* newNodeRawPtr = newNode.get();
179             m_nodeMap.set(nodeID, newNodeRawPtr);
180             ScrollingTreeNodeMap::const_iterator it = m_nodeMap.find(stateNode->parent()->scrollingNodeID());
181             ASSERT(it != m_nodeMap.end());
182             if (it != m_nodeMap.end()) {
183                 ScrollingTreeNode* parent = it->value;
184                 newNode->setParent(parent);
185                 parent->appendChild(newNode.release());
186             }
187             newNodeRawPtr->update(stateNode);
188         }
189     }
190
191     // Now update the children if we have any.
192     Vector<OwnPtr<ScrollingStateNode> >* stateNodeChildren = stateNode->children();
193     if (!stateNodeChildren)
194         return;
195
196     size_t size = stateNodeChildren->size();
197     for (size_t i = 0; i < size; ++i)
198         updateTreeFromStateNode(stateNodeChildren->at(i).get());
199 }
200
201 void ScrollingTree::removeDestroyedNodes(ScrollingStateTree* stateTree)
202 {
203     const Vector<ScrollingNodeID>& removedNodes = stateTree->removedNodes();
204     size_t size = removedNodes.size();
205     for (size_t i = 0; i < size; ++i) {
206         ScrollingTreeNode* node = m_nodeMap.take(removedNodes[i]);
207         // Never destroy the root node. There will be a new root node in the state tree, and we will
208         // associate it with our existing root node in updateTreeFromStateNode().
209         if (node && node->parent())
210             m_rootNode->removeChild(node);
211     }
212 }
213
214 void ScrollingTree::setMainFramePinState(bool pinnedToTheLeft, bool pinnedToTheRight)
215 {
216     MutexLocker locker(m_swipeStateMutex);
217
218     m_mainFramePinnedToTheLeft = pinnedToTheLeft;
219     m_mainFramePinnedToTheRight = pinnedToTheRight;
220 }
221
222 void ScrollingTree::updateMainFrameScrollPosition(const IntPoint& scrollPosition, SetOrSyncScrollingLayerPosition scrollingLayerPositionAction)
223 {
224     if (!m_scrollingCoordinator)
225         return;
226
227     {
228         MutexLocker lock(m_mutex);
229         m_mainFrameScrollPosition = scrollPosition;
230     }
231
232     callOnMainThread(bind(&ScrollingCoordinator::scheduleUpdateMainFrameScrollPosition, m_scrollingCoordinator.get(), scrollPosition, m_isHandlingProgrammaticScroll, scrollingLayerPositionAction));
233 }
234
235 IntPoint ScrollingTree::mainFrameScrollPosition()
236 {
237     MutexLocker lock(m_mutex);
238     return m_mainFrameScrollPosition;
239 }
240
241 #if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN))
242 void ScrollingTree::handleWheelEventPhase(PlatformWheelEventPhase phase)
243 {
244     if (!m_scrollingCoordinator)
245         return;
246
247     callOnMainThread(bind(&ScrollingCoordinator::handleWheelEventPhase, m_scrollingCoordinator.get(), phase));
248 }
249 #endif
250
251 bool ScrollingTree::canGoBack()
252 {
253     MutexLocker lock(m_swipeStateMutex);
254
255     return m_canGoBack;
256 }
257
258 bool ScrollingTree::canGoForward()
259 {
260     MutexLocker lock(m_swipeStateMutex);
261
262     return m_canGoForward;
263 }
264
265 bool ScrollingTree::isRubberBandInProgress()
266 {
267     MutexLocker lock(m_mutex);    
268
269     return m_mainFrameIsRubberBanding;
270 }
271
272 void ScrollingTree::setMainFrameIsRubberBanding(bool isRubberBanding)
273 {
274     MutexLocker locker(m_mutex);
275
276     m_mainFrameIsRubberBanding = isRubberBanding;
277 }
278
279 bool ScrollingTree::willWheelEventStartSwipeGesture(const PlatformWheelEvent& wheelEvent)
280 {
281     if (wheelEvent.phase() != PlatformWheelEventPhaseBegan)
282         return false;
283     if (!wheelEvent.deltaX())
284         return false;
285
286     MutexLocker lock(m_swipeStateMutex);
287
288     if (wheelEvent.deltaX() > 0 && m_mainFramePinnedToTheLeft && m_canGoBack)
289         return true;
290     if (wheelEvent.deltaX() < 0 && m_mainFramePinnedToTheRight && m_canGoForward)
291         return true;
292
293     return false;
294 }
295
296 void ScrollingTree::setScrollingPerformanceLoggingEnabled(bool flag)
297 {
298     m_scrollingPerformanceLoggingEnabled = flag;
299 }
300
301 bool ScrollingTree::scrollingPerformanceLoggingEnabled()
302 {
303     return m_scrollingPerformanceLoggingEnabled;
304 }
305
306 } // namespace WebCore
307
308 #endif // ENABLE(THREADED_SCROLLING)