Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(382)

Side by Side Diff: sky/engine/core/dom/ContainerNode.cpp

Issue 732203004: Clean up child checks in ContainerNode. (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Add back secondary hierarchy checks. Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « sky/engine/core/dom/ContainerNode.h ('k') | sky/engine/core/dom/Document.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org) 3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2001 Dirk Mueller (mueller@kde.org) 4 * (C) 2001 Dirk Mueller (mueller@kde.org)
5 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2013 Apple Inc. All rights reserved. 5 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2013 Apple Inc. All rights reserved.
6 * 6 *
7 * This library is free software; you can redistribute it and/or 7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public 8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either 9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version. 10 * version 2 of the License, or (at your option) any later version.
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
66 removeDetachedChildrenInContainer(*this); 66 removeDetachedChildrenInContainer(*this);
67 } 67 }
68 68
69 ContainerNode::~ContainerNode() 69 ContainerNode::~ContainerNode()
70 { 70 {
71 ASSERT(needsAttach()); 71 ASSERT(needsAttach());
72 willBeDeletedFromDocument(); 72 willBeDeletedFromDocument();
73 removeDetachedChildren(); 73 removeDetachedChildren();
74 } 74 }
75 75
76 bool ContainerNode::isChildTypeAllowed(const Node& child) const
77 {
78 if (!child.isDocumentFragment())
79 return childTypeAllowed(child.nodeType());
80
81 for (Node* node = toDocumentFragment(child).firstChild(); node; node = node- >nextSibling()) {
82 if (!childTypeAllowed(node->nodeType()))
83 return false;
84 }
85 return true;
86 }
87
88 bool ContainerNode::containsConsideringHostElements(const Node& newChild) const 76 bool ContainerNode::containsConsideringHostElements(const Node& newChild) const
89 { 77 {
90 if (isInShadowTree() || document().isTemplateDocument()) 78 if (isInShadowTree() || document().isTemplateDocument())
91 return newChild.containsIncludingHostElements(*this); 79 return newChild.containsIncludingHostElements(*this);
92 return newChild.contains(this); 80 return newChild.contains(this);
93 } 81 }
94 82
95 bool ContainerNode::checkAcceptChild(const Node* newChild, const Node* oldChild, ExceptionState& exceptionState) const 83 void ContainerNode::checkAcceptChildType(const Node* newChild, ExceptionState& e xceptionState) const
96 { 84 {
97 // Not mentioned in spec: throw NotFoundError if newChild is null
98 if (!newChild) { 85 if (!newChild) {
99 exceptionState.throwDOMException(NotFoundError, "The new child element i s null."); 86 exceptionState.throwDOMException(NotFoundError, "The new child element i s null.");
100 return false; 87 return;
101 } 88 }
102 89
103 // Use common case fast path if possible. 90 if (newChild->isTreeScope()) {
104 if ((newChild->isElementNode() || newChild->isTextNode()) && isElementNode() ) { 91 exceptionState.throwDOMException(HierarchyRequestError, "Nodes of type ' " + newChild->nodeName() + "' may not be inserted inside nodes of type '" + node Name() + "'.");
105 ASSERT(isChildTypeAllowed(*newChild)); 92 return;
106 if (containsConsideringHostElements(*newChild)) { 93 }
107 exceptionState.throwDOMException(HierarchyRequestError, "The new chi ld element contains the parent."); 94 }
108 return false; 95
109 } 96 void ContainerNode::checkAcceptChildHierarchy(const Node& newChild, const Node* oldChild, ExceptionState& exceptionState) const
110 return true; 97 {
98 if (containsConsideringHostElements(newChild)) {
99 exceptionState.throwDOMException(HierarchyRequestError, "The new child e lement contains the parent.");
100 return;
111 } 101 }
112 102
113 if (containsConsideringHostElements(*newChild)) { 103 // TODO(esprehn): Remove this, sky should allow multiple top level elements.
114 exceptionState.throwDOMException(HierarchyRequestError, "The new child e lement contains the parent."); 104 if (isDocumentNode()) {
115 return false; 105 unsigned elementCount = 0;
106 if (newChild.isElementNode()) {
107 elementCount = 1;
108 } else if (newChild.isDocumentFragment()) {
109 for (Element* element = ElementTraversal::firstChild(newChild); elem ent; element = ElementTraversal::nextSibling(*element))
110 ++elementCount;
111 }
112 if (elementCount > 1 || ((!oldChild || !oldChild->isElementNode()) && el ementCount && document().documentElement())) {
113 exceptionState.throwDOMException(HierarchyRequestError, "Document ca n only contain one Element.");
114 return;
115 }
116 } 116 }
117
118 if (oldChild && isDocumentNode()) {
119 if (!toDocument(this)->canReplaceChild(*newChild, *oldChild)) {
120 // FIXME: Adjust 'Document::canReplaceChild' to return some addition al detail (or an error message).
121 exceptionState.throwDOMException(HierarchyRequestError, "Failed to r eplace child.");
122 return false;
123 }
124 } else if (!isChildTypeAllowed(*newChild)) {
125 exceptionState.throwDOMException(HierarchyRequestError, "Nodes of type ' " + newChild->nodeName() + "' may not be inserted inside nodes of type '" + node Name() + "'.");
126 return false;
127 }
128
129 return true;
130 }
131
132 bool ContainerNode::checkAcceptChildGuaranteedNodeTypes(const Node& newChild, Ex ceptionState& exceptionState) const
133 {
134 ASSERT(isChildTypeAllowed(newChild));
135 if (newChild.contains(this)) {
136 exceptionState.throwDOMException(HierarchyRequestError, "The new child e lement contains the parent.");
137 return false;
138 }
139 return true;
140 } 117 }
141 118
142 PassRefPtr<Node> ContainerNode::insertBefore(PassRefPtr<Node> newChild, Node* re fChild, ExceptionState& exceptionState) 119 PassRefPtr<Node> ContainerNode::insertBefore(PassRefPtr<Node> newChild, Node* re fChild, ExceptionState& exceptionState)
143 { 120 {
144 // Check that this node is not "floating". 121 // Check that this node is not "floating".
145 // If it is, it can be deleted as a side effect of sending mutation events. 122 // If it is, it can be deleted as a side effect of sending mutation events.
146 ASSERT(refCount() || parentOrShadowHostNode()); 123 ASSERT(refCount() || parentOrShadowHostNode());
147 124
148 RefPtr<Node> protect(this); 125 RefPtr<Node> protect(this);
149 126
150 // insertBefore(node, 0) is equivalent to appendChild(node) 127 // insertBefore(node, 0) is equivalent to appendChild(node)
151 if (!refChild) { 128 if (!refChild) {
152 return appendChild(newChild, exceptionState); 129 return appendChild(newChild, exceptionState);
153 } 130 }
154 131
155 // Make sure adding the new child is OK. 132 checkAcceptChildType(newChild.get(), exceptionState);
156 if (!checkAcceptChild(newChild.get(), 0, exceptionState)) { 133 if (exceptionState.hadException())
157 if (exceptionState.hadException()) 134 return nullptr;
158 return nullptr; 135
159 return newChild; 136 checkAcceptChildHierarchy(*newChild, 0, exceptionState);
160 } 137 if (exceptionState.hadException())
138 return nullptr;
139
161 ASSERT(newChild); 140 ASSERT(newChild);
162 141
163 // NotFoundError: Raised if refChild is not a child of this node 142 // NotFoundError: Raised if refChild is not a child of this node
164 if (refChild->parentNode() != this) { 143 if (refChild->parentNode() != this) {
165 exceptionState.throwDOMException(NotFoundError, "The node before which t he new node is to be inserted is not a child of this node."); 144 exceptionState.throwDOMException(NotFoundError, "The node before which t he new node is to be inserted is not a child of this node.");
166 return nullptr; 145 return nullptr;
167 } 146 }
168 147
169 // nothing to do 148 // nothing to do
170 if (refChild->previousSibling() == newChild || refChild == newChild) 149 if (refChild->previousSibling() == newChild || refChild == newChild)
171 return newChild; 150 return newChild;
172 151
173 RefPtr<Node> next = refChild; 152 RefPtr<Node> next = refChild;
174 153
175 NodeVector targets; 154 NodeVector targets;
176 collectChildrenAndRemoveFromOldParent(*newChild, targets, exceptionState); 155 collectChildrenAndRemoveFromOldParent(*newChild, targets, exceptionState);
177 if (exceptionState.hadException()) 156 if (exceptionState.hadException())
178 return nullptr; 157 return nullptr;
179 if (targets.isEmpty()) 158 if (targets.isEmpty())
180 return newChild; 159 return newChild;
181 160
182 // We need this extra check because collectChildrenAndRemoveFromOldParent() can fire mutation events. 161 // Must check this again beacuse focus events might run synchronously when
183 if (!checkAcceptChildGuaranteedNodeTypes(*newChild, exceptionState)) { 162 // removing children.
184 if (exceptionState.hadException()) 163 checkAcceptChildHierarchy(*newChild, 0, exceptionState);
185 return nullptr; 164 if (exceptionState.hadException())
186 return newChild; 165 return nullptr;
187 }
188 166
189 ChildListMutationScope mutation(*this); 167 ChildListMutationScope mutation(*this);
190 for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); + +it) { 168 for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); + +it) {
191 ASSERT(*it); 169 ASSERT(*it);
192 Node& child = **it; 170 Node& child = **it;
193 171
194 // Due to arbitrary code running in response to a DOM mutation event it' s 172 // Due to arbitrary code running in response to a DOM mutation event it' s
195 // possible that "next" is no longer a child of "this". 173 // possible that "next" is no longer a child of "this".
196 // It's also possible that "child" has been inserted elsewhere. 174 // It's also possible that "child" has been inserted elsewhere.
197 // In either of those cases, we'll just stop. 175 // In either of those cases, we'll just stop.
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
261 if (oldChild == newChild) // nothing to do 239 if (oldChild == newChild) // nothing to do
262 return oldChild; 240 return oldChild;
263 241
264 if (!oldChild) { 242 if (!oldChild) {
265 exceptionState.throwDOMException(NotFoundError, "The node to be replaced is null."); 243 exceptionState.throwDOMException(NotFoundError, "The node to be replaced is null.");
266 return nullptr; 244 return nullptr;
267 } 245 }
268 246
269 RefPtr<Node> child = oldChild; 247 RefPtr<Node> child = oldChild;
270 248
271 // Make sure replacing the old child with the new is ok 249 checkAcceptChildType(newChild.get(), exceptionState);
272 if (!checkAcceptChild(newChild.get(), child.get(), exceptionState)) { 250 if (exceptionState.hadException())
273 if (exceptionState.hadException()) 251 return nullptr;
274 return nullptr; 252
275 return child; 253 checkAcceptChildHierarchy(*newChild, child.get(), exceptionState);
276 } 254 if (exceptionState.hadException())
255 return nullptr;
277 256
278 // NotFoundError: Raised if oldChild is not a child of this node. 257 // NotFoundError: Raised if oldChild is not a child of this node.
279 if (child->parentNode() != this) { 258 if (child->parentNode() != this) {
280 exceptionState.throwDOMException(NotFoundError, "The node to be replaced is not a child of this node."); 259 exceptionState.throwDOMException(NotFoundError, "The node to be replaced is not a child of this node.");
281 return nullptr; 260 return nullptr;
282 } 261 }
283 262
284 ChildListMutationScope mutation(*this); 263 ChildListMutationScope mutation(*this);
285 264
286 RefPtr<Node> next = child->nextSibling(); 265 RefPtr<Node> next = child->nextSibling();
287 266
288 // Remove the node we're replacing 267 // Remove the node we're replacing
289 removeChild(child, exceptionState); 268 removeChild(child, exceptionState);
290 if (exceptionState.hadException()) 269 if (exceptionState.hadException())
291 return nullptr; 270 return nullptr;
292 271
293 if (next && (next->previousSibling() == newChild || next == newChild)) // no thing to do 272 if (next && (next->previousSibling() == newChild || next == newChild)) // no thing to do
294 return child; 273 return child;
295 274
296 // Does this one more time because removeChild() fires a MutationEvent.
297 if (!checkAcceptChild(newChild.get(), child.get(), exceptionState)) {
298 if (exceptionState.hadException())
299 return nullptr;
300 return child;
301 }
302
303 NodeVector targets; 275 NodeVector targets;
304 collectChildrenAndRemoveFromOldParent(*newChild, targets, exceptionState); 276 collectChildrenAndRemoveFromOldParent(*newChild, targets, exceptionState);
305 if (exceptionState.hadException()) 277 if (exceptionState.hadException())
306 return nullptr; 278 return nullptr;
307 279
308 // Does this yet another check because collectChildrenAndRemoveFromOldParent () fires a MutationEvent. 280 // Must check this again beacuse focus events might run synchronously when
309 if (!checkAcceptChild(newChild.get(), child.get(), exceptionState)) { 281 // removing children.
310 if (exceptionState.hadException()) 282 checkAcceptChildHierarchy(*newChild, child.get(),exceptionState);
311 return nullptr; 283 if (exceptionState.hadException())
312 return child; 284 return nullptr;
313 }
314 285
315 // Add the new child(ren) 286 // Add the new child(ren)
316 for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); + +it) { 287 for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); + +it) {
317 ASSERT(*it); 288 ASSERT(*it);
318 Node& child = **it; 289 Node& child = **it;
319 290
320 // Due to arbitrary code running in response to a DOM mutation event it' s 291 // Due to arbitrary code running in response to a DOM mutation event it' s
321 // possible that "next" is no longer a child of "this". 292 // possible that "next" is no longer a child of "this".
322 // It's also possible that "child" has been inserted elsewhere. 293 // It's also possible that "child" has been inserted elsewhere.
323 // In either of those cases, we'll just stop. 294 // In either of those cases, we'll just stop.
(...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after
538 } 509 }
539 510
540 PassRefPtr<Node> ContainerNode::appendChild(PassRefPtr<Node> newChild, Exception State& exceptionState) 511 PassRefPtr<Node> ContainerNode::appendChild(PassRefPtr<Node> newChild, Exception State& exceptionState)
541 { 512 {
542 RefPtr<ContainerNode> protect(this); 513 RefPtr<ContainerNode> protect(this);
543 514
544 // Check that this node is not "floating". 515 // Check that this node is not "floating".
545 // If it is, it can be deleted as a side effect of sending mutation events. 516 // If it is, it can be deleted as a side effect of sending mutation events.
546 ASSERT(refCount() || parentOrShadowHostNode()); 517 ASSERT(refCount() || parentOrShadowHostNode());
547 518
548 // Make sure adding the new child is ok 519 checkAcceptChildType(newChild.get(), exceptionState);
549 if (!checkAcceptChild(newChild.get(), 0, exceptionState)) { 520 if (exceptionState.hadException())
550 if (exceptionState.hadException()) 521 return nullptr;
551 return nullptr; 522
552 return newChild; 523 checkAcceptChildHierarchy(*newChild, 0, exceptionState);
553 } 524 if (exceptionState.hadException())
525 return nullptr;
526
554 ASSERT(newChild); 527 ASSERT(newChild);
555 528
556 if (newChild == m_lastChild) // nothing to do 529 if (newChild == m_lastChild) // nothing to do
557 return newChild; 530 return newChild;
558 531
559 NodeVector targets; 532 NodeVector targets;
560 collectChildrenAndRemoveFromOldParent(*newChild, targets, exceptionState); 533 collectChildrenAndRemoveFromOldParent(*newChild, targets, exceptionState);
561 if (exceptionState.hadException()) 534 if (exceptionState.hadException())
562 return nullptr; 535 return nullptr;
563 536
564 if (targets.isEmpty()) 537 if (targets.isEmpty())
565 return newChild; 538 return newChild;
566 539
567 // We need this extra check because collectChildrenAndRemoveFromOldParent() can fire mutation events. 540 // Must check this again beacuse focus events might run synchronously when
568 if (!checkAcceptChildGuaranteedNodeTypes(*newChild, exceptionState)) { 541 // removing children.
569 if (exceptionState.hadException()) 542 checkAcceptChildHierarchy(*newChild, 0, exceptionState);
570 return nullptr; 543 if (exceptionState.hadException())
571 return newChild; 544 return nullptr;
572 }
573 545
574 // Now actually add the child(ren) 546 // Now actually add the child(ren)
575 ChildListMutationScope mutation(*this); 547 ChildListMutationScope mutation(*this);
576 for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); + +it) { 548 for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); + +it) {
577 ASSERT(*it); 549 ASSERT(*it);
578 Node& child = **it; 550 Node& child = **it;
579 551
580 // If the child has a parent again, just stop what we're doing, because 552 // If the child has a parent again, just stop what we're doing, because
581 // that means someone is doing something with DOM mutation -- can't re-p arent 553 // that means someone is doing something with DOM mutation -- can't re-p arent
582 // a child that already has a parent. 554 // a child that already has a parent.
(...skipping 398 matching lines...) Expand 10 before | Expand all | Expand 10 after
981 return true; 953 return true;
982 954
983 if (node->isElementNode() && toElement(node)->shadow()) 955 if (node->isElementNode() && toElement(node)->shadow())
984 return true; 956 return true;
985 957
986 return false; 958 return false;
987 } 959 }
988 #endif 960 #endif
989 961
990 } // namespace blink 962 } // namespace blink
OLDNEW
« no previous file with comments | « sky/engine/core/dom/ContainerNode.h ('k') | sky/engine/core/dom/Document.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698