1/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2001 Dirk Mueller (mueller@kde.org)
5 * Copyright (C) 2004-2018 Apple Inc. All rights reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#include "config.h"
24#include "ContainerNode.h"
25
26#include "AXObjectCache.h"
27#include "AllDescendantsCollection.h"
28#include "ChildListMutationScope.h"
29#include "ClassCollection.h"
30#include "CommonVM.h"
31#include "ContainerNodeAlgorithms.h"
32#include "Editor.h"
33#include "EventNames.h"
34#include "FloatRect.h"
35#include "FrameView.h"
36#include "GenericCachedHTMLCollection.h"
37#include "HTMLFormControlsCollection.h"
38#include "HTMLOptionsCollection.h"
39#include "HTMLSlotElement.h"
40#include "HTMLTableRowsCollection.h"
41#include "InlineTextBox.h"
42#include "InspectorInstrumentation.h"
43#include "JSNode.h"
44#include "LabelsNodeList.h"
45#include "MutationEvent.h"
46#include "NameNodeList.h"
47#include "NodeRareData.h"
48#include "NodeRenderStyle.h"
49#include "RadioNodeList.h"
50#include "RenderBox.h"
51#include "RenderTheme.h"
52#include "RenderTreeUpdater.h"
53#include "RenderWidget.h"
54#include "RootInlineBox.h"
55#include "RuntimeEnabledFeatures.h"
56#include "SVGDocumentExtensions.h"
57#include "SVGElement.h"
58#include "SVGNames.h"
59#include "SVGUseElement.h"
60#include "ScriptDisallowedScope.h"
61#include "SelectorQuery.h"
62#include "SlotAssignment.h"
63#include "TemplateContentDocumentFragment.h"
64#include <algorithm>
65#include <wtf/IsoMallocInlines.h>
66#include <wtf/Variant.h>
67
68namespace WebCore {
69
70WTF_MAKE_ISO_ALLOCATED_IMPL(ContainerNode);
71
72static void dispatchChildInsertionEvents(Node&);
73static void dispatchChildRemovalEvents(Ref<Node>&);
74
75ChildNodesLazySnapshot* ChildNodesLazySnapshot::latestSnapshot;
76
77unsigned ScriptDisallowedScope::s_count = 0;
78#if !ASSERT_DISABLED
79ScriptDisallowedScope::EventAllowedScope* ScriptDisallowedScope::EventAllowedScope::s_currentScope = nullptr;
80#endif
81
82ALWAYS_INLINE NodeVector ContainerNode::removeAllChildrenWithScriptAssertion(ChildChangeSource source, DeferChildrenChanged deferChildrenChanged)
83{
84 auto children = collectChildNodes(*this);
85
86 if (source == ContainerNode::ChildChangeSource::API) {
87 ChildListMutationScope mutation(*this);
88 for (auto& child : children) {
89 mutation.willRemoveChild(child.get());
90 child->notifyMutationObserversNodeWillDetach();
91 dispatchChildRemovalEvents(child);
92 }
93 } else {
94 ASSERT(source == ContainerNode::ChildChangeSource::Parser);
95 ScriptDisallowedScope::InMainThread scriptDisallowedScope;
96 if (UNLIKELY(document().hasMutationObserversOfType(MutationObserver::ChildList))) {
97 ChildListMutationScope mutation(*this);
98 for (auto& child : children)
99 mutation.willRemoveChild(child.get());
100 }
101 }
102
103 disconnectSubframesIfNeeded(*this, DescendantsOnly);
104
105 WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
106 ScriptDisallowedScope::InMainThread scriptDisallowedScope;
107
108 if (UNLIKELY(isShadowRoot() || isInShadowTree()))
109 containingShadowRoot()->willRemoveAllChildren(*this);
110
111 document().nodeChildrenWillBeRemoved(*this);
112
113 while (RefPtr<Node> child = m_firstChild) {
114 removeBetween(nullptr, child->nextSibling(), *child);
115 notifyChildNodeRemoved(*this, *child);
116 }
117
118 if (deferChildrenChanged == DeferChildrenChanged::No)
119 childrenChanged(ContainerNode::ChildChange { ContainerNode::AllChildrenRemoved, nullptr, nullptr, source });
120
121 return children;
122}
123
124ALWAYS_INLINE bool ContainerNode::removeNodeWithScriptAssertion(Node& childToRemove, ChildChangeSource source)
125{
126 Ref<Node> protectedChildToRemove(childToRemove);
127 ASSERT_WITH_SECURITY_IMPLICATION(childToRemove.parentNode() == this);
128 {
129 ScriptDisallowedScope::InMainThread scriptDisallowedScope;
130 ChildListMutationScope(*this).willRemoveChild(childToRemove);
131 }
132
133 ASSERT_WITH_SECURITY_IMPLICATION(ScriptDisallowedScope::InMainThread::isEventDispatchAllowedInSubtree(childToRemove));
134 if (source == ContainerNode::ChildChangeSource::API) {
135 childToRemove.notifyMutationObserversNodeWillDetach();
136 dispatchChildRemovalEvents(protectedChildToRemove);
137 if (childToRemove.parentNode() != this)
138 return false;
139 }
140
141 if (source == ContainerNode::ChildChangeSource::Parser) {
142 // FIXME: Merge these two code paths. It's a bug in the parser not to update connectedSubframeCount in time.
143 disconnectSubframesIfNeeded(*this, DescendantsOnly);
144 } else {
145 if (is<ContainerNode>(childToRemove))
146 disconnectSubframesIfNeeded(downcast<ContainerNode>(childToRemove), RootAndDescendants);
147 }
148
149 if (childToRemove.parentNode() != this)
150 return false;
151
152 ChildChange change;
153 {
154 WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
155 ScriptDisallowedScope::InMainThread scriptDisallowedScope;
156
157 if (UNLIKELY(isShadowRoot() || isInShadowTree()))
158 containingShadowRoot()->resolveSlotsBeforeNodeInsertionOrRemoval();
159
160 document().nodeWillBeRemoved(childToRemove);
161
162 ASSERT_WITH_SECURITY_IMPLICATION(childToRemove.parentNode() == this);
163 ASSERT(!childToRemove.isDocumentFragment());
164
165 RefPtr<Node> previousSibling = childToRemove.previousSibling();
166 RefPtr<Node> nextSibling = childToRemove.nextSibling();
167 removeBetween(previousSibling.get(), nextSibling.get(), childToRemove);
168 notifyChildNodeRemoved(*this, childToRemove);
169
170 change.type = is<Element>(childToRemove) ? ElementRemoved : (is<Text>(childToRemove) ? TextRemoved : NonContentsChildRemoved);
171 change.previousSiblingElement = (!previousSibling || is<Element>(*previousSibling)) ? downcast<Element>(previousSibling.get()) : ElementTraversal::previousSibling(*previousSibling);
172 change.nextSiblingElement = (!nextSibling || is<Element>(*nextSibling)) ? downcast<Element>(nextSibling.get()) : ElementTraversal::nextSibling(*nextSibling);
173 change.source = source;
174 }
175
176 // FIXME: Move childrenChanged into ScriptDisallowedScope block.
177 childrenChanged(change);
178
179 return true;
180}
181
182enum class ReplacedAllChildren { No, Yes };
183
184template<typename DOMInsertionWork>
185static ALWAYS_INLINE void executeNodeInsertionWithScriptAssertion(ContainerNode& containerNode, Node& child,
186 ContainerNode::ChildChangeSource source, ReplacedAllChildren replacedAllChildren, DOMInsertionWork doNodeInsertion)
187{
188 NodeVector postInsertionNotificationTargets;
189 {
190 ScriptDisallowedScope::InMainThread scriptDisallowedScope;
191
192 if (UNLIKELY(containerNode.isShadowRoot() || containerNode.isInShadowTree()))
193 containerNode.containingShadowRoot()->resolveSlotsBeforeNodeInsertionOrRemoval();
194
195 doNodeInsertion();
196 ChildListMutationScope(containerNode).childAdded(child);
197 postInsertionNotificationTargets = notifyChildNodeInserted(containerNode, child);
198 }
199
200 // FIXME: Move childrenChanged into ScriptDisallowedScope block.
201 if (replacedAllChildren == ReplacedAllChildren::Yes)
202 containerNode.childrenChanged(ContainerNode::ChildChange { ContainerNode::AllChildrenReplaced, nullptr, nullptr, source });
203 else {
204 containerNode.childrenChanged(ContainerNode::ChildChange {
205 child.isElementNode() ? ContainerNode::ElementInserted : (child.isTextNode() ? ContainerNode::TextInserted : ContainerNode::NonContentsChildInserted),
206 ElementTraversal::previousSibling(child),
207 ElementTraversal::nextSibling(child),
208 source
209 });
210 }
211
212 ASSERT(ScriptDisallowedScope::InMainThread::isEventDispatchAllowedInSubtree(child));
213 for (auto& target : postInsertionNotificationTargets)
214 target->didFinishInsertingNode();
215
216 if (source == ContainerNode::ChildChangeSource::API)
217 dispatchChildInsertionEvents(child);
218}
219
220static ExceptionOr<void> collectChildrenAndRemoveFromOldParent(Node& node, NodeVector& nodes)
221{
222 if (!is<DocumentFragment>(node)) {
223 nodes.append(node);
224 auto* oldParent = node.parentNode();
225 if (!oldParent)
226 return { };
227 return oldParent->removeChild(node);
228 }
229
230 nodes = collectChildNodes(node);
231 downcast<DocumentFragment>(node).removeChildren();
232 return { };
233}
234
235// FIXME: This function must get a new name.
236// It removes all children, not just a category called "detached children".
237// So this name is terribly confusing.
238void ContainerNode::removeDetachedChildren()
239{
240 if (connectedSubframeCount()) {
241 for (Node* child = firstChild(); child; child = child->nextSibling())
242 child->updateAncestorConnectedSubframeCountForRemoval();
243 }
244 // FIXME: We should be able to ASSERT(!attached()) here: https://bugs.webkit.org/show_bug.cgi?id=107801
245 ScriptDisallowedScope::InMainThread scriptDisallowedScope;
246 removeDetachedChildrenInContainer(*this);
247}
248
249static inline void destroyRenderTreeIfNeeded(Node& child)
250{
251 bool isElement = is<Element>(child);
252 auto hasDisplayContents = isElement && downcast<Element>(child).hasDisplayContents();
253 if (!child.renderer() && !hasDisplayContents)
254 return;
255 if (isElement)
256 RenderTreeUpdater::tearDownRenderers(downcast<Element>(child));
257 else if (is<Text>(child))
258 RenderTreeUpdater::tearDownRenderer(downcast<Text>(child));
259}
260
261void ContainerNode::takeAllChildrenFrom(ContainerNode* oldParent)
262{
263 ASSERT(oldParent);
264
265 auto children = oldParent->removeAllChildrenWithScriptAssertion(ChildChangeSource::Parser);
266
267 // FIXME: assert that we don't dispatch events here since this container node is still disconnected.
268 for (auto& child : children) {
269 RELEASE_ASSERT(!child->parentNode() && &child->treeScope() == &treeScope());
270 ASSERT(!ensurePreInsertionValidity(child, nullptr).hasException());
271 child->setTreeScopeRecursively(treeScope());
272 parserAppendChild(child);
273 }
274}
275
276ContainerNode::~ContainerNode()
277{
278 if (!isDocumentNode())
279 willBeDeletedFrom(document());
280 removeDetachedChildren();
281}
282
283static inline bool isChildTypeAllowed(ContainerNode& newParent, Node& child)
284{
285 if (!child.isDocumentFragment())
286 return newParent.childTypeAllowed(child.nodeType());
287
288 for (Node* node = child.firstChild(); node; node = node->nextSibling()) {
289 if (!newParent.childTypeAllowed(node->nodeType()))
290 return false;
291 }
292 return true;
293}
294
295static bool containsIncludingHostElements(const Node& possibleAncestor, const Node& node)
296{
297 const Node* currentNode = &node;
298 do {
299 if (currentNode == &possibleAncestor)
300 return true;
301 const ContainerNode* parent = currentNode->parentNode();
302 if (!parent) {
303 if (is<ShadowRoot>(currentNode))
304 parent = downcast<ShadowRoot>(currentNode)->host();
305 else if (is<DocumentFragment>(*currentNode) && downcast<DocumentFragment>(*currentNode).isTemplateContent())
306 parent = static_cast<const TemplateContentDocumentFragment*>(currentNode)->host();
307 }
308 currentNode = parent;
309 } while (currentNode);
310
311 return false;
312}
313
314static inline ExceptionOr<void> checkAcceptChild(ContainerNode& newParent, Node& newChild, const Node* refChild, Document::AcceptChildOperation operation)
315{
316 if (containsIncludingHostElements(newChild, newParent))
317 return Exception { HierarchyRequestError };
318
319 // Use common case fast path if possible.
320 if ((newChild.isElementNode() || newChild.isTextNode()) && newParent.isElementNode()) {
321 ASSERT(!newParent.isDocumentTypeNode());
322 ASSERT(isChildTypeAllowed(newParent, newChild));
323 if (operation == Document::AcceptChildOperation::InsertOrAdd && refChild && refChild->parentNode() != &newParent)
324 return Exception { NotFoundError };
325 return { };
326 }
327
328 // This should never happen, but also protect release builds from tree corruption.
329 ASSERT(!newChild.isPseudoElement());
330 if (newChild.isPseudoElement())
331 return Exception { HierarchyRequestError };
332
333 if (operation == Document::AcceptChildOperation::InsertOrAdd && refChild && refChild->parentNode() != &newParent)
334 return Exception { NotFoundError };
335
336 if (is<Document>(newParent)) {
337 if (!downcast<Document>(newParent).canAcceptChild(newChild, refChild, operation))
338 return Exception { HierarchyRequestError };
339 } else if (!isChildTypeAllowed(newParent, newChild))
340 return Exception { HierarchyRequestError };
341
342 return { };
343}
344
345static inline ExceptionOr<void> checkAcceptChildGuaranteedNodeTypes(ContainerNode& newParent, Node& newChild)
346{
347 ASSERT(!newParent.isDocumentTypeNode());
348 ASSERT(isChildTypeAllowed(newParent, newChild));
349 if (containsIncludingHostElements(newChild, newParent))
350 return Exception { HierarchyRequestError };
351 return { };
352}
353
354// https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity
355ExceptionOr<void> ContainerNode::ensurePreInsertionValidity(Node& newChild, Node* refChild)
356{
357 return checkAcceptChild(*this, newChild, refChild, Document::AcceptChildOperation::InsertOrAdd);
358}
359
360// https://dom.spec.whatwg.org/#concept-node-replace
361static inline ExceptionOr<void> checkPreReplacementValidity(ContainerNode& newParent, Node& newChild, Node& oldChild)
362{
363 return checkAcceptChild(newParent, newChild, &oldChild, Document::AcceptChildOperation::Replace);
364}
365
366ExceptionOr<void> ContainerNode::insertBefore(Node& newChild, Node* refChild)
367{
368 // Check that this node is not "floating".
369 // If it is, it can be deleted as a side effect of sending mutation events.
370 ASSERT(refCount() || parentOrShadowHostNode());
371
372 // Make sure adding the new child is OK.
373 auto validityCheckResult = ensurePreInsertionValidity(newChild, refChild);
374 if (validityCheckResult.hasException())
375 return validityCheckResult.releaseException();
376
377 if (refChild == &newChild)
378 refChild = newChild.nextSibling();
379
380 // insertBefore(node, null) is equivalent to appendChild(node)
381 if (!refChild)
382 return appendChildWithoutPreInsertionValidityCheck(newChild);
383
384 Ref<ContainerNode> protectedThis(*this);
385 Ref<Node> next(*refChild);
386
387 NodeVector targets;
388 auto removeResult = collectChildrenAndRemoveFromOldParent(newChild, targets);
389 if (removeResult.hasException())
390 return removeResult.releaseException();
391 if (targets.isEmpty())
392 return { };
393
394 // We need this extra check because collectChildrenAndRemoveFromOldParent() can fire mutation events.
395 for (auto& child : targets) {
396 auto checkAcceptResult = checkAcceptChildGuaranteedNodeTypes(*this, child);
397 if (checkAcceptResult.hasException())
398 return checkAcceptResult.releaseException();
399 }
400
401 InspectorInstrumentation::willInsertDOMNode(document(), *this);
402
403 ChildListMutationScope mutation(*this);
404 for (auto& child : targets) {
405 // Due to arbitrary code running in response to a DOM mutation event it's
406 // possible that "next" is no longer a child of "this".
407 // It's also possible that "child" has been inserted elsewhere.
408 // In either of those cases, we'll just stop.
409 if (next->parentNode() != this)
410 break;
411 if (child->parentNode())
412 break;
413
414 executeNodeInsertionWithScriptAssertion(*this, child.get(), ChildChangeSource::API, ReplacedAllChildren::No, [&] {
415 child->setTreeScopeRecursively(treeScope());
416 insertBeforeCommon(next, child);
417 });
418 }
419
420 dispatchSubtreeModifiedEvent();
421 return { };
422}
423
424void ContainerNode::insertBeforeCommon(Node& nextChild, Node& newChild)
425{
426 ScriptDisallowedScope::InMainThread scriptDisallowedScope;
427
428 ASSERT(!newChild.parentNode()); // Use insertBefore if you need to handle reparenting (and want DOM mutation events).
429 ASSERT(!newChild.nextSibling());
430 ASSERT(!newChild.previousSibling());
431 ASSERT(!newChild.isShadowRoot());
432
433 Node* prev = nextChild.previousSibling();
434 ASSERT(m_lastChild != prev);
435 nextChild.setPreviousSibling(&newChild);
436 if (prev) {
437 ASSERT(m_firstChild != &nextChild);
438 ASSERT(prev->nextSibling() == &nextChild);
439 prev->setNextSibling(&newChild);
440 } else {
441 ASSERT(m_firstChild == &nextChild);
442 m_firstChild = &newChild;
443 }
444 newChild.setParentNode(this);
445 newChild.setPreviousSibling(prev);
446 newChild.setNextSibling(&nextChild);
447}
448
449void ContainerNode::appendChildCommon(Node& child)
450{
451 ScriptDisallowedScope::InMainThread scriptDisallowedScope;
452
453 child.setParentNode(this);
454
455 if (m_lastChild) {
456 child.setPreviousSibling(m_lastChild);
457 m_lastChild->setNextSibling(&child);
458 } else
459 m_firstChild = &child;
460
461 m_lastChild = &child;
462}
463
464void ContainerNode::parserInsertBefore(Node& newChild, Node& nextChild)
465{
466 ASSERT(nextChild.parentNode() == this);
467 ASSERT(!newChild.isDocumentFragment());
468 ASSERT(!hasTagName(HTMLNames::templateTag));
469
470 if (nextChild.previousSibling() == &newChild || &nextChild == &newChild) // nothing to do
471 return;
472
473 executeNodeInsertionWithScriptAssertion(*this, newChild, ChildChangeSource::Parser, ReplacedAllChildren::No, [&] {
474 if (&document() != &newChild.document())
475 document().adoptNode(newChild);
476
477 insertBeforeCommon(nextChild, newChild);
478
479 newChild.updateAncestorConnectedSubframeCountForInsertion();
480 });
481}
482
483ExceptionOr<void> ContainerNode::replaceChild(Node& newChild, Node& oldChild)
484{
485 // Check that this node is not "floating".
486 // If it is, it can be deleted as a side effect of sending mutation events.
487 ASSERT(refCount() || parentOrShadowHostNode());
488
489 Ref<ContainerNode> protectedThis(*this);
490
491 // Make sure replacing the old child with the new is ok
492 auto validityResult = checkPreReplacementValidity(*this, newChild, oldChild);
493 if (validityResult.hasException())
494 return validityResult.releaseException();
495
496 // NotFoundError: Raised if oldChild is not a child of this node.
497 if (oldChild.parentNode() != this)
498 return Exception { NotFoundError };
499
500 RefPtr<Node> refChild = oldChild.nextSibling();
501 if (refChild.get() == &newChild)
502 refChild = refChild->nextSibling();
503
504 NodeVector targets;
505 {
506 ChildListMutationScope mutation(*this);
507 auto collectResult = collectChildrenAndRemoveFromOldParent(newChild, targets);
508 if (collectResult.hasException())
509 return collectResult.releaseException();
510 }
511
512 // Do this one more time because collectChildrenAndRemoveFromOldParent() fires a MutationEvent.
513 for (auto& child : targets) {
514 validityResult = checkPreReplacementValidity(*this, child, oldChild);
515 if (validityResult.hasException())
516 return validityResult.releaseException();
517 }
518
519 // Remove the node we're replacing.
520 Ref<Node> protectOldChild(oldChild);
521
522 ChildListMutationScope mutation(*this);
523
524 // If oldChild == newChild then oldChild no longer has a parent at this point.
525 if (oldChild.parentNode()) {
526 auto removeResult = removeChild(oldChild);
527 if (removeResult.hasException())
528 return removeResult.releaseException();
529
530 // Does this one more time because removeChild() fires a MutationEvent.
531 for (auto& child : targets) {
532 validityResult = checkPreReplacementValidity(*this, child, oldChild);
533 if (validityResult.hasException())
534 return validityResult.releaseException();
535 }
536 }
537
538 InspectorInstrumentation::willInsertDOMNode(document(), *this);
539
540 // Add the new child(ren).
541 for (auto& child : targets) {
542 // Due to arbitrary code running in response to a DOM mutation event it's
543 // possible that "refChild" is no longer a child of "this".
544 // It's also possible that "child" has been inserted elsewhere.
545 // In either of those cases, we'll just stop.
546 if (refChild && refChild->parentNode() != this)
547 break;
548 if (child->parentNode())
549 break;
550
551 executeNodeInsertionWithScriptAssertion(*this, child.get(), ChildChangeSource::API, ReplacedAllChildren::No, [&] {
552 child->setTreeScopeRecursively(treeScope());
553 if (refChild)
554 insertBeforeCommon(*refChild, child.get());
555 else
556 appendChildCommon(child);
557 });
558 }
559
560 dispatchSubtreeModifiedEvent();
561 return { };
562}
563
564void ContainerNode::disconnectDescendantFrames()
565{
566 disconnectSubframesIfNeeded(*this, RootAndDescendants);
567}
568
569ExceptionOr<void> ContainerNode::removeChild(Node& oldChild)
570{
571 // Check that this node is not "floating".
572 // If it is, it can be deleted as a side effect of sending mutation events.
573 ASSERT(refCount() || parentOrShadowHostNode());
574
575 Ref<ContainerNode> protectedThis(*this);
576
577 // NotFoundError: Raised if oldChild is not a child of this node.
578 if (oldChild.parentNode() != this)
579 return Exception { NotFoundError };
580
581 if (!removeNodeWithScriptAssertion(oldChild, ChildChangeSource::API))
582 return Exception { NotFoundError };
583
584 rebuildSVGExtensionsElementsIfNecessary();
585 dispatchSubtreeModifiedEvent();
586
587 return { };
588}
589
590void ContainerNode::removeBetween(Node* previousChild, Node* nextChild, Node& oldChild)
591{
592 InspectorInstrumentation::didRemoveDOMNode(oldChild.document(), oldChild);
593
594 ScriptDisallowedScope::InMainThread scriptDisallowedScope;
595
596 ASSERT(oldChild.parentNode() == this);
597
598 destroyRenderTreeIfNeeded(oldChild);
599
600 if (nextChild) {
601 nextChild->setPreviousSibling(previousChild);
602 oldChild.setNextSibling(nullptr);
603 } else {
604 ASSERT(m_lastChild == &oldChild);
605 m_lastChild = previousChild;
606 }
607 if (previousChild) {
608 previousChild->setNextSibling(nextChild);
609 oldChild.setPreviousSibling(nullptr);
610 } else {
611 ASSERT(m_firstChild == &oldChild);
612 m_firstChild = nextChild;
613 }
614
615 ASSERT(m_firstChild != &oldChild);
616 ASSERT(m_lastChild != &oldChild);
617 ASSERT(!oldChild.previousSibling());
618 ASSERT(!oldChild.nextSibling());
619 oldChild.setParentNode(nullptr);
620
621 oldChild.setTreeScopeRecursively(document());
622}
623
624void ContainerNode::parserRemoveChild(Node& oldChild)
625{
626 removeNodeWithScriptAssertion(oldChild, ChildChangeSource::Parser);
627}
628
629// https://dom.spec.whatwg.org/#concept-node-replace-all
630void ContainerNode::replaceAllChildren(std::nullptr_t)
631{
632 ChildListMutationScope mutation(*this);
633 removeChildren();
634}
635
636// https://dom.spec.whatwg.org/#concept-node-replace-all
637void ContainerNode::replaceAllChildren(Ref<Node>&& node)
638{
639 // This function assumes the input node is not a DocumentFragment and is parentless to decrease complexity.
640 ASSERT(!is<DocumentFragment>(node));
641 ASSERT(!node->parentNode());
642
643 if (!hasChildNodes()) {
644 // appendChildWithoutPreInsertionValidityCheck() can only throw when node has a parent and we already asserted it doesn't.
645 auto result = appendChildWithoutPreInsertionValidityCheck(node);
646 ASSERT_UNUSED(result, !result.hasException());
647 return;
648 }
649
650 Ref<ContainerNode> protectedThis(*this);
651 ChildListMutationScope mutation(*this);
652 removeAllChildrenWithScriptAssertion(ChildChangeSource::API, DeferChildrenChanged::Yes);
653
654 executeNodeInsertionWithScriptAssertion(*this, node.get(), ChildChangeSource::API, ReplacedAllChildren::Yes, [&] {
655 ASSERT(!ensurePreInsertionValidity(node, nullptr).hasException());
656 InspectorInstrumentation::willInsertDOMNode(document(), *this);
657 node->setTreeScopeRecursively(treeScope());
658 appendChildCommon(node);
659 });
660
661 rebuildSVGExtensionsElementsIfNecessary();
662 dispatchSubtreeModifiedEvent();
663}
664
665inline void ContainerNode::rebuildSVGExtensionsElementsIfNecessary()
666{
667 if (document().svgExtensions() && !is<SVGUseElement>(shadowHost()))
668 document().accessSVGExtensions().rebuildElements();
669}
670
671// this differs from other remove functions because it forcibly removes all the children,
672// regardless of read-only status or event exceptions, e.g.
673void ContainerNode::removeChildren()
674{
675 if (!m_firstChild)
676 return;
677
678 Ref<ContainerNode> protectedThis(*this);
679 removeAllChildrenWithScriptAssertion(ChildChangeSource::API);
680
681 rebuildSVGExtensionsElementsIfNecessary();
682 dispatchSubtreeModifiedEvent();
683}
684
685ExceptionOr<void> ContainerNode::appendChild(Node& newChild)
686{
687 // Check that this node is not "floating".
688 // If it is, it can be deleted as a side effect of sending mutation events.
689 ASSERT(refCount() || parentOrShadowHostNode());
690
691 // Make sure adding the new child is ok
692 auto validityCheckResult = ensurePreInsertionValidity(newChild, nullptr);
693 if (validityCheckResult.hasException())
694 return validityCheckResult.releaseException();
695
696 return appendChildWithoutPreInsertionValidityCheck(newChild);
697}
698
699ExceptionOr<void> ContainerNode::appendChildWithoutPreInsertionValidityCheck(Node& newChild)
700{
701 Ref<ContainerNode> protectedThis(*this);
702
703 NodeVector targets;
704 auto removeResult = collectChildrenAndRemoveFromOldParent(newChild, targets);
705 if (removeResult.hasException())
706 return removeResult.releaseException();
707
708 if (targets.isEmpty())
709 return { };
710
711 // We need this extra check because collectChildrenAndRemoveFromOldParent() can fire mutation events.
712 for (auto& child : targets) {
713 auto nodeTypeResult = checkAcceptChildGuaranteedNodeTypes(*this, child);
714 if (nodeTypeResult.hasException())
715 return nodeTypeResult.releaseException();
716 }
717
718 InspectorInstrumentation::willInsertDOMNode(document(), *this);
719
720 // Now actually add the child(ren)
721 ChildListMutationScope mutation(*this);
722 for (auto& child : targets) {
723 // If the child has a parent again, just stop what we're doing, because
724 // that means someone is doing something with DOM mutation -- can't re-parent
725 // a child that already has a parent.
726 if (child->parentNode())
727 break;
728
729 // Append child to the end of the list
730 executeNodeInsertionWithScriptAssertion(*this, child.get(), ChildChangeSource::API, ReplacedAllChildren::No, [&] {
731 child->setTreeScopeRecursively(treeScope());
732 appendChildCommon(child);
733 });
734 }
735
736 dispatchSubtreeModifiedEvent();
737 return { };
738}
739
740void ContainerNode::parserAppendChild(Node& newChild)
741{
742 ASSERT(!newChild.parentNode()); // Use appendChild if you need to handle reparenting (and want DOM mutation events).
743 ASSERT(!newChild.isDocumentFragment());
744 ASSERT(!hasTagName(HTMLNames::templateTag));
745
746 executeNodeInsertionWithScriptAssertion(*this, newChild, ChildChangeSource::Parser, ReplacedAllChildren::No, [&] {
747 if (&document() != &newChild.document())
748 document().adoptNode(newChild);
749
750 appendChildCommon(newChild);
751 newChild.setTreeScopeRecursively(treeScope());
752 newChild.updateAncestorConnectedSubframeCountForInsertion();
753 });
754}
755
756static bool affectsElements(const ContainerNode::ChildChange& change)
757{
758 switch (change.type) {
759 case ContainerNode::ElementInserted:
760 case ContainerNode::ElementRemoved:
761 case ContainerNode::AllChildrenRemoved:
762 case ContainerNode::AllChildrenReplaced:
763 return true;
764 case ContainerNode::TextInserted:
765 case ContainerNode::TextRemoved:
766 case ContainerNode::TextChanged:
767 case ContainerNode::NonContentsChildInserted:
768 case ContainerNode::NonContentsChildRemoved:
769 return false;
770 }
771 ASSERT_NOT_REACHED();
772 return false;
773}
774
775void ContainerNode::childrenChanged(const ChildChange& change)
776{
777 document().incDOMTreeVersion();
778
779 if (affectsElements(change))
780 document().invalidateAccessKeyCache();
781
782 // FIXME: Unclear why it's always safe to skip this when parser is adding children.
783 // FIXME: Seems like it's equally safe to skip for TextInserted and TextRemoved as for TextChanged.
784 // FIXME: Should use switch for change type so we remember to update when adding new types.
785 if (change.source == ChildChangeSource::API && change.type != TextChanged)
786 document().updateRangesAfterChildrenChanged(*this);
787
788 invalidateNodeListAndCollectionCachesInAncestors();
789}
790
791void ContainerNode::cloneChildNodes(ContainerNode& clone)
792{
793 Document& targetDocument = clone.document();
794 for (Node* child = firstChild(); child; child = child->nextSibling()) {
795 auto clonedChild = child->cloneNodeInternal(targetDocument, CloningOperation::SelfWithTemplateContent);
796 if (!clone.appendChild(clonedChild).hasException() && is<ContainerNode>(*child))
797 downcast<ContainerNode>(*child).cloneChildNodes(downcast<ContainerNode>(clonedChild.get()));
798 }
799}
800
801unsigned ContainerNode::countChildNodes() const
802{
803 unsigned count = 0;
804 for (Node* child = firstChild(); child; child = child->nextSibling())
805 ++count;
806 return count;
807}
808
809Node* ContainerNode::traverseToChildAt(unsigned index) const
810{
811 Node* child = firstChild();
812 for (; child && index > 0; --index)
813 child = child->nextSibling();
814 return child;
815}
816
817static void dispatchChildInsertionEvents(Node& child)
818{
819 if (child.isInShadowTree())
820 return;
821
822 ASSERT_WITH_SECURITY_IMPLICATION(ScriptDisallowedScope::InMainThread::isEventDispatchAllowedInSubtree(child));
823
824 RefPtr<Node> c = &child;
825 Ref<Document> document(child.document());
826
827 if (c->parentNode() && document->hasListenerType(Document::DOMNODEINSERTED_LISTENER))
828 c->dispatchScopedEvent(MutationEvent::create(eventNames().DOMNodeInsertedEvent, Event::CanBubble::Yes, c->parentNode()));
829
830 // dispatch the DOMNodeInsertedIntoDocument event to all descendants
831 if (c->isConnected() && document->hasListenerType(Document::DOMNODEINSERTEDINTODOCUMENT_LISTENER)) {
832 for (; c; c = NodeTraversal::next(*c, &child))
833 c->dispatchScopedEvent(MutationEvent::create(eventNames().DOMNodeInsertedIntoDocumentEvent, Event::CanBubble::No));
834 }
835}
836
837static void dispatchChildRemovalEvents(Ref<Node>& child)
838{
839 ASSERT_WITH_SECURITY_IMPLICATION(ScriptDisallowedScope::InMainThread::isEventDispatchAllowedInSubtree(child));
840 InspectorInstrumentation::willRemoveDOMNode(child->document(), child.get());
841
842 if (child->isInShadowTree())
843 return;
844
845 // FIXME: This doesn't belong in dispatchChildRemovalEvents.
846 // FIXME: Nodes removed from a shadow tree should also be kept alive.
847 willCreatePossiblyOrphanedTreeByRemoval(child.ptr());
848
849 Ref<Document> document = child->document();
850
851 // dispatch pre-removal mutation events
852 if (child->parentNode() && document->hasListenerType(Document::DOMNODEREMOVED_LISTENER))
853 child->dispatchScopedEvent(MutationEvent::create(eventNames().DOMNodeRemovedEvent, Event::CanBubble::Yes, child->parentNode()));
854
855 // dispatch the DOMNodeRemovedFromDocument event to all descendants
856 if (child->isConnected() && document->hasListenerType(Document::DOMNODEREMOVEDFROMDOCUMENT_LISTENER)) {
857 for (RefPtr<Node> currentNode = child.copyRef(); currentNode; currentNode = NodeTraversal::next(*currentNode, child.ptr()))
858 currentNode->dispatchScopedEvent(MutationEvent::create(eventNames().DOMNodeRemovedFromDocumentEvent, Event::CanBubble::No));
859 }
860}
861
862ExceptionOr<Element*> ContainerNode::querySelector(const String& selectors)
863{
864 auto query = document().selectorQueryForString(selectors);
865 if (query.hasException())
866 return query.releaseException();
867 return query.releaseReturnValue().queryFirst(*this);
868}
869
870ExceptionOr<Ref<NodeList>> ContainerNode::querySelectorAll(const String& selectors)
871{
872 auto query = document().selectorQueryForString(selectors);
873 if (query.hasException())
874 return query.releaseException();
875 return query.releaseReturnValue().queryAll(*this);
876}
877
878Ref<HTMLCollection> ContainerNode::getElementsByTagName(const AtomString& qualifiedName)
879{
880 ASSERT(!qualifiedName.isNull());
881
882 if (qualifiedName == starAtom())
883 return ensureRareData().ensureNodeLists().addCachedCollection<AllDescendantsCollection>(*this, AllDescendants);
884
885 if (document().isHTMLDocument())
886 return ensureRareData().ensureNodeLists().addCachedCollection<HTMLTagCollection>(*this, ByHTMLTag, qualifiedName);
887 return ensureRareData().ensureNodeLists().addCachedCollection<TagCollection>(*this, ByTag, qualifiedName);
888}
889
890Ref<HTMLCollection> ContainerNode::getElementsByTagNameNS(const AtomString& namespaceURI, const AtomString& localName)
891{
892 ASSERT(!localName.isNull());
893 return ensureRareData().ensureNodeLists().addCachedTagCollectionNS(*this, namespaceURI.isEmpty() ? nullAtom() : namespaceURI, localName);
894}
895
896Ref<NodeList> ContainerNode::getElementsByName(const String& elementName)
897{
898 return ensureRareData().ensureNodeLists().addCacheWithAtomicName<NameNodeList>(*this, elementName);
899}
900
901Ref<HTMLCollection> ContainerNode::getElementsByClassName(const AtomString& classNames)
902{
903 return ensureRareData().ensureNodeLists().addCachedCollection<ClassCollection>(*this, ByClass, classNames);
904}
905
906Ref<RadioNodeList> ContainerNode::radioNodeList(const AtomString& name)
907{
908 ASSERT(hasTagName(HTMLNames::formTag) || hasTagName(HTMLNames::fieldsetTag));
909 return ensureRareData().ensureNodeLists().addCacheWithAtomicName<RadioNodeList>(*this, name);
910}
911
912Ref<HTMLCollection> ContainerNode::children()
913{
914 return ensureRareData().ensureNodeLists().addCachedCollection<GenericCachedHTMLCollection<CollectionTypeTraits<NodeChildren>::traversalType>>(*this, NodeChildren);
915}
916
917Element* ContainerNode::firstElementChild() const
918{
919 return ElementTraversal::firstChild(*this);
920}
921
922Element* ContainerNode::lastElementChild() const
923{
924 return ElementTraversal::lastChild(*this);
925}
926
927unsigned ContainerNode::childElementCount() const
928{
929 auto children = childrenOfType<Element>(*this);
930 return std::distance(children.begin(), children.end());
931}
932
933ExceptionOr<void> ContainerNode::append(Vector<NodeOrString>&& vector)
934{
935 auto result = convertNodesOrStringsIntoNode(WTFMove(vector));
936 if (result.hasException())
937 return result.releaseException();
938
939 auto node = result.releaseReturnValue();
940 if (!node)
941 return { };
942
943 return appendChild(*node);
944}
945
946ExceptionOr<void> ContainerNode::prepend(Vector<NodeOrString>&& vector)
947{
948 auto result = convertNodesOrStringsIntoNode(WTFMove(vector));
949 if (result.hasException())
950 return result.releaseException();
951
952 auto node = result.releaseReturnValue();
953 if (!node)
954 return { };
955
956 return insertBefore(*node, firstChild());
957}
958
959HTMLCollection* ContainerNode::cachedHTMLCollection(CollectionType type)
960{
961 return hasRareData() && rareData()->nodeLists() ? rareData()->nodeLists()->cachedCollection<HTMLCollection>(type) : nullptr;
962}
963
964} // namespace WebCore
965