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(THREADED_SCROLLING) || USE(COORDINATED_GRAPHICS) 30 31#include "ScrollingStateFixedNode.h" 32#include "ScrollingStateScrollingNode.h" 33#include "ScrollingStateStickyNode.h" 34 35namespace WebCore { 36 37PassOwnPtr<ScrollingStateTree> ScrollingStateTree::create() 38{ 39 return adoptPtr(new ScrollingStateTree); 40} 41 42ScrollingStateTree::ScrollingStateTree() 43 : m_hasChangedProperties(false) 44 , m_hasNewRootStateNode(false) 45{ 46} 47 48ScrollingStateTree::~ScrollingStateTree() 49{ 50} 51 52ScrollingNodeID ScrollingStateTree::attachNode(ScrollingNodeType nodeType, ScrollingNodeID newNodeID, ScrollingNodeID parentID) 53{ 54 ASSERT(newNodeID); 55 56 if (ScrollingStateNode* node = stateNodeForID(newNodeID)) { 57 ScrollingStateNode* parent = stateNodeForID(parentID); 58 if (!parent) 59 return newNodeID; 60 if (node->parent() == parent) 61 return newNodeID; 62 63 // The node is being re-parented. To do that, we'll remove it, and then re-create a new node. 64 removeNode(node); 65 } 66 67 ScrollingStateNode* newNode = 0; 68 if (!parentID) { 69 // If we're resetting the root node, we should clear the HashMap and destroy the current children. 70 clear(); 71 72 setRootStateNode(ScrollingStateScrollingNode::create(this, newNodeID)); 73 newNode = rootStateNode(); 74 m_hasNewRootStateNode = true; 75 } else { 76 ScrollingStateNode* parent = stateNodeForID(parentID); 77 if (!parent) 78 return 0; 79 80 switch (nodeType) { 81 case FixedNode: { 82 OwnPtr<ScrollingStateFixedNode> fixedNode = ScrollingStateFixedNode::create(this, newNodeID); 83 newNode = fixedNode.get(); 84 parent->appendChild(fixedNode.release()); 85 break; 86 } 87 case StickyNode: { 88 OwnPtr<ScrollingStateStickyNode> stickyNode = ScrollingStateStickyNode::create(this, newNodeID); 89 newNode = stickyNode.get(); 90 parent->appendChild(stickyNode.release()); 91 break; 92 } 93 case ScrollingNode: { 94 // FIXME: We currently only support child nodes that are fixed. 95 ASSERT_NOT_REACHED(); 96 OwnPtr<ScrollingStateScrollingNode> scrollingNode = ScrollingStateScrollingNode::create(this, newNodeID); 97 newNode = scrollingNode.get(); 98 parent->appendChild(scrollingNode.release()); 99 break; 100 } 101 } 102 } 103 104 m_stateNodeMap.set(newNodeID, newNode); 105 return newNodeID; 106} 107 108void ScrollingStateTree::detachNode(ScrollingNodeID nodeID) 109{ 110 if (!nodeID) 111 return; 112 113 // The node may not be found if clearStateTree() was recently called. 114 ScrollingStateNode* node = m_stateNodeMap.take(nodeID); 115 if (!node) 116 return; 117 118 removeNode(node); 119} 120 121void ScrollingStateTree::clear() 122{ 123 removeNode(rootStateNode()); 124 m_stateNodeMap.clear(); 125} 126 127PassOwnPtr<ScrollingStateTree> ScrollingStateTree::commit() 128{ 129 // This function clones and resets the current state tree, but leaves the tree structure intact. 130 OwnPtr<ScrollingStateTree> treeStateClone = ScrollingStateTree::create(); 131 if (m_rootStateNode) 132 treeStateClone->setRootStateNode(static_pointer_cast<ScrollingStateScrollingNode>(m_rootStateNode->cloneAndReset())); 133 134 // Copy the IDs of the nodes that have been removed since the last commit into the clone. 135 treeStateClone->m_nodesRemovedSinceLastCommit.swap(m_nodesRemovedSinceLastCommit); 136 137 // Now the clone tree has changed properties, and the original tree does not. 138 treeStateClone->m_hasChangedProperties = true; 139 m_hasChangedProperties = false; 140 141 treeStateClone->m_hasNewRootStateNode = m_hasNewRootStateNode; 142 m_hasNewRootStateNode = false; 143 144 return treeStateClone.release(); 145} 146 147void ScrollingStateTree::removeNode(ScrollingStateNode* node) 148{ 149 if (!node) 150 return; 151 152 if (node == m_rootStateNode) { 153 didRemoveNode(node->scrollingNodeID()); 154 m_rootStateNode = nullptr; 155 return; 156 } 157 158 ASSERT(m_rootStateNode); 159 m_rootStateNode->removeChild(node); 160 161 // ScrollingStateTree::removeNode() will destroy children, so we have to make sure we remove those children 162 // from the HashMap. 163 size_t size = m_nodesRemovedSinceLastCommit.size(); 164 for (size_t i = 0; i < size; ++i) 165 m_stateNodeMap.remove(m_nodesRemovedSinceLastCommit[i]); 166} 167 168void ScrollingStateTree::didRemoveNode(ScrollingNodeID nodeID) 169{ 170 m_nodesRemovedSinceLastCommit.append(nodeID); 171 m_hasChangedProperties = true; 172} 173 174ScrollingStateNode* ScrollingStateTree::stateNodeForID(ScrollingNodeID scrollLayerID) 175{ 176 if (!scrollLayerID) 177 return 0; 178 179 HashMap<ScrollingNodeID, ScrollingStateNode*>::const_iterator it = m_stateNodeMap.find(scrollLayerID); 180 if (it == m_stateNodeMap.end()) 181 return 0; 182 183 ASSERT(it->value->scrollingNodeID() == scrollLayerID); 184 return it->value; 185} 186 187} // namespace WebCore 188 189#endif // ENABLE(THREADED_SCROLLING) || USE(COORDINATED_GRAPHICS) 190