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

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: dry 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::checkAcceptChild(const Node* newChild, const Node* oldChild, ExceptionState& exceptionState) const
96 { 84 {
97 // Not mentioned in spec: throw NotFoundError if newChild is null 85 // Not mentioned in spec: throw NotFoundError if newChild is null
98 if (!newChild) { 86 if (!newChild) {
99 exceptionState.throwDOMException(NotFoundError, "The new child element i s null."); 87 exceptionState.throwDOMException(NotFoundError, "The new child element i s null.");
100 return false; 88 return;
101 } 89 }
102 90
103 // Use common case fast path if possible. 91 if (newChild->isTreeScope()) {
104 if ((newChild->isElementNode() || newChild->isTextNode()) && isElementNode() ) { 92 exceptionState.throwDOMException(HierarchyRequestError, "Nodes of type ' " + newChild->nodeName() + "' may not be inserted inside nodes of type '" + node Name() + "'.");
105 ASSERT(isChildTypeAllowed(*newChild)); 93 return;
106 if (containsConsideringHostElements(*newChild)) {
107 exceptionState.throwDOMException(HierarchyRequestError, "The new chi ld element contains the parent.");
108 return false;
109 }
110 return true;
111 } 94 }
112 95
113 if (containsConsideringHostElements(*newChild)) { 96 if (containsConsideringHostElements(*newChild)) {
114 exceptionState.throwDOMException(HierarchyRequestError, "The new child e lement contains the parent."); 97 exceptionState.throwDOMException(HierarchyRequestError, "The new child e lement contains the parent.");
115 return false; 98 return;
116 } 99 }
117 100
118 if (oldChild && isDocumentNode()) { 101 // TODO(esprehn): Remove this, sky should allow multiple top level elements.
119 if (!toDocument(this)->canReplaceChild(*newChild, *oldChild)) { 102 if (isDocumentNode()) {
120 // FIXME: Adjust 'Document::canReplaceChild' to return some addition al detail (or an error message). 103 unsigned elementCount = 0;
121 exceptionState.throwDOMException(HierarchyRequestError, "Failed to r eplace child."); 104 if (newChild->isElementNode()) {
122 return false; 105 elementCount = 1;
106 } else if (newChild->isDocumentFragment()) {
107 for (Element* element = ElementTraversal::firstChild(*newChild); ele ment; element = ElementTraversal::nextSibling(*element))
108 ++elementCount;
123 } 109 }
124 } else if (!isChildTypeAllowed(*newChild)) { 110 if (elementCount > 1 || ((!oldChild || !oldChild->isElementNode()) && el ementCount && document().documentElement())) {
125 exceptionState.throwDOMException(HierarchyRequestError, "Nodes of type ' " + newChild->nodeName() + "' may not be inserted inside nodes of type '" + node Name() + "'."); 111 exceptionState.throwDOMException(HierarchyRequestError, "Document ca n only contain one Element.");
126 return false; 112 return;
113 }
127 } 114 }
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 } 115 }
141 116
142 PassRefPtr<Node> ContainerNode::insertBefore(PassRefPtr<Node> newChild, Node* re fChild, ExceptionState& exceptionState) 117 PassRefPtr<Node> ContainerNode::insertBefore(PassRefPtr<Node> newChild, Node* re fChild, ExceptionState& exceptionState)
143 { 118 {
144 // Check that this node is not "floating". 119 // Check that this node is not "floating".
145 // If it is, it can be deleted as a side effect of sending mutation events. 120 // If it is, it can be deleted as a side effect of sending mutation events.
146 ASSERT(refCount() || parentOrShadowHostNode()); 121 ASSERT(refCount() || parentOrShadowHostNode());
147 122
148 RefPtr<Node> protect(this); 123 RefPtr<Node> protect(this);
149 124
150 // insertBefore(node, 0) is equivalent to appendChild(node) 125 // insertBefore(node, 0) is equivalent to appendChild(node)
151 if (!refChild) { 126 if (!refChild) {
152 return appendChild(newChild, exceptionState); 127 return appendChild(newChild, exceptionState);
153 } 128 }
154 129
155 // Make sure adding the new child is OK. 130 checkAcceptChild(newChild.get(), 0, exceptionState);
156 if (!checkAcceptChild(newChild.get(), 0, exceptionState)) { 131 if (exceptionState.hadException())
157 if (exceptionState.hadException()) 132 return nullptr;
158 return nullptr; 133
159 return newChild;
160 }
161 ASSERT(newChild); 134 ASSERT(newChild);
162 135
163 // NotFoundError: Raised if refChild is not a child of this node 136 // NotFoundError: Raised if refChild is not a child of this node
164 if (refChild->parentNode() != this) { 137 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."); 138 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; 139 return nullptr;
167 } 140 }
168 141
169 // nothing to do 142 // nothing to do
170 if (refChild->previousSibling() == newChild || refChild == newChild) 143 if (refChild->previousSibling() == newChild || refChild == newChild)
171 return newChild; 144 return newChild;
172 145
173 RefPtr<Node> next = refChild; 146 RefPtr<Node> next = refChild;
174 147
175 NodeVector targets; 148 NodeVector targets;
176 collectChildrenAndRemoveFromOldParent(*newChild, targets, exceptionState); 149 collectChildrenAndRemoveFromOldParent(*newChild, targets, exceptionState);
177 if (exceptionState.hadException()) 150 if (exceptionState.hadException())
178 return nullptr; 151 return nullptr;
179 if (targets.isEmpty()) 152 if (targets.isEmpty())
180 return newChild; 153 return newChild;
181 154
182 // We need this extra check because collectChildrenAndRemoveFromOldParent() can fire mutation events.
ojan 2014/11/18 02:46:57 I think we still need this because of blur events,
esprehn 2014/11/18 03:01:40 Ah yeah, damn. Maybe we should make them async?
183 if (!checkAcceptChildGuaranteedNodeTypes(*newChild, exceptionState)) {
184 if (exceptionState.hadException())
185 return nullptr;
186 return newChild;
187 }
188
189 ChildListMutationScope mutation(*this); 155 ChildListMutationScope mutation(*this);
190 for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); + +it) { 156 for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); + +it) {
191 ASSERT(*it); 157 ASSERT(*it);
192 Node& child = **it; 158 Node& child = **it;
193 159
194 // Due to arbitrary code running in response to a DOM mutation event it' s 160 // 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". 161 // possible that "next" is no longer a child of "this".
196 // It's also possible that "child" has been inserted elsewhere. 162 // It's also possible that "child" has been inserted elsewhere.
197 // In either of those cases, we'll just stop. 163 // In either of those cases, we'll just stop.
198 if (next->parentNode() != this) 164 if (next->parentNode() != this)
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
261 if (oldChild == newChild) // nothing to do 227 if (oldChild == newChild) // nothing to do
262 return oldChild; 228 return oldChild;
263 229
264 if (!oldChild) { 230 if (!oldChild) {
265 exceptionState.throwDOMException(NotFoundError, "The node to be replaced is null."); 231 exceptionState.throwDOMException(NotFoundError, "The node to be replaced is null.");
266 return nullptr; 232 return nullptr;
267 } 233 }
268 234
269 RefPtr<Node> child = oldChild; 235 RefPtr<Node> child = oldChild;
270 236
271 // Make sure replacing the old child with the new is ok 237 checkAcceptChild(newChild.get(), child.get(), exceptionState);
272 if (!checkAcceptChild(newChild.get(), child.get(), exceptionState)) { 238 if (exceptionState.hadException())
273 if (exceptionState.hadException()) 239 return nullptr;
274 return nullptr;
275 return child;
276 }
277 240
278 // NotFoundError: Raised if oldChild is not a child of this node. 241 // NotFoundError: Raised if oldChild is not a child of this node.
279 if (child->parentNode() != this) { 242 if (child->parentNode() != this) {
280 exceptionState.throwDOMException(NotFoundError, "The node to be replaced is not a child of this node."); 243 exceptionState.throwDOMException(NotFoundError, "The node to be replaced is not a child of this node.");
281 return nullptr; 244 return nullptr;
282 } 245 }
283 246
284 ChildListMutationScope mutation(*this); 247 ChildListMutationScope mutation(*this);
285 248
286 RefPtr<Node> next = child->nextSibling(); 249 RefPtr<Node> next = child->nextSibling();
287 250
288 // Remove the node we're replacing 251 // Remove the node we're replacing
289 removeChild(child, exceptionState); 252 removeChild(child, exceptionState);
290 if (exceptionState.hadException()) 253 if (exceptionState.hadException())
291 return nullptr; 254 return nullptr;
292 255
293 if (next && (next->previousSibling() == newChild || next == newChild)) // no thing to do 256 if (next && (next->previousSibling() == newChild || next == newChild)) // no thing to do
294 return child; 257 return child;
295 258
296 // Does this one more time because removeChild() fires a MutationEvent.
ojan 2014/11/18 02:46:57 Ditto
297 if (!checkAcceptChild(newChild.get(), child.get(), exceptionState)) {
298 if (exceptionState.hadException())
299 return nullptr;
300 return child;
301 }
302
303 NodeVector targets; 259 NodeVector targets;
304 collectChildrenAndRemoveFromOldParent(*newChild, targets, exceptionState); 260 collectChildrenAndRemoveFromOldParent(*newChild, targets, exceptionState);
305 if (exceptionState.hadException()) 261 if (exceptionState.hadException())
306 return nullptr; 262 return nullptr;
307 263
308 // Does this yet another check because collectChildrenAndRemoveFromOldParent () fires a MutationEvent.
ojan 2014/11/18 02:46:56 Ditto
309 if (!checkAcceptChild(newChild.get(), child.get(), exceptionState)) {
310 if (exceptionState.hadException())
311 return nullptr;
312 return child;
313 }
314
315 // Add the new child(ren) 264 // Add the new child(ren)
316 for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); + +it) { 265 for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); + +it) {
317 ASSERT(*it); 266 ASSERT(*it);
318 Node& child = **it; 267 Node& child = **it;
319 268
320 // Due to arbitrary code running in response to a DOM mutation event it' s 269 // 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". 270 // possible that "next" is no longer a child of "this".
322 // It's also possible that "child" has been inserted elsewhere. 271 // It's also possible that "child" has been inserted elsewhere.
323 // In either of those cases, we'll just stop. 272 // In either of those cases, we'll just stop.
324 if (next && next->parentNode() != this) 273 if (next && next->parentNode() != this)
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after
538 } 487 }
539 488
540 PassRefPtr<Node> ContainerNode::appendChild(PassRefPtr<Node> newChild, Exception State& exceptionState) 489 PassRefPtr<Node> ContainerNode::appendChild(PassRefPtr<Node> newChild, Exception State& exceptionState)
541 { 490 {
542 RefPtr<ContainerNode> protect(this); 491 RefPtr<ContainerNode> protect(this);
543 492
544 // Check that this node is not "floating". 493 // Check that this node is not "floating".
545 // If it is, it can be deleted as a side effect of sending mutation events. 494 // If it is, it can be deleted as a side effect of sending mutation events.
546 ASSERT(refCount() || parentOrShadowHostNode()); 495 ASSERT(refCount() || parentOrShadowHostNode());
547 496
548 // Make sure adding the new child is ok 497 checkAcceptChild(newChild.get(), 0, exceptionState);
549 if (!checkAcceptChild(newChild.get(), 0, exceptionState)) { 498 if (exceptionState.hadException())
550 if (exceptionState.hadException()) 499 return nullptr;
551 return nullptr; 500
552 return newChild;
553 }
554 ASSERT(newChild); 501 ASSERT(newChild);
555 502
556 if (newChild == m_lastChild) // nothing to do 503 if (newChild == m_lastChild) // nothing to do
557 return newChild; 504 return newChild;
558 505
559 NodeVector targets; 506 NodeVector targets;
560 collectChildrenAndRemoveFromOldParent(*newChild, targets, exceptionState); 507 collectChildrenAndRemoveFromOldParent(*newChild, targets, exceptionState);
561 if (exceptionState.hadException()) 508 if (exceptionState.hadException())
562 return nullptr; 509 return nullptr;
563 510
564 if (targets.isEmpty()) 511 if (targets.isEmpty())
565 return newChild; 512 return newChild;
566 513
567 // We need this extra check because collectChildrenAndRemoveFromOldParent() can fire mutation events.
ojan 2014/11/18 02:46:56 ditto
568 if (!checkAcceptChildGuaranteedNodeTypes(*newChild, exceptionState)) {
569 if (exceptionState.hadException())
570 return nullptr;
571 return newChild;
572 }
573
574 // Now actually add the child(ren) 514 // Now actually add the child(ren)
575 ChildListMutationScope mutation(*this); 515 ChildListMutationScope mutation(*this);
576 for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); + +it) { 516 for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); + +it) {
577 ASSERT(*it); 517 ASSERT(*it);
578 Node& child = **it; 518 Node& child = **it;
579 519
580 // If the child has a parent again, just stop what we're doing, because 520 // 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 521 // that means someone is doing something with DOM mutation -- can't re-p arent
582 // a child that already has a parent. 522 // a child that already has a parent.
583 if (child.parentNode()) 523 if (child.parentNode())
(...skipping 397 matching lines...) Expand 10 before | Expand all | Expand 10 after
981 return true; 921 return true;
982 922
983 if (node->isElementNode() && toElement(node)->shadow()) 923 if (node->isElementNode() && toElement(node)->shadow())
984 return true; 924 return true;
985 925
986 return false; 926 return false;
987 } 927 }
988 #endif 928 #endif
989 929
990 } // namespace blink 930 } // 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