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