7fb27f0327f3c68c65dadd23ae21f831d9406b28
[WebKit-https.git] / Source / WebCore / page / scrolling / ScrollingTree.cpp
1 /*
2  * Copyright (C) 2012-2015 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 "EventNames.h"
32 #include "Logging.h"
33 #include "PlatformWheelEvent.h"
34 #include "ScrollingStateFrameScrollingNode.h"
35 #include "ScrollingStateTree.h"
36 #include "ScrollingTreeFrameScrollingNode.h"
37 #include "ScrollingTreeNode.h"
38 #include "ScrollingTreeScrollingNode.h"
39 #include <wtf/SetForScope.h>
40 #include <wtf/text/TextStream.h>
41
42 namespace WebCore {
43
44 ScrollingTree::ScrollingTree() = default;
45
46 ScrollingTree::~ScrollingTree() = default;
47
48 bool ScrollingTree::shouldHandleWheelEventSynchronously(const PlatformWheelEvent& wheelEvent)
49 {
50     // This method is invoked by the event handling thread
51     LockHolder lock(m_mutex);
52
53     bool shouldSetLatch = wheelEvent.shouldConsiderLatching();
54     
55     if (hasLatchedNode() && !shouldSetLatch)
56         return false;
57
58     if (shouldSetLatch)
59         m_latchedNodeID = 0;
60     
61     if (!m_eventTrackingRegions.isEmpty() && m_rootNode) {
62         auto& frameScrollingNode = downcast<ScrollingTreeFrameScrollingNode>(*m_rootNode);
63         FloatPoint position = wheelEvent.position();
64         position.move(frameScrollingNode.viewToContentsOffset(m_mainFrameScrollPosition));
65
66         const EventNames& names = eventNames();
67         IntPoint roundedPosition = roundedIntPoint(position);
68         bool isSynchronousDispatchRegion = m_eventTrackingRegions.trackingTypeForPoint(names.wheelEvent, roundedPosition) == TrackingType::Synchronous
69             || m_eventTrackingRegions.trackingTypeForPoint(names.mousewheelEvent, roundedPosition) == TrackingType::Synchronous;
70         LOG_WITH_STREAM(Scrolling, stream << "ScrollingTree::shouldHandleWheelEventSynchronously: wheelEvent at " << wheelEvent.position() << " mapped to content point " << position << ", in non-fast region " << isSynchronousDispatchRegion);
71
72         if (isSynchronousDispatchRegion)
73             return true;
74     }
75     return false;
76 }
77
78 void ScrollingTree::setOrClearLatchedNode(const PlatformWheelEvent& wheelEvent, ScrollingNodeID nodeID)
79 {
80     if (wheelEvent.shouldConsiderLatching())
81         setLatchedNode(nodeID);
82     else if (wheelEvent.shouldResetLatching())
83         clearLatchedNode();
84 }
85
86 void ScrollingTree::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
87 {
88     if (m_rootNode)
89         downcast<ScrollingTreeScrollingNode>(*m_rootNode).handleWheelEvent(wheelEvent);
90 }
91
92 void ScrollingTree::viewportChangedViaDelegatedScrolling(ScrollingNodeID nodeID, const FloatRect& fixedPositionRect, double scale)
93 {
94     auto* node = nodeForID(nodeID);
95     if (!is<ScrollingTreeScrollingNode>(node))
96         return;
97
98     downcast<ScrollingTreeScrollingNode>(*node).updateLayersAfterViewportChange(fixedPositionRect, scale);
99 }
100
101 void ScrollingTree::scrollPositionChangedViaDelegatedScrolling(ScrollingNodeID nodeID, const WebCore::FloatPoint& scrollPosition, bool inUserInteraction)
102 {
103     auto* node = nodeForID(nodeID);
104     if (!is<ScrollingTreeScrollingNode>(node))
105         return;
106
107     // Update descendant nodes
108     downcast<ScrollingTreeScrollingNode>(*node).updateLayersAfterDelegatedScroll(scrollPosition);
109
110     // Update GraphicsLayers and scroll state.
111     scrollingTreeNodeDidScroll(nodeID, scrollPosition, WTF::nullopt, inUserInteraction ? ScrollingLayerPositionAction::Sync : ScrollingLayerPositionAction::Set);
112 }
113
114 void ScrollingTree::commitTreeState(std::unique_ptr<ScrollingStateTree> scrollingStateTree)
115 {
116     bool rootStateNodeChanged = scrollingStateTree->hasNewRootStateNode();
117     
118     LOG(Scrolling, "\nScrollingTree::commitTreeState");
119     
120     auto* rootNode = scrollingStateTree->rootStateNode();
121     if (rootNode
122         && (rootStateNodeChanged
123             || rootNode->hasChangedProperty(ScrollingStateFrameScrollingNode::EventTrackingRegion)
124             || rootNode->hasChangedProperty(ScrollingStateNode::ScrollLayer)
125             || rootNode->hasChangedProperty(ScrollingStateFrameScrollingNode::VisualViewportEnabled))) {
126         LockHolder lock(m_mutex);
127
128         if (rootStateNodeChanged || rootNode->hasChangedProperty(ScrollingStateNode::ScrollLayer))
129             m_mainFrameScrollPosition = FloatPoint();
130
131         if (rootStateNodeChanged || rootNode->hasChangedProperty(ScrollingStateFrameScrollingNode::EventTrackingRegion))
132             m_eventTrackingRegions = scrollingStateTree->rootStateNode()->eventTrackingRegions();
133
134         if (rootStateNodeChanged || rootNode->hasChangedProperty(ScrollingStateFrameScrollingNode::VisualViewportEnabled))
135             m_visualViewportEnabled = scrollingStateTree->rootStateNode()->visualViewportEnabled();
136     }
137     
138     bool scrollRequestIsProgammatic = rootNode ? rootNode->requestedScrollPositionRepresentsProgrammaticScroll() : false;
139     SetForScope<bool> changeHandlingProgrammaticScroll(m_isHandlingProgrammaticScroll, scrollRequestIsProgammatic);
140
141     // unvisitedNodes starts with all nodes in the map; we remove nodes as we visit them. At the end, it's the unvisited nodes.
142     // We can't use orphanNodes for this, because orphanNodes won't contain descendants of removed nodes.
143     HashSet<ScrollingNodeID> unvisitedNodes;
144     for (auto nodeID : m_nodeMap.keys())
145         unvisitedNodes.add(nodeID);
146
147     // orphanNodes keeps child nodes alive while we rebuild child lists.
148     OrphanScrollingNodeMap orphanNodes;
149     updateTreeFromStateNode(rootNode, orphanNodes, unvisitedNodes);
150     
151     for (auto nodeID : unvisitedNodes) {
152         if (nodeID == m_latchedNodeID)
153             clearLatchedNode();
154         
155         LOG(Scrolling, "ScrollingTree::commitTreeState - removing unvisited node %llu", nodeID);
156         m_nodeMap.remove(nodeID);
157     }
158 }
159
160 void ScrollingTree::updateTreeFromStateNode(const ScrollingStateNode* stateNode, OrphanScrollingNodeMap& orphanNodes, HashSet<ScrollingNodeID>& unvisitedNodes)
161 {
162     if (!stateNode) {
163         m_nodeMap.clear();
164         m_rootNode = nullptr;
165         return;
166     }
167     
168     ScrollingNodeID nodeID = stateNode->scrollingNodeID();
169     ScrollingNodeID parentNodeID = stateNode->parentNodeID();
170
171     auto it = m_nodeMap.find(nodeID);
172
173     RefPtr<ScrollingTreeNode> node;
174     if (it != m_nodeMap.end()) {
175         node = it->value;
176         unvisitedNodes.remove(nodeID);
177     } else {
178         node = createScrollingTreeNode(stateNode->nodeType(), nodeID);
179         if (!parentNodeID) {
180             // This is the root node. Clear the node map.
181             ASSERT(stateNode->isFrameScrollingNode());
182             m_rootNode = node;
183             m_nodeMap.clear();
184         } 
185         m_nodeMap.set(nodeID, node.get());
186     }
187
188     if (parentNodeID) {
189         auto parentIt = m_nodeMap.find(parentNodeID);
190         ASSERT_WITH_SECURITY_IMPLICATION(parentIt != m_nodeMap.end());
191         if (parentIt != m_nodeMap.end()) {
192             auto* parent = parentIt->value;
193             node->setParent(parent);
194             parent->appendChild(*node);
195         }
196     }
197
198     node->commitStateBeforeChildren(*stateNode);
199     
200     // Move all children into the orphanNodes map. Live ones will get added back as we recurse over children.
201     if (auto nodeChildren = node->children()) {
202         for (auto& childScrollingNode : *nodeChildren) {
203             childScrollingNode->setParent(nullptr);
204             orphanNodes.add(childScrollingNode->scrollingNodeID(), childScrollingNode.get());
205         }
206         nodeChildren->clear();
207     }
208
209     // Now update the children if we have any.
210     if (auto children = stateNode->children()) {
211         for (auto& child : *children)
212             updateTreeFromStateNode(child.get(), orphanNodes, unvisitedNodes);
213     }
214
215     node->commitStateAfterChildren(*stateNode);
216 }
217
218 ScrollingTreeNode* ScrollingTree::nodeForID(ScrollingNodeID nodeID) const
219 {
220     if (!nodeID)
221         return nullptr;
222
223     return m_nodeMap.get(nodeID);
224 }
225
226 void ScrollingTree::setMainFramePinState(bool pinnedToTheLeft, bool pinnedToTheRight, bool pinnedToTheTop, bool pinnedToTheBottom)
227 {
228     LockHolder locker(m_swipeStateMutex);
229
230     m_mainFramePinnedToTheLeft = pinnedToTheLeft;
231     m_mainFramePinnedToTheRight = pinnedToTheRight;
232     m_mainFramePinnedToTheTop = pinnedToTheTop;
233     m_mainFramePinnedToTheBottom = pinnedToTheBottom;
234 }
235
236 FloatPoint ScrollingTree::mainFrameScrollPosition()
237 {
238     LockHolder lock(m_mutex);
239     return m_mainFrameScrollPosition;
240 }
241
242 void ScrollingTree::setMainFrameScrollPosition(FloatPoint position)
243 {
244     LockHolder lock(m_mutex);
245     m_mainFrameScrollPosition = position;
246 }
247
248 TrackingType ScrollingTree::eventTrackingTypeForPoint(const AtomicString& eventName, IntPoint p)
249 {
250     LockHolder lock(m_mutex);
251     
252     return m_eventTrackingRegions.trackingTypeForPoint(eventName, p);
253 }
254
255 bool ScrollingTree::isRubberBandInProgress()
256 {
257     LockHolder lock(m_mutex);    
258
259     return m_mainFrameIsRubberBanding;
260 }
261
262 void ScrollingTree::setMainFrameIsRubberBanding(bool isRubberBanding)
263 {
264     LockHolder locker(m_mutex);
265
266     m_mainFrameIsRubberBanding = isRubberBanding;
267 }
268
269 bool ScrollingTree::isScrollSnapInProgress()
270 {
271     LockHolder lock(m_mutex);
272     
273     return m_mainFrameIsScrollSnapping;
274 }
275     
276 void ScrollingTree::setMainFrameIsScrollSnapping(bool isScrollSnapping)
277 {
278     LockHolder locker(m_mutex);
279     
280     m_mainFrameIsScrollSnapping = isScrollSnapping;
281 }
282
283 void ScrollingTree::setCanRubberBandState(bool canRubberBandAtLeft, bool canRubberBandAtRight, bool canRubberBandAtTop, bool canRubberBandAtBottom)
284 {
285     LockHolder locker(m_swipeStateMutex);
286
287     m_rubberBandsAtLeft = canRubberBandAtLeft;
288     m_rubberBandsAtRight = canRubberBandAtRight;
289     m_rubberBandsAtTop = canRubberBandAtTop;
290     m_rubberBandsAtBottom = canRubberBandAtBottom;
291 }
292
293 bool ScrollingTree::rubberBandsAtLeft()
294 {
295     LockHolder lock(m_swipeStateMutex);
296
297     return m_rubberBandsAtLeft;
298 }
299
300 bool ScrollingTree::rubberBandsAtRight()
301 {
302     LockHolder lock(m_swipeStateMutex);
303
304     return m_rubberBandsAtRight;
305 }
306
307 bool ScrollingTree::rubberBandsAtBottom()
308 {
309     LockHolder lock(m_swipeStateMutex);
310
311     return m_rubberBandsAtBottom;
312 }
313
314 bool ScrollingTree::rubberBandsAtTop()
315 {
316     LockHolder lock(m_swipeStateMutex);
317
318     return m_rubberBandsAtTop;
319 }
320
321 bool ScrollingTree::isHandlingProgrammaticScroll()
322 {
323     return m_isHandlingProgrammaticScroll;
324 }
325
326 void ScrollingTree::setScrollPinningBehavior(ScrollPinningBehavior pinning)
327 {
328     LockHolder locker(m_swipeStateMutex);
329     
330     m_scrollPinningBehavior = pinning;
331 }
332
333 ScrollPinningBehavior ScrollingTree::scrollPinningBehavior()
334 {
335     LockHolder lock(m_swipeStateMutex);
336     
337     return m_scrollPinningBehavior;
338 }
339
340 bool ScrollingTree::willWheelEventStartSwipeGesture(const PlatformWheelEvent& wheelEvent)
341 {
342     if (wheelEvent.phase() != PlatformWheelEventPhaseBegan)
343         return false;
344
345     LockHolder lock(m_swipeStateMutex);
346
347     if (wheelEvent.deltaX() > 0 && m_mainFramePinnedToTheLeft && !m_rubberBandsAtLeft)
348         return true;
349     if (wheelEvent.deltaX() < 0 && m_mainFramePinnedToTheRight && !m_rubberBandsAtRight)
350         return true;
351     if (wheelEvent.deltaY() > 0 && m_mainFramePinnedToTheTop && !m_rubberBandsAtTop)
352         return true;
353     if (wheelEvent.deltaY() < 0 && m_mainFramePinnedToTheBottom && !m_rubberBandsAtBottom)
354         return true;
355
356     return false;
357 }
358
359 void ScrollingTree::setScrollingPerformanceLoggingEnabled(bool flag)
360 {
361     m_scrollingPerformanceLoggingEnabled = flag;
362 }
363
364 bool ScrollingTree::scrollingPerformanceLoggingEnabled()
365 {
366     return m_scrollingPerformanceLoggingEnabled;
367 }
368
369 ScrollingNodeID ScrollingTree::latchedNode()
370 {
371     LockHolder locker(m_mutex);
372     return m_latchedNodeID;
373 }
374
375 void ScrollingTree::setLatchedNode(ScrollingNodeID node)
376 {
377     LockHolder locker(m_mutex);
378     m_latchedNodeID = node;
379 }
380
381 void ScrollingTree::clearLatchedNode()
382 {
383     LockHolder locker(m_mutex);
384     m_latchedNodeID = 0;
385 }
386
387 String ScrollingTree::scrollingTreeAsText()
388 {
389     TextStream ts(TextStream::LineMode::MultipleLine);
390
391     TextStream::GroupScope scope(ts);
392     ts << "scrolling tree";
393     
394     if (m_latchedNodeID)
395         ts.dumpProperty("latched node", m_latchedNodeID);
396
397     if (m_mainFrameScrollPosition != IntPoint())
398         ts.dumpProperty("main frame scroll position", m_mainFrameScrollPosition);
399     
400     {
401         LockHolder lock(m_mutex);
402         if (m_rootNode) {
403             TextStream::GroupScope scope(ts);
404             m_rootNode->dump(ts, ScrollingStateTreeAsTextBehaviorIncludeLayerPositions);
405         }
406     }
407
408     return ts.release();
409 }
410
411 #if ENABLE(POINTER_EVENTS)
412 Optional<TouchActionData> ScrollingTree::touchActionDataAtPoint(IntPoint p) const
413 {
414     // FIXME: This does not handle the case where there are multiple regions matching this point.
415     for (auto& touchActionData : m_eventTrackingRegions.touchActionData) {
416         if (touchActionData.region.contains(p))
417             return touchActionData;
418     }
419
420     return WTF::nullopt;
421 }
422 #endif
423
424 } // namespace WebCore
425
426 #endif // ENABLE(ASYNC_SCROLLING)