[iOS WK2] REGRESSION (r242687): Programmatic scroll of overflow scroll results in...
[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 "ScrollingTreeOverflowScrollingNode.h"
39 #include "ScrollingTreeScrollingNode.h"
40 #include <wtf/SetForScope.h>
41 #include <wtf/text/TextStream.h>
42
43 namespace WebCore {
44
45 ScrollingTree::ScrollingTree() = default;
46
47 ScrollingTree::~ScrollingTree() = default;
48
49 bool ScrollingTree::shouldHandleWheelEventSynchronously(const PlatformWheelEvent& wheelEvent)
50 {
51     // This method is invoked by the event handling thread
52     LockHolder lock(m_treeStateMutex);
53
54     bool shouldSetLatch = wheelEvent.shouldConsiderLatching();
55     
56     if (hasLatchedNode() && !shouldSetLatch)
57         return false;
58
59     if (shouldSetLatch)
60         m_treeState.latchedNodeID = 0;
61     
62     if (!m_treeState.eventTrackingRegions.isEmpty() && m_rootNode) {
63         auto& frameScrollingNode = downcast<ScrollingTreeFrameScrollingNode>(*m_rootNode);
64         FloatPoint position = wheelEvent.position();
65         position.move(frameScrollingNode.viewToContentsOffset(m_treeState.mainFrameScrollPosition));
66
67         const EventNames& names = eventNames();
68         IntPoint roundedPosition = roundedIntPoint(position);
69
70         // Event regions are affected by page scale, so no need to map through scale.
71         bool isSynchronousDispatchRegion = m_treeState.eventTrackingRegions.trackingTypeForPoint(names.wheelEvent, roundedPosition) == TrackingType::Synchronous
72             || m_treeState.eventTrackingRegions.trackingTypeForPoint(names.mousewheelEvent, roundedPosition) == TrackingType::Synchronous;
73         LOG_WITH_STREAM(Scrolling, stream << "ScrollingTree::shouldHandleWheelEventSynchronously: wheelEvent at " << wheelEvent.position() << " mapped to content point " << position << ", in non-fast region " << isSynchronousDispatchRegion);
74
75         if (isSynchronousDispatchRegion)
76             return true;
77     }
78     return false;
79 }
80
81 void ScrollingTree::setOrClearLatchedNode(const PlatformWheelEvent& wheelEvent, ScrollingNodeID nodeID)
82 {
83     if (wheelEvent.shouldConsiderLatching()) {
84         LOG_WITH_STREAM(Scrolling, stream << "ScrollingTree " << this << " setOrClearLatchedNode: setting latched node " << nodeID);
85         setLatchedNode(nodeID);
86     } else if (wheelEvent.shouldResetLatching()) {
87         LOG_WITH_STREAM(Scrolling, stream << "ScrollingTree " << this << " setOrClearLatchedNode: clearing latched node (was " << latchedNode() << ")");
88         clearLatchedNode();
89     }
90 }
91
92 ScrollingEventResult ScrollingTree::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
93 {
94     LOG_WITH_STREAM(Scrolling, stream << "ScrollingTree " << this << " handleWheelEvent (async scrolling enabled: " << asyncFrameOrOverflowScrollingEnabled() << ")");
95
96     LockHolder locker(m_treeMutex);
97
98     if (!asyncFrameOrOverflowScrollingEnabled()) {
99         if (m_rootNode)
100             downcast<ScrollingTreeScrollingNode>(*m_rootNode).handleWheelEvent(wheelEvent);
101         return ScrollingEventResult::DidNotHandleEvent;
102     }
103
104     if (hasLatchedNode()) {
105         LOG_WITH_STREAM(Scrolling, stream << " has latched node " << latchedNode());
106         auto* node = nodeForID(latchedNode());
107         if (is<ScrollingTreeScrollingNode>(node))
108             return downcast<ScrollingTreeScrollingNode>(*node).handleWheelEvent(wheelEvent);
109     }
110
111     if (m_rootNode) {
112         auto& frameScrollingNode = downcast<ScrollingTreeFrameScrollingNode>(*m_rootNode);
113
114         FloatPoint position = wheelEvent.position();
115         ScrollingTreeNode* node = frameScrollingNode.scrollingNodeForPoint(LayoutPoint(position));
116
117         LOG_WITH_STREAM(Scrolling, stream << "ScrollingTree::handleWheelEvent found node " << (node ? node->scrollingNodeID() : 0) << " for point " << position << "\n");
118
119         while (node) {
120             if (is<ScrollingTreeScrollingNode>(*node)) {
121                 auto& scrollingNode = downcast<ScrollingTreeScrollingNode>(*node);
122                 // FIXME: this needs to consult latching logic.
123                 if (scrollingNode.handleWheelEvent(wheelEvent) == ScrollingEventResult::DidHandleEvent)
124                     return ScrollingEventResult::DidHandleEvent;
125             }
126             node = node->parent();
127         }
128     }
129     return ScrollingEventResult::DidNotHandleEvent;
130 }
131
132 void ScrollingTree::mainFrameViewportChangedViaDelegatedScrolling(const FloatPoint& scrollPosition, const FloatRect& layoutViewport, double)
133 {
134     LOG_WITH_STREAM(Scrolling, stream << "ScrollingTree::viewportChangedViaDelegatedScrolling - layoutViewport " << layoutViewport);
135     
136     if (!m_rootNode)
137         return;
138
139     auto& frameScrollingNode = downcast<ScrollingTreeFrameScrollingNode>(*m_rootNode);
140     frameScrollingNode.wasScrolledByDelegatedScrolling(scrollPosition, layoutViewport);
141 }
142
143 void ScrollingTree::commitTreeState(std::unique_ptr<ScrollingStateTree> scrollingStateTree)
144 {
145     LockHolder locker(m_treeMutex);
146
147     bool rootStateNodeChanged = scrollingStateTree->hasNewRootStateNode();
148     
149     LOG(Scrolling, "\nScrollingTree %p commitTreeState", this);
150     
151     auto* rootNode = scrollingStateTree->rootStateNode();
152     if (rootNode
153         && (rootStateNodeChanged
154             || rootNode->hasChangedProperty(ScrollingStateFrameScrollingNode::EventTrackingRegion)
155             || rootNode->hasChangedProperty(ScrollingStateScrollingNode::ScrolledContentsLayer)
156             || rootNode->hasChangedProperty(ScrollingStateFrameScrollingNode::AsyncFrameOrOverflowScrollingEnabled))) {
157         LockHolder lock(m_treeStateMutex);
158
159         if (rootStateNodeChanged || rootNode->hasChangedProperty(ScrollingStateScrollingNode::ScrolledContentsLayer))
160             m_treeState.mainFrameScrollPosition = { };
161
162         if (rootStateNodeChanged || rootNode->hasChangedProperty(ScrollingStateFrameScrollingNode::EventTrackingRegion))
163             m_treeState.eventTrackingRegions = scrollingStateTree->rootStateNode()->eventTrackingRegions();
164
165         if (rootStateNodeChanged || rootNode->hasChangedProperty(ScrollingStateFrameScrollingNode::AsyncFrameOrOverflowScrollingEnabled))
166             m_asyncFrameOrOverflowScrollingEnabled = scrollingStateTree->rootStateNode()->asyncFrameOrOverflowScrollingEnabled();
167     }
168     
169     // unvisitedNodes starts with all nodes in the map; we remove nodes as we visit them. At the end, it's the unvisited nodes.
170     // We can't use orphanNodes for this, because orphanNodes won't contain descendants of removed nodes.
171     HashSet<ScrollingNodeID> unvisitedNodes;
172     for (auto nodeID : m_nodeMap.keys())
173         unvisitedNodes.add(nodeID);
174
175     m_overflowRelatedNodesMap.clear();
176     m_positionedNodesWithRelatedOverflow.clear();
177
178     // orphanNodes keeps child nodes alive while we rebuild child lists.
179     OrphanScrollingNodeMap orphanNodes;
180     updateTreeFromStateNode(rootNode, orphanNodes, unvisitedNodes);
181     
182     for (auto nodeID : unvisitedNodes) {
183         if (nodeID == m_treeState.latchedNodeID)
184             clearLatchedNode();
185         
186         LOG(Scrolling, "ScrollingTree::commitTreeState - removing unvisited node %" PRIu64, nodeID);
187         m_nodeMap.remove(nodeID);
188     }
189
190     LOG(Scrolling, "committed ScrollingTree\n%s", scrollingTreeAsText(ScrollingStateTreeAsTextBehaviorDebug).utf8().data());
191 }
192
193 void ScrollingTree::updateTreeFromStateNode(const ScrollingStateNode* stateNode, OrphanScrollingNodeMap& orphanNodes, HashSet<ScrollingNodeID>& unvisitedNodes)
194 {
195     if (!stateNode) {
196         m_nodeMap.clear();
197         m_rootNode = nullptr;
198         return;
199     }
200     
201     ScrollingNodeID nodeID = stateNode->scrollingNodeID();
202     ScrollingNodeID parentNodeID = stateNode->parentNodeID();
203
204     auto it = m_nodeMap.find(nodeID);
205
206     RefPtr<ScrollingTreeNode> node;
207     if (it != m_nodeMap.end()) {
208         node = it->value;
209         unvisitedNodes.remove(nodeID);
210     } else {
211         node = createScrollingTreeNode(stateNode->nodeType(), nodeID);
212         if (!parentNodeID) {
213             // This is the root node. Clear the node map.
214             ASSERT(stateNode->isFrameScrollingNode());
215             m_rootNode = node;
216             m_nodeMap.clear();
217         } 
218         m_nodeMap.set(nodeID, node.get());
219     }
220
221     if (parentNodeID) {
222         auto parentIt = m_nodeMap.find(parentNodeID);
223         ASSERT_WITH_SECURITY_IMPLICATION(parentIt != m_nodeMap.end());
224         if (parentIt != m_nodeMap.end()) {
225             auto* parent = parentIt->value;
226
227             auto* oldParent = node->parent();
228             if (oldParent)
229                 oldParent->removeChild(*node);
230
231             if (oldParent != parent)
232                 node->setParent(parent);
233
234             parent->appendChild(*node);
235         } else {
236             // FIXME: Use WeakPtr in m_nodeMap.
237             m_nodeMap.remove(nodeID);
238         }
239     }
240
241     node->commitStateBeforeChildren(*stateNode);
242     
243     // Move all children into the orphanNodes map. Live ones will get added back as we recurse over children.
244     if (auto nodeChildren = node->children()) {
245         for (auto& childScrollingNode : *nodeChildren) {
246             childScrollingNode->setParent(nullptr);
247             orphanNodes.add(childScrollingNode->scrollingNodeID(), childScrollingNode.get());
248         }
249         nodeChildren->clear();
250     }
251
252     // Now update the children if we have any.
253     if (auto children = stateNode->children()) {
254         for (auto& child : *children)
255             updateTreeFromStateNode(child.get(), orphanNodes, unvisitedNodes);
256     }
257
258     node->commitStateAfterChildren(*stateNode);
259 }
260
261 void ScrollingTree::applyLayerPositions()
262 {
263     ASSERT(isMainThread());
264     LockHolder locker(m_treeMutex);
265
266     if (!m_rootNode)
267         return;
268
269     LOG(Scrolling, "\nScrollingTree %p applyLayerPositions", this);
270
271     applyLayerPositionsRecursive(*m_rootNode, { }, { });
272
273     LOG(Scrolling, "ScrollingTree %p applyLayerPositions - done\n", this);
274 }
275
276 void ScrollingTree::applyLayerPositionsRecursive(ScrollingTreeNode& currNode, FloatRect layoutViewport, FloatSize cumulativeDelta)
277 {
278     if (is<ScrollingTreeFrameScrollingNode>(currNode)) {
279         layoutViewport = downcast<ScrollingTreeFrameScrollingNode>(currNode).layoutViewport();
280         cumulativeDelta = { };
281     }
282
283     currNode.applyLayerPositions(layoutViewport, cumulativeDelta);
284
285     if (auto children = currNode.children()) {
286         for (auto& child : *children)
287             applyLayerPositionsRecursive(*child, layoutViewport, cumulativeDelta);
288     }
289 }
290
291 ScrollingTreeNode* ScrollingTree::nodeForID(ScrollingNodeID nodeID) const
292 {
293     if (!nodeID)
294         return nullptr;
295
296     return m_nodeMap.get(nodeID);
297 }
298
299 void ScrollingTree::notifyRelatedNodesAfterScrollPositionChange(ScrollingTreeScrollingNode& changedNode)
300 {
301     Vector<ScrollingNodeID> additionalUpdateRoots;
302     
303     FloatSize deltaFromLastCommittedScrollPosition;
304     FloatRect currentFrameLayoutViewport;
305     if (is<ScrollingTreeFrameScrollingNode>(changedNode))
306         currentFrameLayoutViewport = downcast<ScrollingTreeFrameScrollingNode>(changedNode).layoutViewport();
307     else if (is<ScrollingTreeOverflowScrollingNode>(changedNode)) {
308         deltaFromLastCommittedScrollPosition = changedNode.lastCommittedScrollPosition() - changedNode.currentScrollPosition();
309
310         if (auto* frameScrollingNode = changedNode.enclosingFrameNodeIncludingSelf())
311             currentFrameLayoutViewport = frameScrollingNode->layoutViewport();
312         
313         additionalUpdateRoots = overflowRelatedNodes().get(changedNode.scrollingNodeID());
314     }
315
316     notifyRelatedNodesRecursive(changedNode, changedNode, currentFrameLayoutViewport, deltaFromLastCommittedScrollPosition);
317     
318     for (auto positionedNodeID : additionalUpdateRoots) {
319         auto* positionedNode = nodeForID(positionedNodeID);
320         if (positionedNode)
321             notifyRelatedNodesRecursive(changedNode, *positionedNode, currentFrameLayoutViewport, deltaFromLastCommittedScrollPosition);
322     }
323 }
324
325 void ScrollingTree::notifyRelatedNodesRecursive(ScrollingTreeScrollingNode& changedNode, ScrollingTreeNode& currNode, const FloatRect& layoutViewport, FloatSize cumulativeDelta)
326 {
327     currNode.relatedNodeScrollPositionDidChange(changedNode, layoutViewport, cumulativeDelta);
328
329     if (!currNode.children())
330         return;
331     
332     for (auto& child : *currNode.children()) {
333         // Never need to cross frame boundaries, since scroll layer adjustments are isolated to each document.
334         if (is<ScrollingTreeFrameScrollingNode>(child))
335             continue;
336
337         notifyRelatedNodesRecursive(changedNode, *child, layoutViewport, cumulativeDelta);
338     }
339 }
340
341 void ScrollingTree::setAsyncFrameOrOverflowScrollingEnabled(bool enabled)
342 {
343     m_asyncFrameOrOverflowScrollingEnabled = enabled;
344 }
345
346 void ScrollingTree::setMainFrameScrollPosition(FloatPoint position)
347 {
348     LockHolder lock(m_treeStateMutex);
349     m_treeState.mainFrameScrollPosition = position;
350 }
351
352 TrackingType ScrollingTree::eventTrackingTypeForPoint(const AtomicString& eventName, IntPoint p)
353 {
354     LockHolder lock(m_treeStateMutex);
355     return m_treeState.eventTrackingRegions.trackingTypeForPoint(eventName, p);
356 }
357
358 // Can be called from the main thread.
359 bool ScrollingTree::isRubberBandInProgress()
360 {
361     LockHolder lock(m_treeStateMutex);
362     return m_treeState.mainFrameIsRubberBanding;
363 }
364
365 void ScrollingTree::setMainFrameIsRubberBanding(bool isRubberBanding)
366 {
367     LockHolder locker(m_treeStateMutex);
368     m_treeState.mainFrameIsRubberBanding = isRubberBanding;
369 }
370
371 // Can be called from the main thread.
372 bool ScrollingTree::isScrollSnapInProgress()
373 {
374     LockHolder lock(m_treeStateMutex);
375     return m_treeState.mainFrameIsScrollSnapping;
376 }
377     
378 void ScrollingTree::setMainFrameIsScrollSnapping(bool isScrollSnapping)
379 {
380     LockHolder locker(m_treeStateMutex);
381     m_treeState.mainFrameIsScrollSnapping = isScrollSnapping;
382 }
383
384 void ScrollingTree::setMainFramePinState(bool pinnedToTheLeft, bool pinnedToTheRight, bool pinnedToTheTop, bool pinnedToTheBottom)
385 {
386     LockHolder locker(m_swipeStateMutex);
387
388     m_swipeState.mainFramePinnedToTheLeft = pinnedToTheLeft;
389     m_swipeState.mainFramePinnedToTheRight = pinnedToTheRight;
390     m_swipeState.mainFramePinnedToTheTop = pinnedToTheTop;
391     m_swipeState.mainFramePinnedToTheBottom = pinnedToTheBottom;
392 }
393
394 void ScrollingTree::setCanRubberBandState(bool canRubberBandAtLeft, bool canRubberBandAtRight, bool canRubberBandAtTop, bool canRubberBandAtBottom)
395 {
396     LockHolder locker(m_swipeStateMutex);
397
398     m_swipeState.rubberBandsAtLeft = canRubberBandAtLeft;
399     m_swipeState.rubberBandsAtRight = canRubberBandAtRight;
400     m_swipeState.rubberBandsAtTop = canRubberBandAtTop;
401     m_swipeState.rubberBandsAtBottom = canRubberBandAtBottom;
402 }
403
404 // Can be called from the main thread.
405 void ScrollingTree::setScrollPinningBehavior(ScrollPinningBehavior pinning)
406 {
407     LockHolder locker(m_swipeStateMutex);
408     
409     m_swipeState.scrollPinningBehavior = pinning;
410 }
411
412 ScrollPinningBehavior ScrollingTree::scrollPinningBehavior()
413 {
414     LockHolder lock(m_swipeStateMutex);
415     
416     return m_swipeState.scrollPinningBehavior;
417 }
418
419 bool ScrollingTree::willWheelEventStartSwipeGesture(const PlatformWheelEvent& wheelEvent)
420 {
421     if (wheelEvent.phase() != PlatformWheelEventPhaseBegan)
422         return false;
423
424     LockHolder lock(m_swipeStateMutex);
425
426     if (wheelEvent.deltaX() > 0 && m_swipeState.mainFramePinnedToTheLeft && !m_swipeState.rubberBandsAtLeft)
427         return true;
428     if (wheelEvent.deltaX() < 0 && m_swipeState.mainFramePinnedToTheRight && !m_swipeState.rubberBandsAtRight)
429         return true;
430     if (wheelEvent.deltaY() > 0 && m_swipeState.mainFramePinnedToTheTop && !m_swipeState.rubberBandsAtTop)
431         return true;
432     if (wheelEvent.deltaY() < 0 && m_swipeState.mainFramePinnedToTheBottom && !m_swipeState.rubberBandsAtBottom)
433         return true;
434
435     return false;
436 }
437
438 void ScrollingTree::setScrollingPerformanceLoggingEnabled(bool flag)
439 {
440     m_scrollingPerformanceLoggingEnabled = flag;
441 }
442
443 bool ScrollingTree::scrollingPerformanceLoggingEnabled()
444 {
445     return m_scrollingPerformanceLoggingEnabled;
446 }
447
448 ScrollingNodeID ScrollingTree::latchedNode()
449 {
450     LockHolder locker(m_treeStateMutex);
451     return m_treeState.latchedNodeID;
452 }
453
454 void ScrollingTree::setLatchedNode(ScrollingNodeID node)
455 {
456     LockHolder locker(m_treeStateMutex);
457     m_treeState.latchedNodeID = node;
458 }
459
460 void ScrollingTree::clearLatchedNode()
461 {
462     LockHolder locker(m_treeStateMutex);
463     m_treeState.latchedNodeID = 0;
464 }
465
466 String ScrollingTree::scrollingTreeAsText(ScrollingStateTreeAsTextBehavior behavior)
467 {
468     TextStream ts(TextStream::LineMode::MultipleLine);
469
470     {
471         TextStream::GroupScope scope(ts);
472         ts << "scrolling tree";
473
474         LockHolder locker(m_treeStateMutex);
475
476         if (m_treeState.latchedNodeID)
477             ts.dumpProperty("latched node", m_treeState.latchedNodeID);
478
479         if (!m_treeState.mainFrameScrollPosition.isZero())
480             ts.dumpProperty("main frame scroll position", m_treeState.mainFrameScrollPosition);
481         
482         if (m_rootNode) {
483             TextStream::GroupScope scope(ts);
484             m_rootNode->dump(ts, behavior | ScrollingStateTreeAsTextBehaviorIncludeLayerPositions);
485         }
486         
487         if (behavior & ScrollingStateTreeAsTextBehaviorIncludeNodeIDs && !m_overflowRelatedNodesMap.isEmpty()) {
488             TextStream::GroupScope scope(ts);
489             ts << "overflow related nodes";
490             {
491                 TextStream::IndentScope indentScope(ts);
492                 for (auto& it : m_overflowRelatedNodesMap)
493                     ts << "\n" << indent << it.key << " -> " << it.value;
494             }
495         }
496     }
497     return ts.release();
498 }
499
500 #if ENABLE(POINTER_EVENTS)
501 Optional<TouchActionData> ScrollingTree::touchActionDataAtPoint(IntPoint p) const
502 {
503     // FIXME: This does not handle the case where there are multiple regions matching this point.
504     for (auto& touchActionData : m_treeState.eventTrackingRegions.touchActionData) {
505         if (touchActionData.region.contains(p))
506             return touchActionData;
507     }
508
509     return { };
510 }
511 #endif
512
513 } // namespace WebCore
514
515 #endif // ENABLE(ASYNC_SCROLLING)