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 37namespace WebCore { 38 39PassOwnPtr<ScrollingStateTree> ScrollingStateTree::create(AsyncScrollingCoordinator* scrollingCoordinator) 40{ 41 return adoptPtr(new ScrollingStateTree(scrollingCoordinator)); 42} 43 44ScrollingStateTree::ScrollingStateTree(AsyncScrollingCoordinator* scrollingCoordinator) 45 : m_scrollingCoordinator(scrollingCoordinator) 46 , m_hasChangedProperties(false) 47 , m_hasNewRootStateNode(false) 48 , m_preferredLayerRepresentation(LayerRepresentation::GraphicsLayerRepresentation) 49{ 50} 51 52ScrollingStateTree::~ScrollingStateTree() 53{ 54} 55 56void 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 70PassRefPtr<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 86ScrollingNodeID 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 136void 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 149void ScrollingStateTree::clear() 150{ 151 if (rootStateNode()) 152 removeNodeAndAllDescendants(rootStateNode()); 153 154 m_stateNodeMap.clear(); 155 m_orphanedSubframeNodes.clear(); 156} 157 158PassOwnPtr<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 OwnPtr<ScrollingStateTree> treeStateClone = ScrollingStateTree::create(); 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.release(); 186} 187 188void ScrollingStateTree::addNode(ScrollingStateNode* node) 189{ 190 m_stateNodeMap.add(node->scrollingNodeID(), node); 191} 192 193void 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 211void 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 227void ScrollingStateTree::willRemoveNode(ScrollingStateNode* node) 228{ 229 m_nodesRemovedSinceLastCommit.add(node->scrollingNodeID()); 230 m_stateNodeMap.remove(node->scrollingNodeID()); 231 setHasChangedProperties(); 232} 233 234void ScrollingStateTree::setRemovedNodes(HashSet<ScrollingNodeID> nodes) 235{ 236 m_nodesRemovedSinceLastCommit = WTF::move(nodes); 237} 238 239ScrollingStateNode* 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#endif // ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS) 255