Unreviewed, rolling out r218373.
[WebKit-https.git] / Source / WebCore / page / scrolling / ScrollingStateTree.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 "ScrollingStateTree.h"
28
29 #if ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS)
30
31 #include "AsyncScrollingCoordinator.h"
32 #include "ScrollingStateFixedNode.h"
33 #include "ScrollingStateFrameScrollingNode.h"
34 #include "ScrollingStateOverflowScrollingNode.h"
35 #include "ScrollingStateStickyNode.h"
36 #include <wtf/text/CString.h>
37
38 #ifndef NDEBUG
39 #include <stdio.h>
40 #endif
41
42 namespace WebCore {
43
44 ScrollingStateTree::ScrollingStateTree(AsyncScrollingCoordinator* scrollingCoordinator)
45     : m_scrollingCoordinator(scrollingCoordinator)
46     , m_hasChangedProperties(false)
47     , m_hasNewRootStateNode(false)
48     , m_preferredLayerRepresentation(LayerRepresentation::GraphicsLayerRepresentation)
49 {
50 }
51
52 ScrollingStateTree::~ScrollingStateTree()
53 {
54 }
55
56 void ScrollingStateTree::setHasChangedProperties(bool changedProperties)
57 {
58 #if ENABLE(ASYNC_SCROLLING)
59     bool gainedChangedProperties = !m_hasChangedProperties && changedProperties;
60 #endif
61
62     m_hasChangedProperties = changedProperties;
63
64 #if ENABLE(ASYNC_SCROLLING)
65     if (gainedChangedProperties && m_scrollingCoordinator)
66         m_scrollingCoordinator->scrollingStateTreePropertiesChanged();
67 #endif
68 }
69
70 Ref<ScrollingStateNode> ScrollingStateTree::createNode(ScrollingNodeType nodeType, ScrollingNodeID nodeID)
71 {
72     switch (nodeType) {
73     case FixedNode:
74         return ScrollingStateFixedNode::create(*this, nodeID);
75     case StickyNode:
76         return ScrollingStateStickyNode::create(*this, nodeID);
77     case FrameScrollingNode:
78         return ScrollingStateFrameScrollingNode::create(*this, nodeID);
79     case OverflowScrollingNode:
80         return ScrollingStateOverflowScrollingNode::create(*this, nodeID);
81     }
82     ASSERT_NOT_REACHED();
83     return ScrollingStateFixedNode::create(*this, nodeID);
84 }
85
86 bool ScrollingStateTree::nodeTypeAndParentMatch(ScrollingStateNode& node, ScrollingNodeType nodeType, ScrollingNodeID parentID) const
87 {
88     if (node.nodeType() != nodeType)
89         return false;
90
91     ScrollingStateNode* parent = stateNodeForID(parentID);
92     if (!parent)
93         return true;
94
95     return node.parent() == parent;
96 }
97
98 ScrollingNodeID ScrollingStateTree::attachNode(ScrollingNodeType nodeType, ScrollingNodeID newNodeID, ScrollingNodeID parentID)
99 {
100     ASSERT(newNodeID);
101
102     if (ScrollingStateNode* node = stateNodeForID(newNodeID)) {
103         if (nodeTypeAndParentMatch(*node, nodeType, parentID))
104             return newNodeID;
105
106 #if ENABLE(ASYNC_SCROLLING)
107         // If the type has changed, we need to destroy and recreate the node with a new ID.
108         if (nodeType != node->nodeType())
109             newNodeID = m_scrollingCoordinator->uniqueScrollLayerID();
110 #endif
111
112         // The node is being re-parented. To do that, we'll remove it, and then create a new node.
113         removeNodeAndAllDescendants(node, SubframeNodeRemoval::Orphan);
114     }
115
116     ScrollingStateNode* newNode = nullptr;
117     if (!parentID) {
118         // If we're resetting the root node, we should clear the HashMap and destroy the current children.
119         clear();
120
121         setRootStateNode(ScrollingStateFrameScrollingNode::create(*this, newNodeID));
122         newNode = rootStateNode();
123         m_hasNewRootStateNode = true;
124     } else {
125         ScrollingStateNode* parent = stateNodeForID(parentID);
126         if (!parent)
127             return 0;
128
129         if (nodeType == FrameScrollingNode && parentID) {
130             if (auto orphanedNode = m_orphanedSubframeNodes.take(newNodeID)) {
131                 newNode = orphanedNode.get();
132                 parent->appendChild(orphanedNode.releaseNonNull());
133             }
134         }
135
136         if (!newNode) {
137             auto stateNode = createNode(nodeType, newNodeID);
138             newNode = stateNode.ptr();
139             parent->appendChild(WTFMove(stateNode));
140         }
141     }
142
143     m_stateNodeMap.set(newNodeID, newNode);
144     m_nodesRemovedSinceLastCommit.remove(newNodeID);
145     return newNodeID;
146 }
147
148 void ScrollingStateTree::detachNode(ScrollingNodeID nodeID)
149 {
150     if (!nodeID)
151         return;
152
153     // The node may not be found if clearStateTree() was recently called.
154     ScrollingStateNode* node = m_stateNodeMap.take(nodeID);
155     if (!node)
156         return;
157
158     removeNodeAndAllDescendants(node, SubframeNodeRemoval::Orphan);
159 }
160
161 void ScrollingStateTree::clear()
162 {
163     if (rootStateNode())
164         removeNodeAndAllDescendants(rootStateNode());
165
166     m_stateNodeMap.clear();
167     m_orphanedSubframeNodes.clear();
168 }
169
170 std::unique_ptr<ScrollingStateTree> ScrollingStateTree::commit(LayerRepresentation::Type preferredLayerRepresentation)
171 {
172     if (!m_orphanedSubframeNodes.isEmpty()) {
173         // If we still have orphaned subtrees, remove them from m_stateNodeMap since they will be deleted 
174         // when clearing m_orphanedSubframeNodes.
175         for (auto& orphanNode : m_orphanedSubframeNodes.values())
176             recursiveNodeWillBeRemoved(orphanNode.get(), SubframeNodeRemoval::Delete);
177         m_orphanedSubframeNodes.clear();
178     }
179
180     // This function clones and resets the current state tree, but leaves the tree structure intact.
181     std::unique_ptr<ScrollingStateTree> treeStateClone = std::make_unique<ScrollingStateTree>();
182     treeStateClone->setPreferredLayerRepresentation(preferredLayerRepresentation);
183
184     if (m_rootStateNode)
185         treeStateClone->setRootStateNode(static_reference_cast<ScrollingStateFrameScrollingNode>(m_rootStateNode->cloneAndReset(*treeStateClone)));
186
187     // Copy the IDs of the nodes that have been removed since the last commit into the clone.
188     treeStateClone->m_nodesRemovedSinceLastCommit.swap(m_nodesRemovedSinceLastCommit);
189
190     // Now the clone tree has changed properties, and the original tree does not.
191     treeStateClone->m_hasChangedProperties = m_hasChangedProperties;
192     m_hasChangedProperties = false;
193
194     treeStateClone->m_hasNewRootStateNode = m_hasNewRootStateNode;
195     m_hasNewRootStateNode = false;
196
197     return treeStateClone;
198 }
199
200 void ScrollingStateTree::addNode(ScrollingStateNode* node)
201 {
202     m_stateNodeMap.add(node->scrollingNodeID(), node);
203 }
204
205 void ScrollingStateTree::removeNodeAndAllDescendants(ScrollingStateNode* node, SubframeNodeRemoval subframeNodeRemoval)
206 {
207     ScrollingStateNode* parent = node->parent();
208
209     recursiveNodeWillBeRemoved(node, subframeNodeRemoval);
210
211     if (node == m_rootStateNode)
212         m_rootStateNode = nullptr;
213     else if (parent) {
214         ASSERT(parent->children());
215         ASSERT(parent->children()->find(node) != notFound);
216         if (auto children = parent->children()) {
217             size_t index = children->find(node);
218             if (index != notFound)
219                 children->remove(index);
220         }
221     }
222 }
223
224 void ScrollingStateTree::recursiveNodeWillBeRemoved(ScrollingStateNode* currNode, SubframeNodeRemoval subframeNodeRemoval)
225 {
226     currNode->setParent(nullptr);
227     if (subframeNodeRemoval == SubframeNodeRemoval::Orphan && currNode != m_rootStateNode && currNode->isFrameScrollingNode()) {
228         m_orphanedSubframeNodes.add(currNode->scrollingNodeID(), currNode);
229         return;
230     }
231
232     willRemoveNode(currNode);
233
234     if (auto children = currNode->children()) {
235         for (auto& child : *children)
236             recursiveNodeWillBeRemoved(child.get(), subframeNodeRemoval);
237     }
238 }
239
240 void ScrollingStateTree::willRemoveNode(ScrollingStateNode* node)
241 {
242     m_nodesRemovedSinceLastCommit.add(node->scrollingNodeID());
243     m_stateNodeMap.remove(node->scrollingNodeID());
244     setHasChangedProperties();
245 }
246
247 void ScrollingStateTree::setRemovedNodes(HashSet<ScrollingNodeID> nodes)
248 {
249     m_nodesRemovedSinceLastCommit = WTFMove(nodes);
250 }
251
252 ScrollingStateNode* ScrollingStateTree::stateNodeForID(ScrollingNodeID scrollLayerID) const
253 {
254     if (!scrollLayerID)
255         return 0;
256
257     auto it = m_stateNodeMap.find(scrollLayerID);
258     if (it == m_stateNodeMap.end())
259         return 0;
260
261     ASSERT(it->value->scrollingNodeID() == scrollLayerID);
262     return it->value;
263 }
264
265 } // namespace WebCore
266
267 #ifndef NDEBUG
268 void showScrollingStateTree(const WebCore::ScrollingStateTree* tree)
269 {
270     if (!tree)
271         return;
272
273     auto rootNode = tree->rootStateNode();
274     if (!rootNode) {
275         fprintf(stderr, "Scrolling state tree %p with no root node\n", tree);
276         return;
277     }
278
279     String output = rootNode->scrollingStateTreeAsText(WebCore::ScrollingStateTreeAsTextBehaviorDebug);
280     fprintf(stderr, "%s\n", output.utf8().data());
281 }
282
283 void showScrollingStateTree(const WebCore::ScrollingStateNode* node)
284 {
285     if (!node)
286         return;
287
288     showScrollingStateTree(&node->scrollingStateTree());
289 }
290
291 #endif
292
293 #endif // ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS)