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/dom/NodeTraversal.h" 27 #include "core/html/HTMLFrameOwnerElement.h" 28 #include "core/inspector/InspectorInstrumentation.h" 29 #include "wtf/Assertions.h" 30 31 namespace WebCore { 32 33 class ChildNodeInsertionNotifier { 34 public: 35 explicit ChildNodeInsertionNotifier(ContainerNode* insertionPoint) 36 : m_insertionPoint(insertionPoint) 37 { 38 } 39 40 void notify(Node*); 41 42 private: 43 void notifyDescendantInsertedIntoDocument(ContainerNode*); 44 void notifyDescendantInsertedIntoTree(ContainerNode*); 45 void notifyNodeInsertedIntoDocument(Node*); 46 void notifyNodeInsertedIntoTree(ContainerNode*); 47 48 ContainerNode* m_insertionPoint; 49 Vector< RefPtr<Node> > m_postInsertionNotificationTargets; 50 }; 51 52 class ChildNodeRemovalNotifier { 53 public: 54 explicit ChildNodeRemovalNotifier(ContainerNode* insertionPoint) 55 : m_insertionPoint(insertionPoint) 56 { 57 } 58 59 void notify(Node*); 60 61 private: 62 void notifyDescendantRemovedFromDocument(ContainerNode*); 63 void notifyDescendantRemovedFromTree(ContainerNode*); 64 void notifyNodeRemovedFromDocument(Node*); 65 void notifyNodeRemovedFromTree(ContainerNode*); 66 67 ContainerNode* m_insertionPoint; 68 }; 69 70 namespace Private { 71 72 template<class GenericNode, class GenericNodeContainer> 73 void addChildNodesToDeletionQueue(GenericNode*& head, GenericNode*& tail, GenericNodeContainer*); 74 75 } 76 77 // Helper functions for TreeShared-derived classes, which have a 'Node' style interface 78 // This applies to 'ContainerNode' and 'SVGElementInstance' 79 template<class GenericNode, class GenericNodeContainer> 80 inline void removeDetachedChildrenInContainer(GenericNodeContainer* container) 81 { 82 // List of nodes to be deleted. 83 GenericNode* head = 0; 84 GenericNode* tail = 0; 85 86 Private::addChildNodesToDeletionQueue<GenericNode, GenericNodeContainer>(head, tail, container); 87 88 GenericNode* n; 89 GenericNode* next; 90 while ((n = head) != 0) { 91 ASSERT(n->m_deletionHasBegun); 92 93 next = n->nextSibling(); 94 n->setNextSibling(0); 95 96 head = next; 97 if (next == 0) 98 tail = 0; 99 100 if (n->hasChildNodes()) 101 Private::addChildNodesToDeletionQueue<GenericNode, GenericNodeContainer>(head, tail, static_cast<GenericNodeContainer*>(n)); 102 103 delete n; 104 } 105 } 106 107 template<class GenericNode, class GenericNodeContainer> 108 inline void appendChildToContainer(GenericNode* child, GenericNodeContainer* container) 109 { 110 child->setParentOrShadowHostNode(container); 111 112 GenericNode* lastChild = container->lastChild(); 113 if (lastChild) { 114 child->setPreviousSibling(lastChild); 115 lastChild->setNextSibling(child); 116 } else 117 container->setFirstChild(child); 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 != 0; n = next) { 161 ASSERT(!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 #ifndef NDEBUG 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->document(), 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 m_postInsertionNotificationTargets[i]->didNotifySubtreeInsertions(m_insertionPoint); 229 } 230 231 232 inline void ChildNodeRemovalNotifier::notifyNodeRemovedFromDocument(Node* node) 233 { 234 ASSERT(m_insertionPoint->inDocument()); 235 node->removedFrom(m_insertionPoint); 236 237 if (node->isContainerNode()) 238 notifyDescendantRemovedFromDocument(toContainerNode(node)); 239 } 240 241 inline void ChildNodeRemovalNotifier::notifyNodeRemovedFromTree(ContainerNode* node) 242 { 243 NoEventDispatchAssertion assertNoEventDispatch; 244 ASSERT(!m_insertionPoint->inDocument()); 245 246 node->removedFrom(m_insertionPoint); 247 notifyDescendantRemovedFromTree(node); 248 } 249 250 inline void ChildNodeRemovalNotifier::notify(Node* node) 251 { 252 if (node->inDocument()) { 253 notifyNodeRemovedFromDocument(node); 254 node->document()->notifyRemovePendingSheetIfNeeded(); 255 } else if (node->isContainerNode()) 256 notifyNodeRemovedFromTree(toContainerNode(node)); 257 } 258 259 class ChildFrameDisconnector { 260 public: 261 enum DisconnectPolicy { 262 RootAndDescendants, 263 DescendantsOnly 264 }; 265 266 explicit ChildFrameDisconnector(Node* root) 267 : m_root(root) 268 { 269 } 270 271 void disconnect(DisconnectPolicy = RootAndDescendants); 272 273 private: 274 void collectFrameOwners(Node* root); 275 void collectFrameOwners(ElementShadow*); 276 void disconnectCollectedFrameOwners(); 277 278 Vector<RefPtr<HTMLFrameOwnerElement>, 10> m_frameOwners; 279 Node* m_root; 280 }; 281 282 #ifndef NDEBUG 283 unsigned assertConnectedSubrameCountIsConsistent(Node*); 284 #endif 285 286 inline void ChildFrameDisconnector::collectFrameOwners(Node* root) 287 { 288 if (!root->connectedSubframeCount()) 289 return; 290 291 if (root->isHTMLElement() && root->isFrameOwnerElement()) 292 m_frameOwners.append(toFrameOwnerElement(root)); 293 294 for (Node* child = root->firstChild(); child; child = child->nextSibling()) 295 collectFrameOwners(child); 296 297 ElementShadow* shadow = root->isElementNode() ? toElement(root)->shadow() : 0; 298 if (shadow) 299 collectFrameOwners(shadow); 300 } 301 302 inline void ChildFrameDisconnector::disconnectCollectedFrameOwners() 303 { 304 // Must disable frame loading in the subtree so an unload handler cannot 305 // insert more frames and create loaded frames in detached subtrees. 306 SubframeLoadingDisabler disabler(m_root); 307 308 for (unsigned i = 0; i < m_frameOwners.size(); ++i) { 309 HTMLFrameOwnerElement* owner = m_frameOwners[i].get(); 310 // Don't need to traverse up the tree for the first owner since no 311 // script could have moved it. 312 if (!i || m_root->containsIncludingShadowDOM(owner)) 313 owner->disconnectContentFrame(); 314 } 315 } 316 317 inline void ChildFrameDisconnector::disconnect(DisconnectPolicy policy) 318 { 319 #ifndef NDEBUG 320 assertConnectedSubrameCountIsConsistent(m_root); 321 #endif 322 323 if (!m_root->connectedSubframeCount()) 324 return; 325 326 if (policy == RootAndDescendants) 327 collectFrameOwners(m_root); 328 else { 329 for (Node* child = m_root->firstChild(); child; child = child->nextSibling()) 330 collectFrameOwners(child); 331 } 332 333 disconnectCollectedFrameOwners(); 334 } 335 336 } // namespace WebCore 337 338 #endif // ContainerNodeAlgorithms_h 339