Remove scrolling tree dependency on wheel event handler counts, and use fast scrollin...
[WebKit-https.git] / Source / WebCore / page / scrolling / ScrollingTree.cpp
1 /*
2  * Copyright (C) 2012, 2013, 2014 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(ASYNC_SCROLLING)
30
31 #include "PlatformWheelEvent.h"
32 #include "ScrollingStateTree.h"
33 #include "ScrollingTreeNode.h"
34 #include "ScrollingTreeOverflowScrollingNode.h"
35 #include "ScrollingTreeScrollingNode.h"
36 #include <wtf/TemporaryChange.h>
37
38 namespace WebCore {
39
40 ScrollingTree::ScrollingTree()
41     : m_rubberBandsAtLeft(true)
42     , m_rubberBandsAtRight(true)
43     , m_rubberBandsAtTop(true)
44     , m_rubberBandsAtBottom(true)
45     , m_mainFramePinnedToTheLeft(true)
46     , m_mainFramePinnedToTheRight(true)
47     , m_mainFramePinnedToTheTop(true)
48     , m_mainFramePinnedToTheBottom(true)
49     , m_mainFrameIsRubberBanding(false)
50     , m_scrollPinningBehavior(DoNotPin)
51     , m_latchedNode(0)
52     , m_scrollingPerformanceLoggingEnabled(false)
53     , m_isHandlingProgrammaticScroll(false)
54     , m_fixedOrStickyNodeCount(0)
55 {
56 }
57
58 ScrollingTree::~ScrollingTree()
59 {
60 }
61
62 bool ScrollingTree::shouldHandleWheelEventSynchronously(const PlatformWheelEvent& wheelEvent)
63 {
64     // This method is invoked by the event handling thread
65     MutexLocker lock(m_mutex);
66
67     bool shouldSetLatch = wheelEvent.shouldConsiderLatching();
68     
69     if (hasLatchedNode() && !shouldSetLatch)
70         return false;
71
72     if (shouldSetLatch)
73         m_latchedNode = 0;
74     
75     if (!m_nonFastScrollableRegion.isEmpty()) {
76         // FIXME: This is not correct for non-default scroll origins.
77         FloatPoint position = wheelEvent.position();
78         position.moveBy(m_mainFrameScrollPosition);
79         if (m_nonFastScrollableRegion.contains(roundedIntPoint(position)))
80             return true;
81     }
82     return false;
83 }
84
85 void ScrollingTree::setOrClearLatchedNode(const PlatformWheelEvent& wheelEvent, ScrollingNodeID nodeID)
86 {
87     if (wheelEvent.shouldConsiderLatching())
88         setLatchedNode(nodeID);
89     else if (wheelEvent.shouldResetLatching())
90         clearLatchedNode();
91 }
92
93 void ScrollingTree::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
94 {
95     if (m_rootNode)
96         downcast<ScrollingTreeScrollingNode>(*m_rootNode).handleWheelEvent(wheelEvent);
97 }
98
99 void ScrollingTree::viewportChangedViaDelegatedScrolling(ScrollingNodeID nodeID, const WebCore::FloatRect& fixedPositionRect, double scale)
100 {
101     ScrollingTreeNode* node = nodeForID(nodeID);
102     if (!is<ScrollingTreeScrollingNode>(node))
103         return;
104
105     downcast<ScrollingTreeScrollingNode>(*node).updateLayersAfterViewportChange(fixedPositionRect, scale);
106 }
107
108 void ScrollingTree::scrollPositionChangedViaDelegatedScrolling(ScrollingNodeID nodeID, const WebCore::FloatPoint& scrollPosition, bool inUserInteration)
109 {
110     ScrollingTreeNode* node = nodeForID(nodeID);
111     if (!is<ScrollingTreeOverflowScrollingNode>(node))
112         return;
113
114     // Update descendant nodes
115     downcast<ScrollingTreeOverflowScrollingNode>(*node).updateLayersAfterDelegatedScroll(scrollPosition);
116
117     // Update GraphicsLayers and scroll state.
118     scrollingTreeNodeDidScroll(nodeID, scrollPosition, inUserInteration ? SyncScrollingLayerPosition : SetScrollingLayerPosition);
119 }
120
121 void ScrollingTree::commitNewTreeState(std::unique_ptr<ScrollingStateTree> scrollingStateTree)
122 {
123     bool rootStateNodeChanged = scrollingStateTree->hasNewRootStateNode();
124     
125     ScrollingStateScrollingNode* rootNode = scrollingStateTree->rootStateNode();
126     if (rootNode
127         && (rootStateNodeChanged
128             || rootNode->hasChangedProperty(ScrollingStateFrameScrollingNode::NonFastScrollableRegion)
129             || rootNode->hasChangedProperty(ScrollingStateNode::ScrollLayer))) {
130         MutexLocker lock(m_mutex);
131
132         if (rootStateNodeChanged || rootNode->hasChangedProperty(ScrollingStateNode::ScrollLayer))
133             m_mainFrameScrollPosition = FloatPoint();
134         if (rootStateNodeChanged || rootNode->hasChangedProperty(ScrollingStateFrameScrollingNode::NonFastScrollableRegion))
135             m_nonFastScrollableRegion = scrollingStateTree->rootStateNode()->nonFastScrollableRegion();
136     }
137     
138     bool scrollRequestIsProgammatic = rootNode ? rootNode->requestedScrollPositionRepresentsProgrammaticScroll() : false;
139     TemporaryChange<bool> changeHandlingProgrammaticScroll(m_isHandlingProgrammaticScroll, scrollRequestIsProgammatic);
140
141     removeDestroyedNodes(*scrollingStateTree);
142
143     OrphanScrollingNodeMap orphanNodes;
144     updateTreeFromStateNode(rootNode, orphanNodes);
145 }
146
147 void ScrollingTree::updateTreeFromStateNode(const ScrollingStateNode* stateNode, OrphanScrollingNodeMap& orphanNodes)
148 {
149     if (!stateNode) {
150         m_nodeMap.clear();
151         m_rootNode = nullptr;
152         return;
153     }
154     
155     ScrollingNodeID nodeID = stateNode->scrollingNodeID();
156     ScrollingNodeID parentNodeID = stateNode->parentNodeID();
157
158     auto it = m_nodeMap.find(nodeID);
159
160     RefPtr<ScrollingTreeNode> node;
161     if (it != m_nodeMap.end())
162         node = it->value;
163     else {
164         node = createScrollingTreeNode(stateNode->nodeType(), nodeID);
165         if (!parentNodeID) {
166             // This is the root node. Clear the node map.
167             ASSERT(stateNode->nodeType() == FrameScrollingNode);
168             m_rootNode = node;
169             m_nodeMap.clear();
170         } 
171         m_nodeMap.set(nodeID, node.get());
172     }
173
174     if (parentNodeID) {
175         auto parentIt = m_nodeMap.find(parentNodeID);
176         ASSERT_WITH_SECURITY_IMPLICATION(parentIt != m_nodeMap.end());
177         if (parentIt != m_nodeMap.end()) {
178             ScrollingTreeNode* parent = parentIt->value;
179             node->setParent(parent);
180             parent->appendChild(node);
181         }
182     }
183
184     node->updateBeforeChildren(*stateNode);
185     
186     // Move all children into the orphanNodes map. Live ones will get added back as we recurse over children.
187     if (auto nodeChildren = node->children()) {
188         for (auto& childScrollingNode : *nodeChildren) {
189             childScrollingNode->setParent(nullptr);
190             orphanNodes.add(childScrollingNode->scrollingNodeID(), childScrollingNode);
191         }
192         nodeChildren->clear();
193     }
194
195     // Now update the children if we have any.
196     if (auto children = stateNode->children()) {
197         for (auto& child : *children)
198             updateTreeFromStateNode(child.get(), orphanNodes);
199     }
200
201     node->updateAfterChildren(*stateNode);
202 }
203
204 void ScrollingTree::removeDestroyedNodes(const ScrollingStateTree& stateTree)
205 {
206     for (const auto& removedNodeID : stateTree.removedNodes()) {
207         m_nodeMap.remove(removedNodeID);
208         if (removedNodeID == m_latchedNode)
209             clearLatchedNode();
210     }
211 }
212
213 ScrollingTreeNode* ScrollingTree::nodeForID(ScrollingNodeID nodeID) const
214 {
215     if (!nodeID)
216         return nullptr;
217
218     return m_nodeMap.get(nodeID);
219 }
220
221 void ScrollingTree::setMainFramePinState(bool pinnedToTheLeft, bool pinnedToTheRight, bool pinnedToTheTop, bool pinnedToTheBottom)
222 {
223     MutexLocker locker(m_swipeStateMutex);
224
225     m_mainFramePinnedToTheLeft = pinnedToTheLeft;
226     m_mainFramePinnedToTheRight = pinnedToTheRight;
227     m_mainFramePinnedToTheTop = pinnedToTheTop;
228     m_mainFramePinnedToTheBottom = pinnedToTheBottom;
229 }
230
231 FloatPoint ScrollingTree::mainFrameScrollPosition()
232 {
233     MutexLocker lock(m_mutex);
234     return m_mainFrameScrollPosition;
235 }
236
237 void ScrollingTree::setMainFrameScrollPosition(FloatPoint position)
238 {
239     MutexLocker lock(m_mutex);
240     m_mainFrameScrollPosition = position;
241 }
242
243 bool ScrollingTree::isPointInNonFastScrollableRegion(IntPoint p)
244 {
245     MutexLocker lock(m_mutex);
246     
247     return m_nonFastScrollableRegion.contains(p);
248 }
249
250 bool ScrollingTree::isRubberBandInProgress()
251 {
252     MutexLocker lock(m_mutex);    
253
254     return m_mainFrameIsRubberBanding;
255 }
256
257 void ScrollingTree::setMainFrameIsRubberBanding(bool isRubberBanding)
258 {
259     MutexLocker locker(m_mutex);
260
261     m_mainFrameIsRubberBanding = isRubberBanding;
262 }
263
264 void ScrollingTree::setCanRubberBandState(bool canRubberBandAtLeft, bool canRubberBandAtRight, bool canRubberBandAtTop, bool canRubberBandAtBottom)
265 {
266     MutexLocker locker(m_swipeStateMutex);
267
268     m_rubberBandsAtLeft = canRubberBandAtLeft;
269     m_rubberBandsAtRight = canRubberBandAtRight;
270     m_rubberBandsAtTop = canRubberBandAtTop;
271     m_rubberBandsAtBottom = canRubberBandAtBottom;
272 }
273
274 bool ScrollingTree::rubberBandsAtLeft()
275 {
276     MutexLocker lock(m_swipeStateMutex);
277
278     return m_rubberBandsAtLeft;
279 }
280
281 bool ScrollingTree::rubberBandsAtRight()
282 {
283     MutexLocker lock(m_swipeStateMutex);
284
285     return m_rubberBandsAtRight;
286 }
287
288 bool ScrollingTree::rubberBandsAtBottom()
289 {
290     MutexLocker lock(m_swipeStateMutex);
291
292     return m_rubberBandsAtBottom;
293 }
294
295 bool ScrollingTree::rubberBandsAtTop()
296 {
297     MutexLocker lock(m_swipeStateMutex);
298
299     return m_rubberBandsAtTop;
300 }
301
302 bool ScrollingTree::isHandlingProgrammaticScroll()
303 {
304     return m_isHandlingProgrammaticScroll;
305 }
306
307 void ScrollingTree::setScrollPinningBehavior(ScrollPinningBehavior pinning)
308 {
309     MutexLocker locker(m_swipeStateMutex);
310     
311     m_scrollPinningBehavior = pinning;
312 }
313
314 ScrollPinningBehavior ScrollingTree::scrollPinningBehavior()
315 {
316     MutexLocker lock(m_swipeStateMutex);
317     
318     return m_scrollPinningBehavior;
319 }
320
321 bool ScrollingTree::willWheelEventStartSwipeGesture(const PlatformWheelEvent& wheelEvent)
322 {
323     if (wheelEvent.phase() != PlatformWheelEventPhaseBegan)
324         return false;
325
326     MutexLocker lock(m_swipeStateMutex);
327
328     if (wheelEvent.deltaX() > 0 && m_mainFramePinnedToTheLeft && !m_rubberBandsAtLeft)
329         return true;
330     if (wheelEvent.deltaX() < 0 && m_mainFramePinnedToTheRight && !m_rubberBandsAtRight)
331         return true;
332     if (wheelEvent.deltaY() > 0 && m_mainFramePinnedToTheTop && !m_rubberBandsAtTop)
333         return true;
334     if (wheelEvent.deltaY() < 0 && m_mainFramePinnedToTheBottom && !m_rubberBandsAtBottom)
335         return true;
336
337     return false;
338 }
339
340 void ScrollingTree::setScrollingPerformanceLoggingEnabled(bool flag)
341 {
342     m_scrollingPerformanceLoggingEnabled = flag;
343 }
344
345 bool ScrollingTree::scrollingPerformanceLoggingEnabled()
346 {
347     return m_scrollingPerformanceLoggingEnabled;
348 }
349
350 ScrollingNodeID ScrollingTree::latchedNode()
351 {
352     MutexLocker locker(m_mutex);
353     return m_latchedNode;
354 }
355
356 void ScrollingTree::setLatchedNode(ScrollingNodeID node)
357 {
358     MutexLocker locker(m_mutex);
359     m_latchedNode = node;
360 }
361
362 void ScrollingTree::clearLatchedNode()
363 {
364     MutexLocker locker(m_mutex);
365     m_latchedNode = 0;
366 }
367
368 } // namespace WebCore
369
370 #endif // ENABLE(ASYNC_SCROLLING)