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