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, 2005, 2006, 2007, 2008, 2009, 2013 Apple Inc. All rights reserved.
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.
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.
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.
24 #include "core/dom/ContainerNode.h"
26 #include "bindings/core/v8/ExceptionState.h"
27 #include "core/dom/ChildFrameDisconnector.h"
28 #include "core/dom/ChildListMutationScope.h"
29 #include "core/dom/ClassCollection.h"
30 #include "core/dom/ElementTraversal.h"
31 #include "core/dom/ExceptionCode.h"
32 #include "core/dom/NameNodeList.h"
33 #include "core/dom/NodeChildRemovalTracker.h"
34 #include "core/dom/NodeRareData.h"
35 #include "core/dom/NodeRenderStyle.h"
36 #include "core/dom/NodeTraversal.h"
37 #include "core/dom/SelectorQuery.h"
38 #include "core/dom/StaticNodeList.h"
39 #include "core/dom/StyleEngine.h"
40 #include "core/dom/shadow/ElementShadow.h"
41 #include "core/dom/shadow/ShadowRoot.h"
42 #include "core/events/MutationEvent.h"
43 #include "core/html/HTMLCollection.h"
44 #include "core/html/HTMLFrameOwnerElement.h"
45 #include "core/html/HTMLTagCollection.h"
46 #include "core/html/RadioNodeList.h"
47 #include "core/inspector/InspectorInstrumentation.h"
48 #include "core/rendering/InlineTextBox.h"
49 #include "core/rendering/RenderText.h"
50 #include "core/rendering/RenderTheme.h"
51 #include "core/rendering/RenderView.h"
52 #include "platform/EventDispatchForbiddenScope.h"
53 #include "platform/ScriptForbiddenScope.h"
57 using namespace HTMLNames;
59 static void dispatchChildInsertionEvents(Node&);
60 static void dispatchChildRemovalEvents(Node&);
63 unsigned EventDispatchForbiddenScope::s_count = 0;
66 static void collectChildrenAndRemoveFromOldParent(Node& node, NodeVector& nodes, ExceptionState& exceptionState)
68 if (node.isDocumentFragment()) {
69 DocumentFragment& fragment = toDocumentFragment(node);
70 getChildNodes(fragment, nodes);
71 fragment.removeChildren();
75 if (ContainerNode* oldParent = node.parentNode())
76 oldParent->removeChild(&node, exceptionState);
80 void ContainerNode::removeDetachedChildren()
82 ASSERT(!connectedSubframeCount());
83 ASSERT(needsAttach());
84 removeDetachedChildrenInContainer(*this);
88 void ContainerNode::parserTakeAllChildrenFrom(ContainerNode& oldParent)
90 while (RefPtrWillBeRawPtr<Node> child = oldParent.firstChild()) {
91 oldParent.parserRemoveChild(*child);
92 treeScope().adoptIfNeeded(*child);
93 parserAppendChild(child.get());
97 ContainerNode::~ContainerNode()
99 ASSERT(needsAttach());
101 willBeDeletedFromDocument();
102 removeDetachedChildren();
106 bool ContainerNode::isChildTypeAllowed(const Node& child) const
108 if (!child.isDocumentFragment())
109 return childTypeAllowed(child.nodeType());
111 for (Node* node = toDocumentFragment(child).firstChild(); node; node = node->nextSibling()) {
112 if (!childTypeAllowed(node->nodeType()))
118 bool ContainerNode::containsConsideringHostElements(const Node& newChild) const
120 if (isInShadowTree() || document().isTemplateDocument())
121 return newChild.containsIncludingHostElements(*this);
122 return newChild.contains(this);
125 bool ContainerNode::checkAcceptChild(const Node* newChild, const Node* oldChild, ExceptionState& exceptionState) const
127 // Not mentioned in spec: throw NotFoundError if newChild is null
129 exceptionState.throwDOMException(NotFoundError, "The new child element is null.");
133 // Use common case fast path if possible.
134 if ((newChild->isElementNode() || newChild->isTextNode()) && isElementNode()) {
135 ASSERT(isChildTypeAllowed(*newChild));
136 if (containsConsideringHostElements(*newChild)) {
137 exceptionState.throwDOMException(HierarchyRequestError, "The new child element contains the parent.");
143 // This should never happen, but also protect release builds from tree corruption.
144 ASSERT(!newChild->isPseudoElement());
145 if (newChild->isPseudoElement()) {
146 exceptionState.throwDOMException(HierarchyRequestError, "The new child element is a pseudo-element.");
150 if (containsConsideringHostElements(*newChild)) {
151 exceptionState.throwDOMException(HierarchyRequestError, "The new child element contains the parent.");
155 if (oldChild && isDocumentNode()) {
156 if (!toDocument(this)->canReplaceChild(*newChild, *oldChild)) {
157 // FIXME: Adjust 'Document::canReplaceChild' to return some additional detail (or an error message).
158 exceptionState.throwDOMException(HierarchyRequestError, "Failed to replace child.");
161 } else if (!isChildTypeAllowed(*newChild)) {
162 exceptionState.throwDOMException(HierarchyRequestError, "Nodes of type '" + newChild->nodeName() + "' may not be inserted inside nodes of type '" + nodeName() + "'.");
169 bool ContainerNode::checkAcceptChildGuaranteedNodeTypes(const Node& newChild, ExceptionState& exceptionState) const
171 ASSERT(isChildTypeAllowed(newChild));
172 if (newChild.contains(this)) {
173 exceptionState.throwDOMException(HierarchyRequestError, "The new child element contains the parent.");
179 PassRefPtrWillBeRawPtr<Node> ContainerNode::insertBefore(PassRefPtrWillBeRawPtr<Node> newChild, Node* refChild, ExceptionState& exceptionState)
182 // Check that this node is not "floating".
183 // If it is, it can be deleted as a side effect of sending mutation events.
184 ASSERT(refCount() || parentOrShadowHostNode());
187 RefPtrWillBeRawPtr<Node> protect(this);
189 // insertBefore(node, 0) is equivalent to appendChild(node)
191 return appendChild(newChild, exceptionState);
194 // Make sure adding the new child is OK.
195 if (!checkAcceptChild(newChild.get(), 0, exceptionState)) {
196 if (exceptionState.hadException())
202 // NotFoundError: Raised if refChild is not a child of this node
203 if (refChild->parentNode() != this) {
204 exceptionState.throwDOMException(NotFoundError, "The node before which the new node is to be inserted is not a child of this node.");
209 if (refChild->previousSibling() == newChild || refChild == newChild)
212 RefPtrWillBeRawPtr<Node> next = refChild;
215 collectChildrenAndRemoveFromOldParent(*newChild, targets, exceptionState);
216 if (exceptionState.hadException())
218 if (targets.isEmpty())
221 // We need this extra check because collectChildrenAndRemoveFromOldParent() can fire mutation events.
222 if (!checkAcceptChildGuaranteedNodeTypes(*newChild, exceptionState)) {
223 if (exceptionState.hadException())
228 InspectorInstrumentation::willInsertDOMNode(this);
230 ChildListMutationScope mutation(*this);
231 for (const auto& targetNode : targets) {
233 Node& child = *targetNode;
235 // Due to arbitrary code running in response to a DOM mutation event it's
236 // possible that "next" is no longer a child of "this".
237 // It's also possible that "child" has been inserted elsewhere.
238 // In either of those cases, we'll just stop.
239 if (next->parentNode() != this)
241 if (child.parentNode())
244 treeScope().adoptIfNeeded(child);
246 insertBeforeCommon(*next, child);
248 updateTreeAfterInsertion(child);
251 dispatchSubtreeModifiedEvent();
256 void ContainerNode::insertBeforeCommon(Node& nextChild, Node& newChild)
258 EventDispatchForbiddenScope assertNoEventDispatch;
259 ScriptForbiddenScope forbidScript;
261 ASSERT(!newChild.parentNode()); // Use insertBefore if you need to handle reparenting (and want DOM mutation events).
262 ASSERT(!newChild.nextSibling());
263 ASSERT(!newChild.previousSibling());
264 ASSERT(!newChild.isShadowRoot());
266 Node* prev = nextChild.previousSibling();
267 ASSERT(m_lastChild != prev);
268 nextChild.setPreviousSibling(&newChild);
270 ASSERT(firstChild() != nextChild);
271 ASSERT(prev->nextSibling() == nextChild);
272 prev->setNextSibling(&newChild);
274 ASSERT(firstChild() == nextChild);
275 m_firstChild = &newChild;
277 newChild.setParentOrShadowHostNode(this);
278 newChild.setPreviousSibling(prev);
279 newChild.setNextSibling(&nextChild);
282 void ContainerNode::appendChildCommon(Node& child)
284 child.setParentOrShadowHostNode(this);
287 child.setPreviousSibling(m_lastChild);
288 m_lastChild->setNextSibling(&child);
290 setFirstChild(&child);
293 setLastChild(&child);
296 void ContainerNode::parserInsertBefore(PassRefPtrWillBeRawPtr<Node> newChild, Node& nextChild)
299 ASSERT(nextChild.parentNode() == this);
300 ASSERT(!newChild->isDocumentFragment());
301 ASSERT(!isHTMLTemplateElement(this));
303 if (nextChild.previousSibling() == newChild || &nextChild == newChild) // nothing to do
306 RefPtrWillBeRawPtr<Node> protect(this);
308 if (document() != newChild->document())
309 document().adoptNode(newChild.get(), ASSERT_NO_EXCEPTION);
311 insertBeforeCommon(nextChild, *newChild);
313 newChild->updateAncestorConnectedSubframeCountForInsertion();
315 ChildListMutationScope(*this).childAdded(*newChild);
317 notifyNodeInserted(*newChild, ChildrenChangeSourceParser);
320 PassRefPtrWillBeRawPtr<Node> ContainerNode::replaceChild(PassRefPtrWillBeRawPtr<Node> newChild, PassRefPtrWillBeRawPtr<Node> oldChild, ExceptionState& exceptionState)
323 // Check that this node is not "floating".
324 // If it is, it can be deleted as a side effect of sending mutation events.
325 ASSERT(refCount() || parentOrShadowHostNode());
328 RefPtrWillBeRawPtr<Node> protect(this);
330 if (oldChild == newChild) // Nothing to do.
334 exceptionState.throwDOMException(NotFoundError, "The node to be replaced is null.");
338 RefPtrWillBeRawPtr<Node> child = oldChild;
340 // Make sure replacing the old child with the new is OK.
341 if (!checkAcceptChild(newChild.get(), child.get(), exceptionState)) {
342 if (exceptionState.hadException())
347 // NotFoundError: Raised if oldChild is not a child of this node.
348 if (child->parentNode() != this) {
349 exceptionState.throwDOMException(NotFoundError, "The node to be replaced is not a child of this node.");
353 ChildListMutationScope mutation(*this);
355 RefPtrWillBeRawPtr<Node> next = child->nextSibling();
357 // Remove the node we're replacing.
358 removeChild(child, exceptionState);
359 if (exceptionState.hadException())
362 if (next && (next->previousSibling() == newChild || next == newChild)) // nothing to do
365 // Does this one more time because removeChild() fires a MutationEvent.
366 if (!checkAcceptChild(newChild.get(), child.get(), exceptionState)) {
367 if (exceptionState.hadException())
373 collectChildrenAndRemoveFromOldParent(*newChild, targets, exceptionState);
374 if (exceptionState.hadException())
377 // Does this yet another check because collectChildrenAndRemoveFromOldParent() fires a MutationEvent.
378 if (!checkAcceptChild(newChild.get(), child.get(), exceptionState)) {
379 if (exceptionState.hadException())
384 InspectorInstrumentation::willInsertDOMNode(this);
386 // Add the new child(ren).
387 for (const auto& targetNode : targets) {
389 Node& child = *targetNode;
391 // Due to arbitrary code running in response to a DOM mutation event it's
392 // possible that "next" is no longer a child of "this".
393 // It's also possible that "child" has been inserted elsewhere.
394 // In either of those cases, we'll just stop.
395 if (next && next->parentNode() != this)
397 if (child.parentNode())
400 treeScope().adoptIfNeeded(child);
402 // Add child before "next".
404 EventDispatchForbiddenScope assertNoEventDispatch;
406 insertBeforeCommon(*next, child);
408 appendChildCommon(child);
411 updateTreeAfterInsertion(child);
414 dispatchSubtreeModifiedEvent();
418 void ContainerNode::willRemoveChild(Node& child)
420 ASSERT(child.parentNode() == this);
421 ChildListMutationScope(*this).willRemoveChild(child);
422 child.notifyMutationObserversNodeWillDetach();
423 dispatchChildRemovalEvents(child);
424 document().nodeWillBeRemoved(child); // e.g. mutation event listener can create a new range.
425 ChildFrameDisconnector(child).disconnect();
428 void ContainerNode::willRemoveChildren()
431 getChildNodes(*this, children);
433 ChildListMutationScope mutation(*this);
434 for (const auto& node : children) {
437 mutation.willRemoveChild(child);
438 child.notifyMutationObserversNodeWillDetach();
439 dispatchChildRemovalEvents(child);
442 ChildFrameDisconnector(*this).disconnect(ChildFrameDisconnector::DescendantsOnly);
446 void ContainerNode::removeDetachedChildrenInContainer(ContainerNode& container)
448 // List of nodes to be deleted.
449 Node* head = nullptr;
450 Node* tail = nullptr;
452 addChildNodesToDeletionQueue(head, tail, container);
458 ASSERT_WITH_SECURITY_IMPLICATION(n->m_deletionHasBegun);
460 next = n->nextSibling();
461 n->setNextSibling(nullptr);
467 if (n->hasChildren())
468 addChildNodesToDeletionQueue(head, tail, toContainerNode(*n));
474 void ContainerNode::addChildNodesToDeletionQueue(Node*& head, Node*& tail, ContainerNode& container)
476 // We have to tell all children that their parent has died.
477 Node* next = nullptr;
478 for (Node* n = container.firstChild(); n; n = next) {
479 ASSERT_WITH_SECURITY_IMPLICATION(!n->m_deletionHasBegun);
481 next = n->nextSibling();
482 n->setNextSibling(nullptr);
483 n->setParentOrShadowHostNode(nullptr);
484 container.setFirstChild(next);
486 next->setPreviousSibling(nullptr);
488 if (!n->refCount()) {
489 #if ENABLE(SECURITY_ASSERT)
490 n->m_deletionHasBegun = true;
492 // Add the node to the list of nodes to be deleted.
493 // Reuse the nextSibling pointer for this purpose.
495 tail->setNextSibling(n);
501 RefPtrWillBeRawPtr<Node> protect(n); // removedFromDocument may remove all references to this node.
502 container.document().adoptIfNeeded(*n);
504 container.notifyNodeRemoved(*n);
508 container.setLastChild(nullptr);
512 void ContainerNode::disconnectDescendantFrames()
514 ChildFrameDisconnector(*this).disconnect();
517 void ContainerNode::trace(Visitor* visitor)
519 visitor->trace(m_firstChild);
520 visitor->trace(m_lastChild);
521 Node::trace(visitor);
524 PassRefPtrWillBeRawPtr<Node> ContainerNode::removeChild(PassRefPtrWillBeRawPtr<Node> oldChild, ExceptionState& exceptionState)
527 // Check that this node is not "floating".
528 // If it is, it can be deleted as a side effect of sending mutation events.
529 ASSERT(refCount() || parentOrShadowHostNode());
532 RefPtrWillBeRawPtr<Node> protect(this);
534 // NotFoundError: Raised if oldChild is not a child of this node.
535 // FIXME: We should never really get PseudoElements in here, but editing will sometimes
536 // attempt to remove them still. We should fix that and enable this ASSERT.
537 // ASSERT(!oldChild->isPseudoElement())
538 if (!oldChild || oldChild->parentNode() != this || oldChild->isPseudoElement()) {
539 exceptionState.throwDOMException(NotFoundError, "The node to be removed is not a child of this node.");
543 RefPtrWillBeRawPtr<Node> child = oldChild;
545 document().removeFocusedElementOfSubtree(child.get());
547 // Events fired when blurring currently focused node might have moved this
548 // child into a different parent.
549 if (child->parentNode() != this) {
550 exceptionState.throwDOMException(NotFoundError, "The node to be removed is no longer a child of this node. Perhaps it was moved in a 'blur' event handler?");
554 willRemoveChild(*child);
556 // Mutation events might have moved this child into a different parent.
557 if (child->parentNode() != this) {
558 exceptionState.throwDOMException(NotFoundError, "The node to be removed is no longer a child of this node. Perhaps it was moved in response to a mutation?");
563 HTMLFrameOwnerElement::UpdateSuspendScope suspendWidgetHierarchyUpdates;
565 Node* prev = child->previousSibling();
566 Node* next = child->nextSibling();
567 removeBetween(prev, next, *child);
568 notifyNodeRemoved(*child);
569 childrenChanged(ChildrenChange::forRemoval(*child, prev, next, ChildrenChangeSourceAPI));
571 dispatchSubtreeModifiedEvent();
575 void ContainerNode::removeBetween(Node* previousChild, Node* nextChild, Node& oldChild)
577 EventDispatchForbiddenScope assertNoEventDispatch;
579 ASSERT(oldChild.parentNode() == this);
581 if (!oldChild.needsAttach())
585 nextChild->setPreviousSibling(previousChild);
587 previousChild->setNextSibling(nextChild);
588 if (m_firstChild == &oldChild)
589 m_firstChild = nextChild;
590 if (m_lastChild == &oldChild)
591 m_lastChild = previousChild;
593 oldChild.setPreviousSibling(nullptr);
594 oldChild.setNextSibling(nullptr);
595 oldChild.setParentOrShadowHostNode(nullptr);
597 document().adoptIfNeeded(oldChild);
600 void ContainerNode::parserRemoveChild(Node& oldChild)
602 ASSERT(oldChild.parentNode() == this);
603 ASSERT(!oldChild.isDocumentFragment());
605 Node* prev = oldChild.previousSibling();
606 Node* next = oldChild.nextSibling();
608 oldChild.updateAncestorConnectedSubframeCountForRemoval();
610 ChildListMutationScope(*this).willRemoveChild(oldChild);
611 oldChild.notifyMutationObserversNodeWillDetach();
613 removeBetween(prev, next, oldChild);
615 notifyNodeRemoved(oldChild);
616 childrenChanged(ChildrenChange::forRemoval(oldChild, prev, next, ChildrenChangeSourceParser));
619 // This differs from other remove functions because it forcibly removes all the children,
620 // regardless of read-only status or event exceptions, e.g.
621 void ContainerNode::removeChildren()
626 // The container node can be removed from event handlers.
627 RefPtrWillBeRawPtr<ContainerNode> protect(this);
629 // Do any prep work needed before actually starting to detach
630 // and remove... e.g. stop loading frames, fire unload events.
631 willRemoveChildren();
634 // Removing focus can cause frames to load, either via events (focusout, blur)
635 // or widget updates (e.g., for <embed>).
636 SubframeLoadingDisabler disabler(*this);
638 // Exclude this node when looking for removed focusedElement since only
639 // children will be removed.
640 // This must be later than willRemoveChildren, which might change focus
642 document().removeFocusedElementOfSubtree(this, true);
644 // Removing a node from a selection can cause widget updates.
645 document().nodeChildrenWillBeRemoved(*this);
648 // FIXME: Remove this NodeVector. Right now WebPluginContainerImpl::m_element is a
649 // raw ptr which means the code below can drop the last ref to a plugin element and
650 // then the code in UpdateSuspendScope::performDeferredWidgetTreeOperations will
651 // try to destroy the plugin which will be a use-after-free. We should use a RefPtr
652 // in the WebPluginContainerImpl instead.
653 NodeVector removedChildren;
655 HTMLFrameOwnerElement::UpdateSuspendScope suspendWidgetHierarchyUpdates;
658 EventDispatchForbiddenScope assertNoEventDispatch;
659 ScriptForbiddenScope forbidScript;
661 removedChildren.reserveInitialCapacity(countChildren());
663 while (RefPtrWillBeRawPtr<Node> child = m_firstChild) {
664 removeBetween(0, child->nextSibling(), *child);
665 removedChildren.append(child.get());
666 notifyNodeRemoved(*child);
670 ChildrenChange change = {AllChildrenRemoved, nullptr, nullptr, ChildrenChangeSourceAPI};
671 childrenChanged(change);
674 // We don't fire the DOMSubtreeModified event for Attr Nodes. This matches the behavior
675 // of IE and Firefox. This event is fired synchronously and is a source of trouble for
676 // attributes as the JS callback could alter the attributes and leave us in a bad state.
677 if (!isAttributeNode())
678 dispatchSubtreeModifiedEvent();
681 PassRefPtrWillBeRawPtr<Node> ContainerNode::appendChild(PassRefPtrWillBeRawPtr<Node> newChild, ExceptionState& exceptionState)
683 RefPtrWillBeRawPtr<ContainerNode> protect(this);
686 // Check that this node is not "floating".
687 // If it is, it can be deleted as a side effect of sending mutation events.
688 ASSERT(refCount() || parentOrShadowHostNode());
691 // Make sure adding the new child is ok
692 if (!checkAcceptChild(newChild.get(), 0, exceptionState)) {
693 if (exceptionState.hadException())
699 if (newChild == m_lastChild) // nothing to do
703 collectChildrenAndRemoveFromOldParent(*newChild, targets, exceptionState);
704 if (exceptionState.hadException())
707 if (targets.isEmpty())
710 // We need this extra check because collectChildrenAndRemoveFromOldParent() can fire mutation events.
711 if (!checkAcceptChildGuaranteedNodeTypes(*newChild, exceptionState)) {
712 if (exceptionState.hadException())
717 InspectorInstrumentation::willInsertDOMNode(this);
719 // Now actually add the child(ren).
720 ChildListMutationScope mutation(*this);
721 for (const auto& targetNode : targets) {
723 Node& child = *targetNode;
725 // If the child has a parent again, just stop what we're doing, because
726 // that means someone is doing something with DOM mutation -- can't re-parent
727 // a child that already has a parent.
728 if (child.parentNode())
732 EventDispatchForbiddenScope assertNoEventDispatch;
733 ScriptForbiddenScope forbidScript;
735 treeScope().adoptIfNeeded(child);
736 appendChildCommon(child);
739 updateTreeAfterInsertion(child);
742 dispatchSubtreeModifiedEvent();
746 void ContainerNode::parserAppendChild(PassRefPtrWillBeRawPtr<Node> newChild)
749 ASSERT(!newChild->parentNode()); // Use appendChild if you need to handle reparenting (and want DOM mutation events).
750 ASSERT(!newChild->isDocumentFragment());
751 ASSERT(!isHTMLTemplateElement(this));
753 RefPtrWillBeRawPtr<Node> protect(this);
755 if (document() != newChild->document())
756 document().adoptNode(newChild.get(), ASSERT_NO_EXCEPTION);
759 EventDispatchForbiddenScope assertNoEventDispatch;
760 ScriptForbiddenScope forbidScript;
762 treeScope().adoptIfNeeded(*newChild);
763 appendChildCommon(*newChild);
764 newChild->updateAncestorConnectedSubframeCountForInsertion();
765 ChildListMutationScope(*this).childAdded(*newChild);
768 notifyNodeInserted(*newChild, ChildrenChangeSourceParser);
771 void ContainerNode::notifyNodeInserted(Node& root, ChildrenChangeSource source)
773 ASSERT(!EventDispatchForbiddenScope::isEventDispatchForbidden());
774 ASSERT(!root.isShadowRoot());
776 InspectorInstrumentation::didInsertDOMNode(&root);
778 RefPtrWillBeRawPtr<Node> protect(this);
779 RefPtrWillBeRawPtr<Node> protectNode(root);
781 NodeVector postInsertionNotificationTargets;
782 notifyNodeInsertedInternal(root, postInsertionNotificationTargets);
784 childrenChanged(ChildrenChange::forInsertion(root, source));
786 for (const auto& targetNode : postInsertionNotificationTargets) {
787 if (targetNode->inDocument())
788 targetNode->didNotifySubtreeInsertionsToDocument();
792 void ContainerNode::notifyNodeInsertedInternal(Node& root, NodeVector& postInsertionNotificationTargets)
794 EventDispatchForbiddenScope assertNoEventDispatch;
795 ScriptForbiddenScope forbidScript;
797 for (Node& node : NodeTraversal::inclusiveDescendantsOf(root)) {
798 // As an optimization we don't notify leaf nodes when when inserting
799 // into detached subtrees.
800 if (!inDocument() && !node.isContainerNode())
802 if (Node::InsertionShouldCallDidNotifySubtreeInsertions == node.insertedInto(this))
803 postInsertionNotificationTargets.append(&node);
804 for (ShadowRoot* shadowRoot = node.youngestShadowRoot(); shadowRoot; shadowRoot = shadowRoot->olderShadowRoot())
805 notifyNodeInsertedInternal(*shadowRoot, postInsertionNotificationTargets);
809 void ContainerNode::notifyNodeRemoved(Node& root)
811 ScriptForbiddenScope forbidScript;
812 EventDispatchForbiddenScope assertNoEventDispatch;
814 Document& document = root.document();
815 for (Node& node : NodeTraversal::inclusiveDescendantsOf(root)) {
816 // As an optimization we skip notifying Text nodes and other leaf nodes
817 // of removal when they're not in the Document tree since the virtual
818 // call to removedFrom is not needed.
819 if (!node.inDocument() && !node.isContainerNode())
821 if (document.cssTarget() == node)
822 document.setCSSTarget(nullptr);
823 node.removedFrom(this);
824 for (ShadowRoot* shadowRoot = node.youngestShadowRoot(); shadowRoot; shadowRoot = shadowRoot->olderShadowRoot())
825 notifyNodeRemoved(*shadowRoot);
829 void ContainerNode::attach(const AttachContext& context)
831 attachChildren(context);
832 clearChildNeedsStyleRecalc();
833 Node::attach(context);
836 void ContainerNode::detach(const AttachContext& context)
838 detachChildren(context);
839 clearChildNeedsStyleRecalc();
840 Node::detach(context);
843 void ContainerNode::childrenChanged(const ChildrenChange& change)
845 document().incDOMTreeVersion();
846 if (!change.byParser && change.type != TextChanged)
847 document().updateRangesAfterChildrenChanged(this);
848 invalidateNodeListCachesInAncestors();
849 if (change.isChildInsertion() && !childNeedsStyleRecalc()) {
850 setChildNeedsStyleRecalc();
851 markAncestorsWithChildNeedsStyleRecalc();
855 void ContainerNode::cloneChildNodes(ContainerNode *clone)
857 TrackExceptionState exceptionState;
858 for (Node* n = firstChild(); n && !exceptionState.hadException(); n = n->nextSibling())
859 clone->appendChild(n->cloneNode(true), exceptionState);
863 bool ContainerNode::getUpperLeftCorner(FloatPoint& point) const
868 // FIXME: What is this code really trying to do?
869 RenderObject* o = renderer();
870 if (!o->isInline() || o->isReplaced()) {
871 point = o->localToAbsolute(FloatPoint(), UseTransforms);
875 // Find the next text/image child, to get a position.
878 if (RenderObject* oFirstChild = o->slowFirstChild()) {
880 } else if (o->nextSibling()) {
881 o = o->nextSibling();
883 RenderObject* next = nullptr;
884 while (!next && o->parent()) {
886 next = o->nextSibling();
895 if (!o->isInline() || o->isReplaced()) {
896 point = o->localToAbsolute(FloatPoint(), UseTransforms);
900 if (p->node() && p->node() == this && o->isText() && !o->isBR() && !toRenderText(o)->firstTextBox()) {
901 // Do nothing - skip unrendered whitespace that is a child or next sibling of the anchor.
902 } else if ((o->isText() && !o->isBR()) || o->isReplaced()) {
903 point = FloatPoint();
904 if (o->isText() && toRenderText(o)->firstTextBox()) {
905 point.move(toRenderText(o)->linesBoundingBox().x(), toRenderText(o)->firstTextBox()->root().lineTop().toFloat());
906 } else if (o->isBox()) {
907 RenderBox* box = toRenderBox(o);
908 point.moveBy(box->location());
910 point = o->container()->localToAbsolute(point, UseTransforms);
915 // If the target doesn't have any children or siblings that could be used to calculate the scroll position, we must be
916 // at the end of the document. Scroll to the bottom. FIXME: who said anything about scrolling?
917 if (!o && document().view()) {
918 point = FloatPoint(0, document().view()->contentsHeight());
924 bool ContainerNode::getLowerRightCorner(FloatPoint& point) const
929 RenderObject* o = renderer();
930 if (!o->isInline() || o->isReplaced()) {
931 RenderBox* box = toRenderBox(o);
932 point = o->localToAbsolute(LayoutPoint(box->size()), UseTransforms);
936 // Find the last text/image child, to get a position.
938 if (RenderObject* oLastChild = o->slowLastChild()) {
940 } else if (o->previousSibling()) {
941 o = o->previousSibling();
943 RenderObject* prev = nullptr;
948 prev = o->previousSibling();
953 if (o->isText() || o->isReplaced()) {
954 point = FloatPoint();
956 RenderText* text = toRenderText(o);
957 IntRect linesBox = text->linesBoundingBox();
958 if (!linesBox.maxX() && !linesBox.maxY())
960 point.moveBy(linesBox.maxXMaxYCorner());
962 RenderBox* box = toRenderBox(o);
963 point.moveBy(box->frameRect().maxXMaxYCorner());
965 point = o->container()->localToAbsolute(point, UseTransforms);
972 // FIXME: This override is only needed for inline anchors without an
973 // InlineBox and it does not belong in ContainerNode as it reaches into
974 // the render and line box trees.
975 // https://code.google.com/p/chromium/issues/detail?id=248354
976 LayoutRect ContainerNode::boundingBox() const
978 FloatPoint upperLeft, lowerRight;
979 bool foundUpperLeft = getUpperLeftCorner(upperLeft);
980 bool foundLowerRight = getLowerRightCorner(lowerRight);
982 // If we've found one corner, but not the other,
983 // then we should just return a point at the corner that we did find.
984 if (foundUpperLeft != foundLowerRight) {
986 lowerRight = upperLeft;
988 upperLeft = lowerRight;
991 return enclosingLayoutRect(FloatRect(upperLeft, lowerRight.expandedTo(upperLeft) - upperLeft));
994 // This is used by FrameSelection to denote when the active-state of the page has changed
995 // independent of the focused element changing.
996 void ContainerNode::focusStateChanged()
998 // If we're just changing the window's active state and the focused node has no
999 // renderer we can just ignore the state change.
1003 if (styleChangeType() < SubtreeStyleChange) {
1004 if (renderStyle()->affectedByFocus() && renderStyle()->hasPseudoStyle(FIRST_LETTER))
1005 setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::createWithExtraData(StyleChangeReason::PseudoClass, StyleChangeExtraData::Focus));
1006 else if (isElementNode() && toElement(this)->childrenOrSiblingsAffectedByFocus())
1007 document().ensureStyleResolver().ensureUpdatedRuleFeatureSet().scheduleStyleInvalidationForPseudoChange(CSSSelector::PseudoFocus, *toElement(this));
1008 else if (renderStyle()->affectedByFocus())
1009 setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing::createWithExtraData(StyleChangeReason::PseudoClass, StyleChangeExtraData::Focus));
1012 if (renderer() && renderer()->style()->hasAppearance())
1013 RenderTheme::theme().stateChanged(renderer(), FocusControlState);
1016 void ContainerNode::setFocus(bool received)
1018 if (focused() == received)
1021 Node::setFocus(received);
1023 focusStateChanged();
1025 if (renderer() || received)
1028 // If :focus sets display: none, we lose focus but still need to recalc our style.
1029 if (isElementNode() && toElement(this)->childrenOrSiblingsAffectedByFocus() && styleChangeType() < SubtreeStyleChange)
1030 document().ensureStyleResolver().ensureUpdatedRuleFeatureSet().scheduleStyleInvalidationForPseudoChange(CSSSelector::PseudoFocus, *toElement(this));
1032 setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing::createWithExtraData(StyleChangeReason::PseudoClass, StyleChangeExtraData::Focus));
1035 void ContainerNode::setActive(bool down)
1037 if (down == active())
1040 Node::setActive(down);
1042 // FIXME: Why does this not need to handle the display: none transition like :hover does?
1044 if (styleChangeType() < SubtreeStyleChange) {
1045 if (renderStyle()->affectedByActive() && renderStyle()->hasPseudoStyle(FIRST_LETTER))
1046 setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::createWithExtraData(StyleChangeReason::PseudoClass, StyleChangeExtraData::Active));
1047 else if (isElementNode() && toElement(this)->childrenOrSiblingsAffectedByActive())
1048 document().ensureStyleResolver().ensureUpdatedRuleFeatureSet().scheduleStyleInvalidationForPseudoChange(CSSSelector::PseudoActive, *toElement(this));
1049 else if (renderStyle()->affectedByActive())
1050 setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing::createWithExtraData(StyleChangeReason::PseudoClass, StyleChangeExtraData::Active));
1053 if (renderStyle()->hasAppearance())
1054 RenderTheme::theme().stateChanged(renderer(), PressedControlState);
1058 void ContainerNode::setHovered(bool over)
1060 if (over == hovered())
1063 Node::setHovered(over);
1065 // If :hover sets display: none we lose our hover but still need to recalc our style.
1069 if (isElementNode() && toElement(this)->childrenOrSiblingsAffectedByHover() && styleChangeType() < SubtreeStyleChange)
1070 document().ensureStyleResolver().ensureUpdatedRuleFeatureSet().scheduleStyleInvalidationForPseudoChange(CSSSelector::PseudoHover, *toElement(this));
1072 setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing::createWithExtraData(StyleChangeReason::PseudoClass, StyleChangeExtraData::Hover));
1076 if (styleChangeType() < SubtreeStyleChange) {
1077 if (renderStyle()->affectedByHover() && renderStyle()->hasPseudoStyle(FIRST_LETTER))
1078 setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::createWithExtraData(StyleChangeReason::PseudoClass, StyleChangeExtraData::Hover));
1079 else if (isElementNode() && toElement(this)->childrenOrSiblingsAffectedByHover())
1080 document().ensureStyleResolver().ensureUpdatedRuleFeatureSet().scheduleStyleInvalidationForPseudoChange(CSSSelector::PseudoHover, *toElement(this));
1081 else if (renderStyle()->affectedByHover())
1082 setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing::createWithExtraData(StyleChangeReason::PseudoClass, StyleChangeExtraData::Hover));
1085 if (renderer()->style()->hasAppearance())
1086 RenderTheme::theme().stateChanged(renderer(), HoverControlState);
1089 PassRefPtrWillBeRawPtr<HTMLCollection> ContainerNode::children()
1091 return ensureCachedCollection<HTMLCollection>(NodeChildren);
1094 unsigned ContainerNode::countChildren() const
1098 for (n = firstChild(); n; n = n->nextSibling())
1103 PassRefPtrWillBeRawPtr<Element> ContainerNode::querySelector(const AtomicString& selectors, ExceptionState& exceptionState)
1105 if (selectors.isEmpty()) {
1106 exceptionState.throwDOMException(SyntaxError, "The provided selector is empty.");
1110 SelectorQuery* selectorQuery = document().selectorQueryCache().add(selectors, document(), exceptionState);
1113 return selectorQuery->queryFirst(*this);
1116 PassRefPtrWillBeRawPtr<StaticElementList> ContainerNode::querySelectorAll(const AtomicString& selectors, ExceptionState& exceptionState)
1118 if (selectors.isEmpty()) {
1119 exceptionState.throwDOMException(SyntaxError, "The provided selector is empty.");
1123 SelectorQuery* selectorQuery = document().selectorQueryCache().add(selectors, document(), exceptionState);
1127 return selectorQuery->queryAll(*this);
1130 static void dispatchChildInsertionEvents(Node& child)
1132 if (child.isInShadowTree())
1135 ASSERT(!EventDispatchForbiddenScope::isEventDispatchForbidden());
1137 RefPtrWillBeRawPtr<Node> c(child);
1138 RefPtrWillBeRawPtr<Document> document(child.document());
1140 if (c->parentNode() && document->hasListenerType(Document::DOMNODEINSERTED_LISTENER))
1141 c->dispatchScopedEvent(MutationEvent::create(EventTypeNames::DOMNodeInserted, true, c->parentNode()));
1143 // dispatch the DOMNodeInsertedIntoDocument event to all descendants
1144 if (c->inDocument() && document->hasListenerType(Document::DOMNODEINSERTEDINTODOCUMENT_LISTENER)) {
1145 for (; c; c = NodeTraversal::next(*c, &child))
1146 c->dispatchScopedEvent(MutationEvent::create(EventTypeNames::DOMNodeInsertedIntoDocument, false));
1150 static void dispatchChildRemovalEvents(Node& child)
1152 if (child.isInShadowTree()) {
1153 InspectorInstrumentation::willRemoveDOMNode(&child);
1157 ASSERT(!EventDispatchForbiddenScope::isEventDispatchForbidden());
1159 InspectorInstrumentation::willRemoveDOMNode(&child);
1161 RefPtrWillBeRawPtr<Node> c(child);
1162 RefPtrWillBeRawPtr<Document> document(child.document());
1164 // Dispatch pre-removal mutation events.
1165 if (c->parentNode() && document->hasListenerType(Document::DOMNODEREMOVED_LISTENER)) {
1166 NodeChildRemovalTracker scope(child);
1167 c->dispatchScopedEvent(MutationEvent::create(EventTypeNames::DOMNodeRemoved, true, c->parentNode()));
1170 // Dispatch the DOMNodeRemovedFromDocument event to all descendants.
1171 if (c->inDocument() && document->hasListenerType(Document::DOMNODEREMOVEDFROMDOCUMENT_LISTENER)) {
1172 NodeChildRemovalTracker scope(child);
1173 for (; c; c = NodeTraversal::next(*c, &child))
1174 c->dispatchScopedEvent(MutationEvent::create(EventTypeNames::DOMNodeRemovedFromDocument, false));
1178 void ContainerNode::updateTreeAfterInsertion(Node& child)
1182 ASSERT(child.refCount());
1185 ChildListMutationScope(*this).childAdded(child);
1187 notifyNodeInserted(child);
1189 dispatchChildInsertionEvents(child);
1192 bool ContainerNode::hasRestyleFlagInternal(DynamicRestyleFlags mask) const
1194 return rareData()->hasRestyleFlag(mask);
1197 bool ContainerNode::hasRestyleFlagsInternal() const
1199 return rareData()->hasRestyleFlags();
1202 void ContainerNode::setRestyleFlag(DynamicRestyleFlags mask)
1204 ASSERT(isElementNode() || isShadowRoot());
1205 ensureRareData().setRestyleFlag(mask);
1208 void ContainerNode::recalcChildStyle(StyleRecalcChange change)
1210 ASSERT(document().inStyleRecalc());
1211 ASSERT(change >= UpdatePseudoElements || childNeedsStyleRecalc());
1212 ASSERT(!needsStyleRecalc());
1214 if (change < Force && hasRareData() && childNeedsStyleRecalc())
1215 checkForChildrenAdjacentRuleChanges();
1217 // This loop is deliberately backwards because we use insertBefore in the rendering tree, and want to avoid
1218 // a potentially n^2 loop to find the insertion point while resolving style. Having us start from the last
1219 // child and work our way back means in the common case, we'll find the insertion point in O(1) time.
1220 // See crbug.com/288225
1221 StyleResolver& styleResolver = document().ensureStyleResolver();
1222 Text* lastTextNode = nullptr;
1223 for (Node* child = lastChild(); child; child = child->previousSibling()) {
1224 if (child->isTextNode()) {
1225 toText(child)->recalcTextStyle(change, lastTextNode);
1226 lastTextNode = toText(child);
1227 } else if (child->isElementNode()) {
1228 Element* element = toElement(child);
1229 if (element->shouldCallRecalcStyle(change))
1230 element->recalcStyle(change, lastTextNode);
1231 else if (element->supportsStyleSharing())
1232 styleResolver.addToStyleSharingList(*element);
1233 if (element->renderer())
1234 lastTextNode = nullptr;
1239 void ContainerNode::checkForChildrenAdjacentRuleChanges()
1241 bool hasDirectAdjacentRules = childrenAffectedByDirectAdjacentRules();
1242 bool hasIndirectAdjacentRules = childrenAffectedByIndirectAdjacentRules();
1244 if (!hasDirectAdjacentRules && !hasIndirectAdjacentRules)
1247 unsigned forceCheckOfNextElementCount = 0;
1248 bool forceCheckOfAnyElementSibling = false;
1249 Document& document = this->document();
1251 for (Element* child = ElementTraversal::firstChild(*this); child; child = ElementTraversal::nextSibling(*child)) {
1252 bool childRulesChanged = child->needsStyleRecalc() && child->styleChangeType() >= SubtreeStyleChange;
1254 if (forceCheckOfNextElementCount || forceCheckOfAnyElementSibling)
1255 child->setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::SiblingSelector));
1257 if (childRulesChanged && hasDirectAdjacentRules)
1258 forceCheckOfNextElementCount = document.styleEngine()->maxDirectAdjacentSelectors();
1259 else if (forceCheckOfNextElementCount)
1260 --forceCheckOfNextElementCount;
1262 forceCheckOfAnyElementSibling = forceCheckOfAnyElementSibling || (childRulesChanged && hasIndirectAdjacentRules);
1266 void ContainerNode::checkForSiblingStyleChanges(SiblingCheckType changeType, Node* nodeBeforeChange, Node* nodeAfterChange)
1268 if (!inActiveDocument() || document().hasPendingForcedStyleRecalc() || styleChangeType() >= SubtreeStyleChange)
1271 if (needsStyleRecalc() && childrenAffectedByPositionalRules())
1274 // Forward positional selectors include nth-child, nth-of-type, first-of-type and only-of-type.
1275 // The indirect adjacent selector is the ~ selector.
1276 // Backward positional selectors include nth-last-child, nth-last-of-type, last-of-type and only-of-type.
1277 // We have to invalidate everything following the insertion point in the forward and indirect adjacent case,
1278 // and everything before the insertion point in the backward case.
1279 // |afterChange| is 0 in the parser callback case, so we won't do any work for the forward case if we don't have to.
1280 // For performance reasons we just mark the parent node as changed, since we don't want to make childrenChanged O(n^2) by crawling all our kids
1281 // here. recalcStyle will then force a walk of the children when it sees that this has happened.
1282 if (((childrenAffectedByForwardPositionalRules() || childrenAffectedByIndirectAdjacentRules()) && nodeAfterChange)
1283 || (childrenAffectedByBackwardPositionalRules() && nodeBeforeChange)) {
1284 setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::SiblingSelector));
1288 // :first-child. In the parser callback case, we don't have to check anything, since we were right the first time.
1289 // In the DOM case, we only need to do something if |afterChange| is not 0.
1290 // |afterChange| is 0 in the parser case, so it works out that we'll skip this block.
1291 if (childrenAffectedByFirstChildRules() && nodeAfterChange) {
1292 ASSERT(changeType != FinishedParsingChildren);
1293 // Find our new first child element.
1294 Element* firstChildElement = ElementTraversal::firstChild(*this);
1295 RenderStyle* firstChildElementStyle = firstChildElement ? firstChildElement->renderStyle() : nullptr;
1297 // Find the first element after the change.
1298 Element* elementAfterChange = nodeAfterChange->isElementNode() ? toElement(nodeAfterChange) : ElementTraversal::nextSibling(*nodeAfterChange);
1299 RenderStyle* elementAfterChangeStyle = elementAfterChange ? elementAfterChange->renderStyle() : nullptr;
1301 // This is the element insertion as first child element case.
1302 if (firstChildElement != elementAfterChange && elementAfterChangeStyle && elementAfterChangeStyle->firstChildState()) {
1303 ASSERT(changeType == SiblingElementInserted);
1304 elementAfterChange->setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::SiblingSelector));
1307 // This is the first child element removal case.
1308 if (changeType == SiblingElementRemoved && firstChildElement == elementAfterChange && firstChildElement && (!firstChildElementStyle || !firstChildElementStyle->firstChildState()))
1309 firstChildElement->setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::SiblingSelector));
1312 // :last-child. In the parser callback case, we don't have to check anything, since we were right the first time.
1313 // In the DOM case, we only need to do something if |afterChange| is not 0.
1314 if (childrenAffectedByLastChildRules() && nodeBeforeChange) {
1315 // Find our new last child element.
1316 Element* lastChildElement = ElementTraversal::lastChild(*this);
1317 RenderStyle* lastChildElementStyle = lastChildElement ? lastChildElement->renderStyle() : nullptr;
1319 // Find the last element before the change.
1320 Element* elementBeforeChange = nodeBeforeChange->isElementNode() ? toElement(nodeBeforeChange) : ElementTraversal::previousSibling(*nodeBeforeChange);
1321 RenderStyle* elementBeforeChangeStyle = elementBeforeChange ? elementBeforeChange->renderStyle() : nullptr;
1323 // This is the element insertion as last child element case.
1324 if (lastChildElement != elementBeforeChange && elementBeforeChangeStyle && elementBeforeChangeStyle->lastChildState()) {
1325 ASSERT(SiblingElementInserted);
1326 elementBeforeChange->setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::SiblingSelector));
1329 // This is the last child element removal case. The parser callback case is similar to node removal as well in that we need to change the last child
1331 if ((changeType == SiblingElementRemoved || changeType == FinishedParsingChildren) && lastChildElement == elementBeforeChange && lastChildElement && (!lastChildElementStyle || !lastChildElementStyle->lastChildState()))
1332 lastChildElement->setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::SiblingSelector));
1335 // The + selector. We need to invalidate the first element following the change. It is the only possible element
1336 // that could be affected by this DOM change.
1337 if (childrenAffectedByDirectAdjacentRules() && nodeAfterChange) {
1338 if (Element* elementAfterChange = nodeAfterChange->isElementNode() ? toElement(nodeAfterChange) : ElementTraversal::nextSibling(*nodeAfterChange))
1339 elementAfterChange->setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::SiblingSelector));
1343 void ContainerNode::invalidateNodeListCachesInAncestors(const QualifiedName* attrName, Element* attributeOwnerElement)
1345 if (hasRareData() && (!attrName || isAttributeNode())) {
1346 if (NodeListsNodeData* lists = rareData()->nodeLists()) {
1347 if (ChildNodeList* childNodeList = lists->childNodeList(*this))
1348 childNodeList->invalidateCache();
1352 // Modifications to attributes that are not associated with an Element can't invalidate NodeList caches.
1353 if (attrName && !attributeOwnerElement)
1356 if (!document().shouldInvalidateNodeListCaches(attrName))
1359 document().invalidateNodeListCaches(attrName);
1361 for (ContainerNode* node = this; node; node = node->parentNode()) {
1362 if (NodeListsNodeData* lists = node->nodeLists())
1363 lists->invalidateCaches(attrName);
1367 PassRefPtrWillBeRawPtr<TagCollection> ContainerNode::getElementsByTagName(const AtomicString& localName)
1369 if (localName.isNull())
1372 if (document().isHTMLDocument())
1373 return ensureCachedCollection<HTMLTagCollection>(HTMLTagCollectionType, localName);
1374 return ensureCachedCollection<TagCollection>(TagCollectionType, localName);
1377 PassRefPtrWillBeRawPtr<TagCollection> ContainerNode::getElementsByTagNameNS(const AtomicString& namespaceURI, const AtomicString& localName)
1379 if (localName.isNull())
1382 if (namespaceURI == starAtom)
1383 return getElementsByTagName(localName);
1385 return ensureCachedCollection<TagCollection>(TagCollectionType, namespaceURI.isEmpty() ? nullAtom : namespaceURI, localName);
1388 // Takes an AtomicString in argument because it is common for elements to share the same name attribute.
1389 // Therefore, the NameNodeList factory function expects an AtomicString type.
1390 PassRefPtrWillBeRawPtr<NameNodeList> ContainerNode::getElementsByName(const AtomicString& elementName)
1392 return ensureCachedCollection<NameNodeList>(NameNodeListType, elementName);
1395 // Takes an AtomicString in argument because it is common for elements to share the same set of class names.
1396 // Therefore, the ClassNodeList factory function expects an AtomicString type.
1397 PassRefPtrWillBeRawPtr<ClassCollection> ContainerNode::getElementsByClassName(const AtomicString& classNames)
1399 return ensureCachedCollection<ClassCollection>(ClassCollectionType, classNames);
1402 PassRefPtrWillBeRawPtr<RadioNodeList> ContainerNode::radioNodeList(const AtomicString& name, bool onlyMatchImgElements)
1404 ASSERT(isHTMLFormElement(this) || isHTMLFieldSetElement(this));
1405 CollectionType type = onlyMatchImgElements ? RadioImgNodeListType : RadioNodeListType;
1406 return ensureCachedCollection<RadioNodeList>(type, name);
1409 Element* ContainerNode::getElementById(const AtomicString& id) const
1411 if (isInTreeScope()) {
1412 // Fast path if we are in a tree scope: call getElementById() on tree scope
1413 // and check if the matching element is in our subtree.
1414 Element* element = treeScope().getElementById(id);
1417 if (element->isDescendantOf(this))
1421 // Fall back to traversing our subtree. In case of duplicate ids, the first element found will be returned.
1422 for (Element& element : ElementTraversal::descendantsOf(*this)) {
1423 if (element.getIdAttribute() == id)
1429 NodeListsNodeData& ContainerNode::ensureNodeLists()
1431 return ensureRareData().ensureNodeLists();
1435 bool childAttachedAllowedWhenAttachingChildren(ContainerNode* node)
1437 if (node->isShadowRoot())
1440 if (node->isInsertionPoint())
1443 if (node->isElementNode() && toElement(node)->shadow())
1450 } // namespace blink