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