1 /* 2 * Copyright (C) 2007 Apple Inc. All rights reserved. 3 * (C) 2008 Nikolas Zimmermann <zimmermann (at) kde.org> 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Library General Public 7 * License as published by the Free Software Foundation; either 8 * version 2 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Library General Public License for more details. 14 * 15 * You should have received a copy of the GNU Library General Public License 16 * along with this library; see the file COPYING.LIB. If not, write to 17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 * Boston, MA 02110-1301, USA. 19 * 20 */ 21 22 #ifndef ContainerNodeAlgorithms_h 23 #define ContainerNodeAlgorithms_h 24 25 #include "core/dom/Document.h" 26 #include "core/html/HTMLFrameOwnerElement.h" 27 #include "core/inspector/InspectorInstrumentation.h" 28 #include "wtf/Assertions.h" 29 30 namespace WebCore { 31 32 class ChildNodeInsertionNotifier { 33 public: 34 explicit ChildNodeInsertionNotifier(ContainerNode& insertionPoint) 35 : m_insertionPoint(insertionPoint) 36 { 37 } 38 39 void notify(Node&); 40 41 private: 42 void notifyDescendantInsertedIntoDocument(ContainerNode&); 43 void notifyDescendantInsertedIntoTree(ContainerNode&); 44 void notifyNodeInsertedIntoDocument(Node&); 45 void notifyNodeInsertedIntoTree(ContainerNode&); 46 47 ContainerNode& m_insertionPoint; 48 Vector< RefPtr<Node> > m_postInsertionNotificationTargets; 49 }; 50 51 class ChildNodeRemovalNotifier { 52 public: 53 explicit ChildNodeRemovalNotifier(ContainerNode& insertionPoint) 54 : m_insertionPoint(insertionPoint) 55 { 56 } 57 58 void notify(Node&); 59 60 private: 61 void notifyDescendantRemovedFromDocument(ContainerNode&); 62 void notifyDescendantRemovedFromTree(ContainerNode&); 63 void notifyNodeRemovedFromDocument(Node&); 64 void notifyNodeRemovedFromTree(ContainerNode&); 65 66 ContainerNode& m_insertionPoint; 67 }; 68 69 namespace Private { 70 71 template<class GenericNode, class GenericNodeContainer> 72 void addChildNodesToDeletionQueue(GenericNode*& head, GenericNode*& tail, GenericNodeContainer&); 73 74 } 75 76 // Helper functions for TreeShared-derived classes, which have a 'Node' style interface 77 // This applies to 'ContainerNode' and 'SVGElementInstance' 78 template<class GenericNode, class GenericNodeContainer> 79 inline void removeDetachedChildrenInContainer(GenericNodeContainer& container) 80 { 81 // List of nodes to be deleted. 82 GenericNode* head = 0; 83 GenericNode* tail = 0; 84 85 Private::addChildNodesToDeletionQueue<GenericNode, GenericNodeContainer>(head, tail, container); 86 87 GenericNode* n; 88 GenericNode* next; 89 while ((n = head) != 0) { 90 ASSERT_WITH_SECURITY_IMPLICATION(n->m_deletionHasBegun); 91 92 next = n->nextSibling(); 93 n->setNextSibling(0); 94 95 head = next; 96 if (next == 0) 97 tail = 0; 98 99 if (n->hasChildNodes()) 100 Private::addChildNodesToDeletionQueue<GenericNode, GenericNodeContainer>(head, tail, static_cast<GenericNodeContainer&>(*n)); 101 102 delete n; 103 } 104 } 105 106 template<class GenericNode, class GenericNodeContainer> 107 inline void appendChildToContainer(GenericNode& child, GenericNodeContainer& container) 108 { 109 child.setParentOrShadowHostNode(&container); 110 111 GenericNode* lastChild = container.lastChild(); 112 if (lastChild) { 113 child.setPreviousSibling(lastChild); 114 lastChild->setNextSibling(&child); 115 } else { 116 container.setFirstChild(&child); 117 } 118 119 container.setLastChild(&child); 120 } 121 122 // Helper methods for removeDetachedChildrenInContainer, hidden from WebCore namespace 123 namespace Private { 124 125 template<class GenericNode, class GenericNodeContainer, bool dispatchRemovalNotification> 126 struct NodeRemovalDispatcher { 127 static void dispatch(GenericNode&, GenericNodeContainer&) 128 { 129 // no-op, by default 130 } 131 }; 132 133 template<class GenericNode, class GenericNodeContainer> 134 struct NodeRemovalDispatcher<GenericNode, GenericNodeContainer, true> { 135 static void dispatch(GenericNode& node, GenericNodeContainer& container) 136 { 137 // Clean up any TreeScope to a removed tree. 138 if (Document* containerDocument = container.ownerDocument()) 139 containerDocument->adoptIfNeeded(node); 140 if (node.inDocument()) 141 ChildNodeRemovalNotifier(container).notify(node); 142 } 143 }; 144 145 template<class GenericNode> 146 struct ShouldDispatchRemovalNotification { 147 static const bool value = false; 148 }; 149 150 template<> 151 struct ShouldDispatchRemovalNotification<Node> { 152 static const bool value = true; 153 }; 154 155 template<class GenericNode, class GenericNodeContainer> 156 void addChildNodesToDeletionQueue(GenericNode*& head, GenericNode*& tail, GenericNodeContainer& container) 157 { 158 // We have to tell all children that their parent has died. 159 GenericNode* next = 0; 160 for (GenericNode* n = container.firstChild(); n; n = next) { 161 ASSERT_WITH_SECURITY_IMPLICATION(!n->m_deletionHasBegun); 162 163 next = n->nextSibling(); 164 n->setNextSibling(0); 165 n->setParentOrShadowHostNode(0); 166 container.setFirstChild(next); 167 if (next) 168 next->setPreviousSibling(0); 169 170 if (!n->refCount()) { 171 #if SECURITY_ASSERT_ENABLED 172 n->m_deletionHasBegun = true; 173 #endif 174 // Add the node to the list of nodes to be deleted. 175 // Reuse the nextSibling pointer for this purpose. 176 if (tail) 177 tail->setNextSibling(n); 178 else 179 head = n; 180 181 tail = n; 182 } else { 183 RefPtr<GenericNode> protect(n); // removedFromDocument may remove remove all references to this node. 184 NodeRemovalDispatcher<GenericNode, GenericNodeContainer, ShouldDispatchRemovalNotification<GenericNode>::value>::dispatch(*n, container); 185 } 186 } 187 188 container.setLastChild(0); 189 } 190 191 } // namespace Private 192 193 inline void ChildNodeInsertionNotifier::notifyNodeInsertedIntoDocument(Node& node) 194 { 195 ASSERT(m_insertionPoint.inDocument()); 196 RefPtr<Node> protect(node); 197 if (Node::InsertionShouldCallDidNotifySubtreeInsertions == node.insertedInto(&m_insertionPoint)) 198 m_postInsertionNotificationTargets.append(&node); 199 if (node.isContainerNode()) 200 notifyDescendantInsertedIntoDocument(toContainerNode(node)); 201 } 202 203 inline void ChildNodeInsertionNotifier::notifyNodeInsertedIntoTree(ContainerNode& node) 204 { 205 NoEventDispatchAssertion assertNoEventDispatch; 206 ASSERT(!m_insertionPoint.inDocument()); 207 208 if (Node::InsertionShouldCallDidNotifySubtreeInsertions == node.insertedInto(&m_insertionPoint)) 209 m_postInsertionNotificationTargets.append(&node); 210 notifyDescendantInsertedIntoTree(node); 211 } 212 213 inline void ChildNodeInsertionNotifier::notify(Node& node) 214 { 215 ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden()); 216 217 InspectorInstrumentation::didInsertDOMNode(&node); 218 219 RefPtr<Document> protectDocument(node.document()); 220 RefPtr<Node> protectNode(node); 221 222 if (m_insertionPoint.inDocument()) 223 notifyNodeInsertedIntoDocument(node); 224 else if (node.isContainerNode()) 225 notifyNodeInsertedIntoTree(toContainerNode(node)); 226 227 for (size_t i = 0; i < m_postInsertionNotificationTargets.size(); ++i) { 228 Node* targetNode = m_postInsertionNotificationTargets[i].get(); 229 if (targetNode->inDocument()) 230 targetNode->didNotifySubtreeInsertionsToDocument(); 231 } 232 } 233 234 inline void ChildNodeRemovalNotifier::notifyNodeRemovedFromDocument(Node& node) 235 { 236 ASSERT(m_insertionPoint.inDocument()); 237 node.removedFrom(&m_insertionPoint); 238 239 if (node.isContainerNode()) 240 notifyDescendantRemovedFromDocument(toContainerNode(node)); 241 } 242 243 inline void ChildNodeRemovalNotifier::notifyNodeRemovedFromTree(ContainerNode& node) 244 { 245 NoEventDispatchAssertion assertNoEventDispatch; 246 ASSERT(!m_insertionPoint.inDocument()); 247 248 node.removedFrom(&m_insertionPoint); 249 notifyDescendantRemovedFromTree(node); 250 } 251 252 inline void ChildNodeRemovalNotifier::notify(Node& node) 253 { 254 if (node.inDocument()) { 255 notifyNodeRemovedFromDocument(node); 256 node.document().notifyRemovePendingSheetIfNeeded(); 257 } else if (node.isContainerNode()) 258 notifyNodeRemovedFromTree(toContainerNode(node)); 259 } 260 261 class ChildFrameDisconnector { 262 public: 263 enum DisconnectPolicy { 264 RootAndDescendants, 265 DescendantsOnly 266 }; 267 268 explicit ChildFrameDisconnector(Node& root) 269 : m_root(root) 270 { 271 } 272 273 void disconnect(DisconnectPolicy = RootAndDescendants); 274 275 private: 276 void collectFrameOwners(Node& root); 277 void collectFrameOwners(ElementShadow&); 278 void disconnectCollectedFrameOwners(); 279 280 Vector<RefPtr<HTMLFrameOwnerElement>, 10> m_frameOwners; 281 Node& m_root; 282 }; 283 284 #ifndef NDEBUG 285 unsigned assertConnectedSubrameCountIsConsistent(Node&); 286 #endif 287 288 inline void ChildFrameDisconnector::collectFrameOwners(Node& root) 289 { 290 if (!root.connectedSubframeCount()) 291 return; 292 293 if (root.isHTMLElement() && root.isFrameOwnerElement()) 294 m_frameOwners.append(&toHTMLFrameOwnerElement(root)); 295 296 for (Node* child = root.firstChild(); child; child = child->nextSibling()) 297 collectFrameOwners(*child); 298 299 ElementShadow* shadow = root.isElementNode() ? toElement(root).shadow() : 0; 300 if (shadow) 301 collectFrameOwners(*shadow); 302 } 303 304 inline void ChildFrameDisconnector::disconnectCollectedFrameOwners() 305 { 306 // Must disable frame loading in the subtree so an unload handler cannot 307 // insert more frames and create loaded frames in detached subtrees. 308 SubframeLoadingDisabler disabler(m_root); 309 310 for (unsigned i = 0; i < m_frameOwners.size(); ++i) { 311 HTMLFrameOwnerElement* owner = m_frameOwners[i].get(); 312 // Don't need to traverse up the tree for the first owner since no 313 // script could have moved it. 314 if (!i || m_root.containsIncludingShadowDOM(owner)) 315 owner->disconnectContentFrame(); 316 } 317 } 318 319 inline void ChildFrameDisconnector::disconnect(DisconnectPolicy policy) 320 { 321 #ifndef NDEBUG 322 assertConnectedSubrameCountIsConsistent(m_root); 323 #endif 324 325 if (!m_root.connectedSubframeCount()) 326 return; 327 328 if (policy == RootAndDescendants) 329 collectFrameOwners(m_root); 330 else { 331 for (Node* child = m_root.firstChild(); child; child = child->nextSibling()) 332 collectFrameOwners(*child); 333 } 334 335 disconnectCollectedFrameOwners(); 336 } 337 338 } // namespace WebCore 339 340 #endif // ContainerNodeAlgorithms_h 341