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