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/NodeComputedStyle.h"
35 #include "core/dom/NodeRareData.h"
36 #include "core/dom/NodeTraversal.h"
37 #include "core/dom/NthIndexCache.h"
38 #include "core/dom/SelectorQuery.h"
39 #include "core/dom/StaticNodeList.h"
40 #include "core/dom/StyleEngine.h"
41 #include "core/dom/shadow/ElementShadow.h"
42 #include "core/dom/shadow/ShadowRoot.h"
43 #include "core/events/MutationEvent.h"
44 #include "core/html/HTMLCollection.h"
45 #include "core/html/HTMLFrameOwnerElement.h"
46 #include "core/html/HTMLTagCollection.h"
47 #include "core/html/RadioNodeList.h"
48 #include "core/inspector/InspectorInstrumentation.h"
49 #include "core/layout/LayoutInline.h"
50 #include "core/layout/LayoutText.h"
51 #include "core/layout/LayoutTheme.h"
52 #include "core/layout/LayoutView.h"
53 #include "core/layout/line/InlineTextBox.h"
54 #include "platform/EventDispatchForbiddenScope.h"
55 #include "platform/ScriptForbiddenScope.h"
59 using namespace HTMLNames
;
61 static void dispatchChildInsertionEvents(Node
&);
62 static void dispatchChildRemovalEvents(Node
&);
64 static void collectChildrenAndRemoveFromOldParent(Node
& node
, NodeVector
& nodes
, ExceptionState
& exceptionState
)
66 if (node
.isDocumentFragment()) {
67 DocumentFragment
& fragment
= toDocumentFragment(node
);
68 getChildNodes(fragment
, nodes
);
69 fragment
.removeChildren();
73 if (ContainerNode
* oldParent
= node
.parentNode())
74 oldParent
->removeChild(&node
, exceptionState
);
78 void ContainerNode::removeDetachedChildren()
80 ASSERT(!connectedSubframeCount());
81 ASSERT(needsAttach());
82 removeDetachedChildrenInContainer(*this);
86 void ContainerNode::parserTakeAllChildrenFrom(ContainerNode
& oldParent
)
88 while (RefPtrWillBeRawPtr
<Node
> child
= oldParent
.firstChild()) {
89 // Explicitly remove since appending can fail, but this loop shouldn't be infinite.
90 oldParent
.parserRemoveChild(*child
);
91 parserAppendChild(child
.get());
95 ContainerNode::~ContainerNode()
97 ASSERT(needsAttach());
99 willBeDeletedFromDocument();
100 removeDetachedChildren();
104 bool ContainerNode::isChildTypeAllowed(const Node
& child
) const
106 if (!child
.isDocumentFragment())
107 return childTypeAllowed(child
.nodeType());
109 for (Node
* node
= toDocumentFragment(child
).firstChild(); node
; node
= node
->nextSibling()) {
110 if (!childTypeAllowed(node
->nodeType()))
116 bool ContainerNode::containsConsideringHostElements(const Node
& newChild
) const
118 if (isInShadowTree() || document().isTemplateDocument())
119 return newChild
.containsIncludingHostElements(*this);
120 return newChild
.contains(this);
123 bool ContainerNode::checkAcceptChild(const Node
* newChild
, const Node
* oldChild
, ExceptionState
& exceptionState
) const
125 // Not mentioned in spec: throw NotFoundError if newChild is null
127 exceptionState
.throwDOMException(NotFoundError
, "The new child element is null.");
131 // Use common case fast path if possible.
132 if ((newChild
->isElementNode() || newChild
->isTextNode()) && isElementNode()) {
133 ASSERT(isChildTypeAllowed(*newChild
));
134 if (containsConsideringHostElements(*newChild
)) {
135 exceptionState
.throwDOMException(HierarchyRequestError
, "The new child element contains the parent.");
141 // This should never happen, but also protect release builds from tree corruption.
142 ASSERT(!newChild
->isPseudoElement());
143 if (newChild
->isPseudoElement()) {
144 exceptionState
.throwDOMException(HierarchyRequestError
, "The new child element is a pseudo-element.");
148 return checkAcceptChildGuaranteedNodeTypes(*newChild
, oldChild
, exceptionState
);
151 bool ContainerNode::checkAcceptChildGuaranteedNodeTypes(const Node
& newChild
, const Node
* oldChild
, ExceptionState
& exceptionState
) const
153 if (isDocumentNode())
154 return toDocument(this)->canAcceptChild(newChild
, oldChild
, exceptionState
);
155 if (newChild
.containsIncludingHostElements(*this)) {
156 exceptionState
.throwDOMException(HierarchyRequestError
, "The new child element contains the parent.");
159 if (!isChildTypeAllowed(newChild
)) {
160 exceptionState
.throwDOMException(HierarchyRequestError
, "Nodes of type '" + newChild
.nodeName() + "' may not be inserted inside nodes of type '" + nodeName() + "'.");
166 PassRefPtrWillBeRawPtr
<Node
> ContainerNode::insertBefore(PassRefPtrWillBeRawPtr
<Node
> newChild
, Node
* refChild
, ExceptionState
& exceptionState
)
169 // Check that this node is not "floating".
170 // If it is, it can be deleted as a side effect of sending mutation events.
171 ASSERT(refCount() || parentOrShadowHostNode());
174 RefPtrWillBeRawPtr
<Node
> protect(this);
176 // insertBefore(node, 0) is equivalent to appendChild(node)
178 return appendChild(newChild
, exceptionState
);
181 // Make sure adding the new child is OK.
182 if (!checkAcceptChild(newChild
.get(), 0, exceptionState
)) {
183 if (exceptionState
.hadException())
189 // NotFoundError: Raised if refChild is not a child of this node
190 if (refChild
->parentNode() != this) {
191 exceptionState
.throwDOMException(NotFoundError
, "The node before which the new node is to be inserted is not a child of this node.");
196 if (refChild
->previousSibling() == newChild
|| refChild
== newChild
)
199 RefPtrWillBeRawPtr
<Node
> next
= refChild
;
202 collectChildrenAndRemoveFromOldParent(*newChild
, targets
, exceptionState
);
203 if (exceptionState
.hadException())
205 if (targets
.isEmpty())
208 // We need this extra check because collectChildrenAndRemoveFromOldParent() can fire mutation events.
209 if (!checkAcceptChildGuaranteedNodeTypes(*newChild
, nullptr, exceptionState
)) {
210 if (exceptionState
.hadException())
215 InspectorInstrumentation::willInsertDOMNode(this);
217 ChildListMutationScope
mutation(*this);
218 for (const auto& targetNode
: targets
) {
220 Node
& child
= *targetNode
;
222 // Due to arbitrary code running in response to a DOM mutation event it's
223 // possible that "next" is no longer a child of "this".
224 // It's also possible that "child" has been inserted elsewhere.
225 // In either of those cases, we'll just stop.
226 if (next
->parentNode() != this)
228 if (child
.parentNode())
232 EventDispatchForbiddenScope assertNoEventDispatch
;
233 ScriptForbiddenScope forbidScript
;
235 treeScope().adoptIfNeeded(child
);
236 insertBeforeCommon(*next
, child
);
239 updateTreeAfterInsertion(child
);
242 dispatchSubtreeModifiedEvent();
247 void ContainerNode::insertBeforeCommon(Node
& nextChild
, Node
& newChild
)
249 EventDispatchForbiddenScope assertNoEventDispatch
;
250 ScriptForbiddenScope forbidScript
;
252 ASSERT(!newChild
.parentNode()); // Use insertBefore if you need to handle reparenting (and want DOM mutation events).
253 ASSERT(!newChild
.nextSibling());
254 ASSERT(!newChild
.previousSibling());
255 ASSERT(!newChild
.isShadowRoot());
257 Node
* prev
= nextChild
.previousSibling();
258 ASSERT(m_lastChild
!= prev
);
259 nextChild
.setPreviousSibling(&newChild
);
261 ASSERT(firstChild() != nextChild
);
262 ASSERT(prev
->nextSibling() == nextChild
);
263 prev
->setNextSibling(&newChild
);
265 ASSERT(firstChild() == nextChild
);
266 m_firstChild
= &newChild
;
268 newChild
.setParentOrShadowHostNode(this);
269 newChild
.setPreviousSibling(prev
);
270 newChild
.setNextSibling(&nextChild
);
273 void ContainerNode::appendChildCommon(Node
& child
)
275 child
.setParentOrShadowHostNode(this);
278 child
.setPreviousSibling(m_lastChild
);
279 m_lastChild
->setNextSibling(&child
);
281 setFirstChild(&child
);
284 setLastChild(&child
);
287 bool ContainerNode::checkParserAcceptChild(const Node
& newChild
) const
289 if (!isDocumentNode())
291 // TODO(esprehn): Are there other conditions where the parser can create
293 return toDocument(*this).canAcceptChild(newChild
, nullptr, IGNORE_EXCEPTION
);
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 if (!checkParserAcceptChild(*newChild
))
309 RefPtrWillBeRawPtr
<Node
> protect(this);
311 // FIXME: parserRemoveChild can run script which could then insert the
312 // newChild back into the page. Loop until the child is actually removed.
313 // See: fast/parser/execute-script-during-adoption-agency-removal.html
314 while (RefPtrWillBeRawPtr
<ContainerNode
> parent
= newChild
->parentNode())
315 parent
->parserRemoveChild(*newChild
);
317 if (nextChild
.parentNode() != this)
320 if (document() != newChild
->document())
321 document().adoptNode(newChild
.get(), ASSERT_NO_EXCEPTION
);
324 EventDispatchForbiddenScope assertNoEventDispatch
;
325 ScriptForbiddenScope forbidScript
;
327 treeScope().adoptIfNeeded(*newChild
);
328 insertBeforeCommon(nextChild
, *newChild
);
329 newChild
->updateAncestorConnectedSubframeCountForInsertion();
330 ChildListMutationScope(*this).childAdded(*newChild
);
333 notifyNodeInserted(*newChild
, ChildrenChangeSourceParser
);
336 PassRefPtrWillBeRawPtr
<Node
> ContainerNode::replaceChild(PassRefPtrWillBeRawPtr
<Node
> newChild
, PassRefPtrWillBeRawPtr
<Node
> oldChild
, ExceptionState
& exceptionState
)
339 // Check that this node is not "floating".
340 // If it is, it can be deleted as a side effect of sending mutation events.
341 ASSERT(refCount() || parentOrShadowHostNode());
344 RefPtrWillBeRawPtr
<Node
> protect(this);
346 if (oldChild
== newChild
) // Nothing to do.
350 exceptionState
.throwDOMException(NotFoundError
, "The node to be replaced is null.");
354 RefPtrWillBeRawPtr
<Node
> child
= oldChild
;
356 // Make sure replacing the old child with the new is OK.
357 if (!checkAcceptChild(newChild
.get(), child
.get(), exceptionState
)) {
358 if (exceptionState
.hadException())
363 // NotFoundError: Raised if oldChild is not a child of this node.
364 if (child
->parentNode() != this) {
365 exceptionState
.throwDOMException(NotFoundError
, "The node to be replaced is not a child of this node.");
369 ChildListMutationScope
mutation(*this);
371 RefPtrWillBeRawPtr
<Node
> next
= child
->nextSibling();
373 // Remove the node we're replacing.
374 removeChild(child
, exceptionState
);
375 if (exceptionState
.hadException())
378 if (next
&& (next
->previousSibling() == newChild
|| next
== newChild
)) // nothing to do
381 // Does this one more time because removeChild() fires a MutationEvent.
382 if (!checkAcceptChild(newChild
.get(), child
.get(), exceptionState
)) {
383 if (exceptionState
.hadException())
389 collectChildrenAndRemoveFromOldParent(*newChild
, targets
, exceptionState
);
390 if (exceptionState
.hadException())
393 // Does this yet another check because collectChildrenAndRemoveFromOldParent() fires a MutationEvent.
394 if (!checkAcceptChild(newChild
.get(), child
.get(), exceptionState
)) {
395 if (exceptionState
.hadException())
400 InspectorInstrumentation::willInsertDOMNode(this);
402 // Add the new child(ren).
403 for (const auto& targetNode
: targets
) {
405 Node
& child
= *targetNode
;
407 // Due to arbitrary code running in response to a DOM mutation event it's
408 // possible that "next" is no longer a child of "this".
409 // It's also possible that "child" has been inserted elsewhere.
410 // In either of those cases, we'll just stop.
411 if (next
&& next
->parentNode() != this)
413 if (child
.parentNode())
416 treeScope().adoptIfNeeded(child
);
418 // Add child before "next".
420 EventDispatchForbiddenScope assertNoEventDispatch
;
422 insertBeforeCommon(*next
, child
);
424 appendChildCommon(child
);
427 updateTreeAfterInsertion(child
);
430 dispatchSubtreeModifiedEvent();
434 void ContainerNode::willRemoveChild(Node
& child
)
436 ASSERT(child
.parentNode() == this);
437 ChildListMutationScope(*this).willRemoveChild(child
);
438 child
.notifyMutationObserversNodeWillDetach();
439 dispatchChildRemovalEvents(child
);
440 ChildFrameDisconnector(child
).disconnect();
441 if (document() != child
.document()) {
442 // |child| was moved another document by DOM mutation event handler.
446 // |nodeWillBeRemoved()| must be run after |ChildFrameDisconnector|, because
447 // |ChildFrameDisconnector| can run script which may cause state that is to
448 // be invalidated by removing the node.
449 ScriptForbiddenScope scriptForbiddenScope
;
450 EventDispatchForbiddenScope assertNoEventDispatch
;
451 // e.g. mutation event listener can create a new range.
452 document().nodeWillBeRemoved(child
);
455 void ContainerNode::willRemoveChildren()
458 getChildNodes(*this, children
);
460 ChildListMutationScope
mutation(*this);
461 for (const auto& node
: children
) {
464 mutation
.willRemoveChild(child
);
465 child
.notifyMutationObserversNodeWillDetach();
466 dispatchChildRemovalEvents(child
);
469 ChildFrameDisconnector(*this).disconnect(ChildFrameDisconnector::DescendantsOnly
);
473 void ContainerNode::removeDetachedChildrenInContainer(ContainerNode
& container
)
475 // List of nodes to be deleted.
476 Node
* head
= nullptr;
477 Node
* tail
= nullptr;
479 addChildNodesToDeletionQueue(head
, tail
, container
);
485 ASSERT_WITH_SECURITY_IMPLICATION(n
->m_deletionHasBegun
);
487 next
= n
->nextSibling();
488 n
->setNextSibling(nullptr);
494 if (n
->hasChildren())
495 addChildNodesToDeletionQueue(head
, tail
, toContainerNode(*n
));
501 void ContainerNode::addChildNodesToDeletionQueue(Node
*& head
, Node
*& tail
, ContainerNode
& container
)
503 // We have to tell all children that their parent has died.
504 Node
* next
= nullptr;
505 for (Node
* n
= container
.firstChild(); n
; n
= next
) {
506 ASSERT_WITH_SECURITY_IMPLICATION(!n
->m_deletionHasBegun
);
508 next
= n
->nextSibling();
509 n
->setNextSibling(nullptr);
510 n
->setParentOrShadowHostNode(nullptr);
511 container
.setFirstChild(next
);
513 next
->setPreviousSibling(nullptr);
515 if (!n
->refCount()) {
517 container
.document().decrementNodeCount();
519 #if ENABLE(SECURITY_ASSERT)
520 n
->m_deletionHasBegun
= true;
522 // Add the node to the list of nodes to be deleted.
523 // Reuse the nextSibling pointer for this purpose.
525 tail
->setNextSibling(n
);
531 RefPtrWillBeRawPtr
<Node
> protect(n
); // removedFromDocument may remove all references to this node.
532 container
.document().adoptIfNeeded(*n
);
534 container
.notifyNodeRemoved(*n
);
538 container
.setLastChild(nullptr);
542 void ContainerNode::disconnectDescendantFrames()
544 ChildFrameDisconnector(*this).disconnect();
547 DEFINE_TRACE(ContainerNode
)
549 visitor
->trace(m_firstChild
);
550 visitor
->trace(m_lastChild
);
551 Node::trace(visitor
);
554 PassRefPtrWillBeRawPtr
<Node
> ContainerNode::removeChild(PassRefPtrWillBeRawPtr
<Node
> oldChild
, ExceptionState
& exceptionState
)
557 // Check that this node is not "floating".
558 // If it is, it can be deleted as a side effect of sending mutation events.
559 ASSERT(refCount() || parentOrShadowHostNode());
562 RefPtrWillBeRawPtr
<Node
> protect(this);
564 // NotFoundError: Raised if oldChild is not a child of this node.
565 // FIXME: We should never really get PseudoElements in here, but editing will sometimes
566 // attempt to remove them still. We should fix that and enable this ASSERT.
567 // ASSERT(!oldChild->isPseudoElement())
568 if (!oldChild
|| oldChild
->parentNode() != this || oldChild
->isPseudoElement()) {
569 exceptionState
.throwDOMException(NotFoundError
, "The node to be removed is not a child of this node.");
573 RefPtrWillBeRawPtr
<Node
> child
= oldChild
;
575 document().removeFocusedElementOfSubtree(child
.get());
577 // Events fired when blurring currently focused node might have moved this
578 // child into a different parent.
579 if (child
->parentNode() != this) {
580 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?");
584 willRemoveChild(*child
);
586 // Mutation events might have moved this child into a different parent.
587 if (child
->parentNode() != this) {
588 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?");
593 HTMLFrameOwnerElement::UpdateSuspendScope suspendWidgetHierarchyUpdates
;
595 Node
* prev
= child
->previousSibling();
596 Node
* next
= child
->nextSibling();
597 removeBetween(prev
, next
, *child
);
598 notifyNodeRemoved(*child
);
599 childrenChanged(ChildrenChange::forRemoval(*child
, prev
, next
, ChildrenChangeSourceAPI
));
601 dispatchSubtreeModifiedEvent();
605 void ContainerNode::removeBetween(Node
* previousChild
, Node
* nextChild
, Node
& oldChild
)
607 EventDispatchForbiddenScope assertNoEventDispatch
;
609 ASSERT(oldChild
.parentNode() == this);
611 if (!oldChild
.needsAttach())
615 nextChild
->setPreviousSibling(previousChild
);
617 previousChild
->setNextSibling(nextChild
);
618 if (m_firstChild
== &oldChild
)
619 m_firstChild
= nextChild
;
620 if (m_lastChild
== &oldChild
)
621 m_lastChild
= previousChild
;
623 oldChild
.setPreviousSibling(nullptr);
624 oldChild
.setNextSibling(nullptr);
625 oldChild
.setParentOrShadowHostNode(nullptr);
627 document().adoptIfNeeded(oldChild
);
630 void ContainerNode::parserRemoveChild(Node
& oldChild
)
632 ASSERT(oldChild
.parentNode() == this);
633 ASSERT(!oldChild
.isDocumentFragment());
635 // This may cause arbitrary Javascript execution via onunload handlers.
636 if (oldChild
.connectedSubframeCount())
637 ChildFrameDisconnector(oldChild
).disconnect();
639 if (oldChild
.parentNode() != this)
642 ChildListMutationScope(*this).willRemoveChild(oldChild
);
643 oldChild
.notifyMutationObserversNodeWillDetach();
645 Node
* prev
= oldChild
.previousSibling();
646 Node
* next
= oldChild
.nextSibling();
647 removeBetween(prev
, next
, oldChild
);
649 notifyNodeRemoved(oldChild
);
650 childrenChanged(ChildrenChange::forRemoval(oldChild
, prev
, next
, ChildrenChangeSourceParser
));
653 // This differs from other remove functions because it forcibly removes all the children,
654 // regardless of read-only status or event exceptions, e.g.
655 void ContainerNode::removeChildren(SubtreeModificationAction action
)
660 // The container node can be removed from event handlers.
661 RefPtrWillBeRawPtr
<ContainerNode
> protect(this);
663 // Do any prep work needed before actually starting to detach
664 // and remove... e.g. stop loading frames, fire unload events.
665 willRemoveChildren();
668 // Removing focus can cause frames to load, either via events (focusout, blur)
669 // or widget updates (e.g., for <embed>).
670 SubframeLoadingDisabler
disabler(*this);
672 // Exclude this node when looking for removed focusedElement since only
673 // children will be removed.
674 // This must be later than willRemoveChildren, which might change focus
676 document().removeFocusedElementOfSubtree(this, true);
678 // Removing a node from a selection can cause widget updates.
679 document().nodeChildrenWillBeRemoved(*this);
683 // FIXME: Remove this NodeVector. Right now WebPluginContainerImpl::m_element is a
684 // raw ptr which means the code below can drop the last ref to a plugin element and
685 // then the code in UpdateSuspendScope::performDeferredWidgetTreeOperations will
686 // try to destroy the plugin which will be a use-after-free. We should use a RefPtr
687 // in the WebPluginContainerImpl instead.
688 NodeVector removedChildren
;
691 HTMLFrameOwnerElement::UpdateSuspendScope suspendWidgetHierarchyUpdates
;
694 EventDispatchForbiddenScope assertNoEventDispatch
;
695 ScriptForbiddenScope forbidScript
;
698 removedChildren
.reserveInitialCapacity(countChildren());
700 while (RefPtrWillBeRawPtr
<Node
> child
= m_firstChild
) {
701 removeBetween(0, child
->nextSibling(), *child
);
703 removedChildren
.append(child
.get());
705 notifyNodeRemoved(*child
);
709 ChildrenChange change
= {AllChildrenRemoved
, nullptr, nullptr, ChildrenChangeSourceAPI
};
710 childrenChanged(change
);
713 if (action
== DispatchSubtreeModifiedEvent
)
714 dispatchSubtreeModifiedEvent();
717 PassRefPtrWillBeRawPtr
<Node
> ContainerNode::appendChild(PassRefPtrWillBeRawPtr
<Node
> newChild
, ExceptionState
& exceptionState
)
719 RefPtrWillBeRawPtr
<ContainerNode
> protect(this);
722 // Check that this node is not "floating".
723 // If it is, it can be deleted as a side effect of sending mutation events.
724 ASSERT(refCount() || parentOrShadowHostNode());
727 // Make sure adding the new child is ok
728 if (!checkAcceptChild(newChild
.get(), 0, exceptionState
)) {
729 if (exceptionState
.hadException())
735 if (newChild
== m_lastChild
) // nothing to do
739 collectChildrenAndRemoveFromOldParent(*newChild
, targets
, exceptionState
);
740 if (exceptionState
.hadException())
743 if (targets
.isEmpty())
746 // We need this extra check because collectChildrenAndRemoveFromOldParent() can fire mutation events.
747 if (!checkAcceptChildGuaranteedNodeTypes(*newChild
, nullptr, exceptionState
)) {
748 if (exceptionState
.hadException())
753 InspectorInstrumentation::willInsertDOMNode(this);
755 // Now actually add the child(ren).
756 ChildListMutationScope
mutation(*this);
757 for (const auto& targetNode
: targets
) {
759 Node
& child
= *targetNode
;
761 // If the child has a parent again, just stop what we're doing, because
762 // that means someone is doing something with DOM mutation -- can't re-parent
763 // a child that already has a parent.
764 if (child
.parentNode())
768 EventDispatchForbiddenScope assertNoEventDispatch
;
769 ScriptForbiddenScope forbidScript
;
771 treeScope().adoptIfNeeded(child
);
772 appendChildCommon(child
);
775 updateTreeAfterInsertion(child
);
778 dispatchSubtreeModifiedEvent();
782 void ContainerNode::parserAppendChild(PassRefPtrWillBeRawPtr
<Node
> newChild
)
785 ASSERT(!newChild
->isDocumentFragment());
786 ASSERT(!isHTMLTemplateElement(this));
788 if (!checkParserAcceptChild(*newChild
))
791 RefPtrWillBeRawPtr
<Node
> protect(this);
793 // FIXME: parserRemoveChild can run script which could then insert the
794 // newChild back into the page. Loop until the child is actually removed.
795 // See: fast/parser/execute-script-during-adoption-agency-removal.html
796 while (RefPtrWillBeRawPtr
<ContainerNode
> parent
= newChild
->parentNode())
797 parent
->parserRemoveChild(*newChild
);
799 if (document() != newChild
->document())
800 document().adoptNode(newChild
.get(), ASSERT_NO_EXCEPTION
);
803 EventDispatchForbiddenScope assertNoEventDispatch
;
804 ScriptForbiddenScope forbidScript
;
806 treeScope().adoptIfNeeded(*newChild
);
807 appendChildCommon(*newChild
);
808 newChild
->updateAncestorConnectedSubframeCountForInsertion();
809 ChildListMutationScope(*this).childAdded(*newChild
);
812 notifyNodeInserted(*newChild
, ChildrenChangeSourceParser
);
815 void ContainerNode::notifyNodeInserted(Node
& root
, ChildrenChangeSource source
)
817 ASSERT(!EventDispatchForbiddenScope::isEventDispatchForbidden());
818 ASSERT(!root
.isShadowRoot());
820 InspectorInstrumentation::didInsertDOMNode(&root
);
822 RefPtrWillBeRawPtr
<Node
> protect(this);
823 RefPtrWillBeRawPtr
<Node
> protectNode(root
);
825 NodeVector postInsertionNotificationTargets
;
826 notifyNodeInsertedInternal(root
, postInsertionNotificationTargets
);
828 childrenChanged(ChildrenChange::forInsertion(root
, source
));
830 for (const auto& targetNode
: postInsertionNotificationTargets
) {
831 if (targetNode
->inDocument())
832 targetNode
->didNotifySubtreeInsertionsToDocument();
836 void ContainerNode::notifyNodeInsertedInternal(Node
& root
, NodeVector
& postInsertionNotificationTargets
)
838 EventDispatchForbiddenScope assertNoEventDispatch
;
839 ScriptForbiddenScope forbidScript
;
841 for (Node
& node
: NodeTraversal::inclusiveDescendantsOf(root
)) {
842 // As an optimization we don't notify leaf nodes when when inserting
843 // into detached subtrees.
844 if (!inDocument() && !node
.isContainerNode())
846 if (Node::InsertionShouldCallDidNotifySubtreeInsertions
== node
.insertedInto(this))
847 postInsertionNotificationTargets
.append(&node
);
848 for (ShadowRoot
* shadowRoot
= node
.youngestShadowRoot(); shadowRoot
; shadowRoot
= shadowRoot
->olderShadowRoot())
849 notifyNodeInsertedInternal(*shadowRoot
, postInsertionNotificationTargets
);
853 void ContainerNode::notifyNodeRemoved(Node
& root
)
855 ScriptForbiddenScope forbidScript
;
856 EventDispatchForbiddenScope assertNoEventDispatch
;
858 for (Node
& node
: NodeTraversal::inclusiveDescendantsOf(root
)) {
859 // As an optimization we skip notifying Text nodes and other leaf nodes
860 // of removal when they're not in the Document tree and not in a shadow root since the virtual
861 // call to removedFrom is not needed.
862 if (!node
.isContainerNode() && !node
.isInTreeScope())
864 node
.removedFrom(this);
865 for (ShadowRoot
* shadowRoot
= node
.youngestShadowRoot(); shadowRoot
; shadowRoot
= shadowRoot
->olderShadowRoot())
866 notifyNodeRemoved(*shadowRoot
);
870 void ContainerNode::attach(const AttachContext
& context
)
872 attachChildren(context
);
873 clearChildNeedsStyleRecalc();
874 Node::attach(context
);
877 void ContainerNode::detach(const AttachContext
& context
)
879 detachChildren(context
);
880 setChildNeedsStyleRecalc();
881 Node::detach(context
);
884 void ContainerNode::childrenChanged(const ChildrenChange
& change
)
886 document().incDOMTreeVersion();
887 if (!change
.byParser
&& change
.type
!= TextChanged
)
888 document().updateRangesAfterChildrenChanged(this);
889 invalidateNodeListCachesInAncestors();
890 if (change
.isChildInsertion() && !childNeedsStyleRecalc()) {
891 setChildNeedsStyleRecalc();
892 markAncestorsWithChildNeedsStyleRecalc();
896 void ContainerNode::cloneChildNodes(ContainerNode
*clone
)
898 TrackExceptionState exceptionState
;
899 for (Node
* n
= firstChild(); n
&& !exceptionState
.hadException(); n
= n
->nextSibling())
900 clone
->appendChild(n
->cloneNode(true), exceptionState
);
904 bool ContainerNode::getUpperLeftCorner(FloatPoint
& point
) const
909 // FIXME: What is this code really trying to do?
910 LayoutObject
* o
= layoutObject();
911 if (!o
->isInline() || o
->isReplaced()) {
912 point
= o
->localToAbsolute(FloatPoint(), UseTransforms
);
916 // Find the next text/image child, to get a position.
919 if (LayoutObject
* oFirstChild
= o
->slowFirstChild()) {
921 } else if (o
->nextSibling()) {
922 o
= o
->nextSibling();
924 LayoutObject
* next
= nullptr;
925 while (!next
&& o
->parent()) {
927 next
= o
->nextSibling();
936 if (!o
->isInline() || o
->isReplaced()) {
937 point
= o
->localToAbsolute(FloatPoint(), UseTransforms
);
941 if (p
->node() && p
->node() == this && o
->isText() && !o
->isBR() && !toLayoutText(o
)->firstTextBox()) {
942 // Do nothing - skip unrendered whitespace that is a child or next sibling of the anchor.
943 } else if ((o
->isText() && !o
->isBR()) || o
->isReplaced()) {
944 point
= FloatPoint();
945 if (o
->isText() && toLayoutText(o
)->firstTextBox()) {
946 point
.move(toLayoutText(o
)->linesBoundingBox().x(), toLayoutText(o
)->firstTextBox()->root().lineTop().toFloat());
947 point
= o
->localToAbsolute(point
, UseTransforms
);
948 } else if (o
->isBox()) {
949 LayoutBox
* box
= toLayoutBox(o
);
950 point
.moveBy(box
->location());
951 point
= o
->container()->localToAbsolute(point
, UseTransforms
);
957 // If the target doesn't have any children or siblings that could be used to calculate the scroll position, we must be
958 // at the end of the document. Scroll to the bottom. FIXME: who said anything about scrolling?
959 if (!o
&& document().view()) {
960 point
= FloatPoint(0, document().view()->contentsHeight());
966 static inline LayoutObject
* endOfContinuations(LayoutObject
* layoutObject
)
968 LayoutObject
* prev
= nullptr;
969 LayoutObject
* cur
= layoutObject
;
971 if (!cur
->isLayoutInline() && !cur
->isLayoutBlock())
976 if (cur
->isLayoutInline())
977 cur
= toLayoutInline(cur
)->continuation();
979 cur
= toLayoutBlock(cur
)->continuation();
985 bool ContainerNode::getLowerRightCorner(FloatPoint
& point
) const
990 LayoutObject
* o
= layoutObject();
991 if (!o
->isInline() || o
->isReplaced()) {
992 LayoutBox
* box
= toLayoutBox(o
);
993 point
= o
->localToAbsolute(FloatPoint(box
->size()), UseTransforms
);
997 LayoutObject
* startContinuation
= nullptr;
998 // Find the last text/image child, to get a position.
1000 if (LayoutObject
* oLastChild
= o
->slowLastChild()) {
1002 } else if (o
!= layoutObject() && o
->previousSibling()) {
1003 o
= o
->previousSibling();
1005 LayoutObject
* prev
= nullptr;
1007 // Check if the current layoutObject has contiunation and move the location for
1008 // finding the layoutObject to the end of continuations if there is the continuation.
1009 // Skip to check the contiunation on contiunations section
1010 if (startContinuation
== o
) {
1011 startContinuation
= nullptr;
1012 } else if (!startContinuation
) {
1013 if (LayoutObject
* continuation
= endOfContinuations(o
)) {
1014 startContinuation
= o
;
1015 prev
= continuation
;
1019 // Prevent to overrun out of own layout tree
1020 if (o
== layoutObject()) {
1026 prev
= o
->previousSibling();
1031 if (o
->isText() || o
->isReplaced()) {
1032 point
= FloatPoint();
1034 LayoutText
* text
= toLayoutText(o
);
1035 IntRect linesBox
= text
->linesBoundingBox();
1036 if (!linesBox
.maxX() && !linesBox
.maxY())
1038 point
.moveBy(linesBox
.maxXMaxYCorner());
1039 point
= o
->localToAbsolute(point
, UseTransforms
);
1041 LayoutBox
* box
= toLayoutBox(o
);
1042 point
.moveBy(box
->frameRect().maxXMaxYCorner());
1043 point
= o
->container()->localToAbsolute(point
, UseTransforms
);
1051 // FIXME: This override is only needed for inline anchors without an
1052 // InlineBox and it does not belong in ContainerNode as it reaches into
1053 // the layout and line box trees.
1054 // https://code.google.com/p/chromium/issues/detail?id=248354
1055 LayoutRect
ContainerNode::boundingBox() const
1057 FloatPoint upperLeft
, lowerRight
;
1058 bool foundUpperLeft
= getUpperLeftCorner(upperLeft
);
1059 bool foundLowerRight
= getLowerRightCorner(lowerRight
);
1061 // If we've found one corner, but not the other,
1062 // then we should just return a point at the corner that we did find.
1063 if (foundUpperLeft
!= foundLowerRight
) {
1065 lowerRight
= upperLeft
;
1067 upperLeft
= lowerRight
;
1070 return enclosingLayoutRect(FloatRect(upperLeft
, lowerRight
.expandedTo(upperLeft
) - upperLeft
));
1073 // This is used by FrameSelection to denote when the active-state of the page has changed
1074 // independent of the focused element changing.
1075 void ContainerNode::focusStateChanged()
1077 // If we're just changing the window's active state and the focused node has no
1078 // layoutObject we can just ignore the state change.
1079 if (!layoutObject())
1082 if (styleChangeType() < SubtreeStyleChange
) {
1083 if (computedStyle()->affectedByFocus() && computedStyle()->hasPseudoStyle(FIRST_LETTER
))
1084 setNeedsStyleRecalc(SubtreeStyleChange
, StyleChangeReasonForTracing::createWithExtraData(StyleChangeReason::PseudoClass
, StyleChangeExtraData::Focus
));
1085 else if (isElementNode() && toElement(this)->childrenOrSiblingsAffectedByFocus())
1086 document().styleEngine().pseudoStateChangedForElement(CSSSelector::PseudoFocus
, *toElement(this));
1087 else if (computedStyle()->affectedByFocus())
1088 setNeedsStyleRecalc(LocalStyleChange
, StyleChangeReasonForTracing::createWithExtraData(StyleChangeReason::PseudoClass
, StyleChangeExtraData::Focus
));
1091 LayoutTheme::theme().controlStateChanged(*layoutObject(), FocusControlState
);
1094 void ContainerNode::setFocus(bool received
)
1096 // Recurse up author shadow trees to mark shadow hosts if it matches :focus.
1097 // TODO(kochi): Handle UA shadows which marks multiple nodes as focused such as
1098 // <input type="date"> the same way as author shadow.
1099 if (ShadowRoot
* root
= containingShadowRoot()) {
1100 if (root
->type() != ShadowRootType::UserAgent
)
1101 shadowHost()->setFocus(received
);
1104 // If this is an author shadow host and indirectly focused (has focused element within
1105 // its shadow root), update focus.
1106 if (isElementNode() && document().focusedElement() && document().focusedElement() != this) {
1107 if (toElement(this)->authorShadowRoot())
1108 received
= received
&& toElement(this)->authorShadowRoot()->delegatesFocus();
1111 if (focused() == received
)
1114 Node::setFocus(received
);
1116 focusStateChanged();
1118 if (layoutObject() || received
)
1121 // If :focus sets display: none, we lose focus but still need to recalc our style.
1122 if (isElementNode() && toElement(this)->childrenOrSiblingsAffectedByFocus() && styleChangeType() < SubtreeStyleChange
)
1123 document().styleEngine().pseudoStateChangedForElement(CSSSelector::PseudoFocus
, *toElement(this));
1125 setNeedsStyleRecalc(LocalStyleChange
, StyleChangeReasonForTracing::createWithExtraData(StyleChangeReason::PseudoClass
, StyleChangeExtraData::Focus
));
1128 void ContainerNode::setActive(bool down
)
1130 if (down
== active())
1133 Node::setActive(down
);
1135 // FIXME: Why does this not need to handle the display: none transition like :hover does?
1136 if (layoutObject()) {
1137 if (styleChangeType() < SubtreeStyleChange
) {
1138 if (computedStyle()->affectedByActive() && computedStyle()->hasPseudoStyle(FIRST_LETTER
))
1139 setNeedsStyleRecalc(SubtreeStyleChange
, StyleChangeReasonForTracing::createWithExtraData(StyleChangeReason::PseudoClass
, StyleChangeExtraData::Active
));
1140 else if (isElementNode() && toElement(this)->childrenOrSiblingsAffectedByActive())
1141 document().styleEngine().pseudoStateChangedForElement(CSSSelector::PseudoActive
, *toElement(this));
1142 else if (computedStyle()->affectedByActive())
1143 setNeedsStyleRecalc(LocalStyleChange
, StyleChangeReasonForTracing::createWithExtraData(StyleChangeReason::PseudoClass
, StyleChangeExtraData::Active
));
1146 LayoutTheme::theme().controlStateChanged(*layoutObject(), PressedControlState
);
1150 void ContainerNode::setHovered(bool over
)
1152 if (over
== hovered())
1155 Node::setHovered(over
);
1157 // If :hover sets display: none we lose our hover but still need to recalc our style.
1158 if (!layoutObject()) {
1161 if (isElementNode() && toElement(this)->childrenOrSiblingsAffectedByHover() && styleChangeType() < SubtreeStyleChange
)
1162 document().styleEngine().pseudoStateChangedForElement(CSSSelector::PseudoHover
, *toElement(this));
1164 setNeedsStyleRecalc(LocalStyleChange
, StyleChangeReasonForTracing::createWithExtraData(StyleChangeReason::PseudoClass
, StyleChangeExtraData::Hover
));
1168 if (styleChangeType() < SubtreeStyleChange
) {
1169 if (computedStyle()->affectedByHover() && computedStyle()->hasPseudoStyle(FIRST_LETTER
))
1170 setNeedsStyleRecalc(SubtreeStyleChange
, StyleChangeReasonForTracing::createWithExtraData(StyleChangeReason::PseudoClass
, StyleChangeExtraData::Hover
));
1171 else if (isElementNode() && toElement(this)->childrenOrSiblingsAffectedByHover())
1172 document().styleEngine().pseudoStateChangedForElement(CSSSelector::PseudoHover
, *toElement(this));
1173 else if (computedStyle()->affectedByHover())
1174 setNeedsStyleRecalc(LocalStyleChange
, StyleChangeReasonForTracing::createWithExtraData(StyleChangeReason::PseudoClass
, StyleChangeExtraData::Hover
));
1177 LayoutTheme::theme().controlStateChanged(*layoutObject(), HoverControlState
);
1180 PassRefPtrWillBeRawPtr
<HTMLCollection
> ContainerNode::children()
1182 return ensureCachedCollection
<HTMLCollection
>(NodeChildren
);
1185 unsigned ContainerNode::countChildren() const
1189 for (n
= firstChild(); n
; n
= n
->nextSibling())
1194 PassRefPtrWillBeRawPtr
<Element
> ContainerNode::querySelector(const AtomicString
& selectors
, ExceptionState
& exceptionState
)
1196 if (selectors
.isEmpty()) {
1197 exceptionState
.throwDOMException(SyntaxError
, "The provided selector is empty.");
1201 SelectorQuery
* selectorQuery
= document().selectorQueryCache().add(selectors
, document(), exceptionState
);
1205 NthIndexCache
nthIndexCache(document());
1206 return selectorQuery
->queryFirst(*this);
1209 PassRefPtrWillBeRawPtr
<StaticElementList
> ContainerNode::querySelectorAll(const AtomicString
& selectors
, ExceptionState
& exceptionState
)
1211 if (selectors
.isEmpty()) {
1212 exceptionState
.throwDOMException(SyntaxError
, "The provided selector is empty.");
1216 SelectorQuery
* selectorQuery
= document().selectorQueryCache().add(selectors
, document(), exceptionState
);
1220 NthIndexCache
nthIndexCache(document());
1221 return selectorQuery
->queryAll(*this);
1224 static void dispatchChildInsertionEvents(Node
& child
)
1226 if (child
.isInShadowTree())
1229 ASSERT(!EventDispatchForbiddenScope::isEventDispatchForbidden());
1231 RefPtrWillBeRawPtr
<Node
> c(child
);
1232 RefPtrWillBeRawPtr
<Document
> document(child
.document());
1234 if (c
->parentNode() && document
->hasListenerType(Document::DOMNODEINSERTED_LISTENER
))
1235 c
->dispatchScopedEvent(MutationEvent::create(EventTypeNames::DOMNodeInserted
, true, c
->parentNode()));
1237 // dispatch the DOMNodeInsertedIntoDocument event to all descendants
1238 if (c
->inDocument() && document
->hasListenerType(Document::DOMNODEINSERTEDINTODOCUMENT_LISTENER
)) {
1239 for (; c
; c
= NodeTraversal::next(*c
, &child
))
1240 c
->dispatchScopedEvent(MutationEvent::create(EventTypeNames::DOMNodeInsertedIntoDocument
, false));
1244 static void dispatchChildRemovalEvents(Node
& child
)
1246 if (child
.isInShadowTree()) {
1247 InspectorInstrumentation::willRemoveDOMNode(&child
);
1251 ASSERT(!EventDispatchForbiddenScope::isEventDispatchForbidden());
1253 InspectorInstrumentation::willRemoveDOMNode(&child
);
1255 RefPtrWillBeRawPtr
<Node
> c(child
);
1256 RefPtrWillBeRawPtr
<Document
> document(child
.document());
1258 // Dispatch pre-removal mutation events.
1259 if (c
->parentNode() && document
->hasListenerType(Document::DOMNODEREMOVED_LISTENER
)) {
1260 NodeChildRemovalTracker
scope(child
);
1261 c
->dispatchScopedEvent(MutationEvent::create(EventTypeNames::DOMNodeRemoved
, true, c
->parentNode()));
1264 // Dispatch the DOMNodeRemovedFromDocument event to all descendants.
1265 if (c
->inDocument() && document
->hasListenerType(Document::DOMNODEREMOVEDFROMDOCUMENT_LISTENER
)) {
1266 NodeChildRemovalTracker
scope(child
);
1267 for (; c
; c
= NodeTraversal::next(*c
, &child
))
1268 c
->dispatchScopedEvent(MutationEvent::create(EventTypeNames::DOMNodeRemovedFromDocument
, false));
1272 void ContainerNode::updateTreeAfterInsertion(Node
& child
)
1276 ASSERT(child
.refCount());
1279 ChildListMutationScope(*this).childAdded(child
);
1281 notifyNodeInserted(child
);
1283 dispatchChildInsertionEvents(child
);
1286 bool ContainerNode::hasRestyleFlagInternal(DynamicRestyleFlags mask
) const
1288 return rareData()->hasRestyleFlag(mask
);
1291 bool ContainerNode::hasRestyleFlagsInternal() const
1293 return rareData()->hasRestyleFlags();
1296 void ContainerNode::setRestyleFlag(DynamicRestyleFlags mask
)
1298 ASSERT(isElementNode() || isShadowRoot());
1299 ensureRareData().setRestyleFlag(mask
);
1302 void ContainerNode::recalcChildStyle(StyleRecalcChange change
)
1304 ASSERT(document().inStyleRecalc());
1305 ASSERT(change
>= UpdatePseudoElements
|| childNeedsStyleRecalc());
1306 ASSERT(!needsStyleRecalc());
1308 if (change
< Force
&& hasRareData() && childNeedsStyleRecalc())
1309 checkForChildrenAdjacentRuleChanges();
1311 // This loop is deliberately backwards because we use insertBefore in the layout tree, and want to avoid
1312 // a potentially n^2 loop to find the insertion point while resolving style. Having us start from the last
1313 // child and work our way back means in the common case, we'll find the insertion point in O(1) time.
1314 // See crbug.com/288225
1315 StyleResolver
& styleResolver
= document().ensureStyleResolver();
1316 Text
* lastTextNode
= nullptr;
1317 for (Node
* child
= lastChild(); child
; child
= child
->previousSibling()) {
1318 if (child
->isTextNode()) {
1319 toText(child
)->recalcTextStyle(change
, lastTextNode
);
1320 lastTextNode
= toText(child
);
1321 } else if (child
->isElementNode()) {
1322 Element
* element
= toElement(child
);
1323 if (element
->shouldCallRecalcStyle(change
))
1324 element
->recalcStyle(change
, lastTextNode
);
1325 else if (element
->supportsStyleSharing())
1326 styleResolver
.addToStyleSharingList(*element
);
1327 if (element
->layoutObject())
1328 lastTextNode
= nullptr;
1333 void ContainerNode::checkForChildrenAdjacentRuleChanges()
1335 bool hasDirectAdjacentRules
= childrenAffectedByDirectAdjacentRules();
1336 bool hasIndirectAdjacentRules
= childrenAffectedByIndirectAdjacentRules();
1338 if (!hasDirectAdjacentRules
&& !hasIndirectAdjacentRules
)
1341 unsigned forceCheckOfNextElementCount
= 0;
1342 bool forceCheckOfAnyElementSibling
= false;
1343 Document
& document
= this->document();
1345 for (Element
* child
= ElementTraversal::firstChild(*this); child
; child
= ElementTraversal::nextSibling(*child
)) {
1346 bool childRulesChanged
= child
->needsStyleRecalc() && child
->styleChangeType() >= SubtreeStyleChange
;
1348 if (forceCheckOfNextElementCount
|| forceCheckOfAnyElementSibling
)
1349 child
->setNeedsStyleRecalc(SubtreeStyleChange
, StyleChangeReasonForTracing::create(StyleChangeReason::SiblingSelector
));
1351 if (childRulesChanged
&& hasDirectAdjacentRules
)
1352 forceCheckOfNextElementCount
= document
.styleEngine().maxDirectAdjacentSelectors();
1353 else if (forceCheckOfNextElementCount
)
1354 --forceCheckOfNextElementCount
;
1356 forceCheckOfAnyElementSibling
= forceCheckOfAnyElementSibling
|| (childRulesChanged
&& hasIndirectAdjacentRules
);
1360 void ContainerNode::checkForSiblingStyleChanges(SiblingCheckType changeType
, Node
* nodeBeforeChange
, Node
* nodeAfterChange
)
1362 if (!inActiveDocument() || document().hasPendingForcedStyleRecalc() || styleChangeType() >= SubtreeStyleChange
)
1365 // Forward positional selectors include nth-child, nth-of-type, first-of-type and only-of-type.
1366 // The indirect adjacent selector is the ~ selector.
1367 // Backward positional selectors include nth-last-child, nth-last-of-type, last-of-type and only-of-type.
1368 // We have to invalidate everything following the insertion point in the forward and indirect adjacent case,
1369 // and everything before the insertion point in the backward case.
1370 // |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.
1371 // 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
1372 // here. recalcStyle will then force a walk of the children when it sees that this has happened.
1373 if (((childrenAffectedByForwardPositionalRules() || childrenAffectedByIndirectAdjacentRules()) && nodeAfterChange
)
1374 || (childrenAffectedByBackwardPositionalRules() && nodeBeforeChange
)) {
1375 setNeedsStyleRecalc(SubtreeStyleChange
, StyleChangeReasonForTracing::create(StyleChangeReason::SiblingSelector
));
1379 // :first-child. In the parser callback case, we don't have to check anything, since we were right the first time.
1380 // In the DOM case, we only need to do something if |afterChange| is not 0.
1381 // |afterChange| is 0 in the parser case, so it works out that we'll skip this block.
1382 if (childrenAffectedByFirstChildRules() && nodeAfterChange
) {
1383 ASSERT(changeType
!= FinishedParsingChildren
);
1384 // Find our new first child element.
1385 Element
* firstChildElement
= ElementTraversal::firstChild(*this);
1387 // Find the first element after the change.
1388 Element
* elementAfterChange
= nodeAfterChange
->isElementNode() ? toElement(nodeAfterChange
) : ElementTraversal::nextSibling(*nodeAfterChange
);
1390 // This is the element insertion as first child element case.
1391 if (changeType
== SiblingElementInserted
&& elementAfterChange
&& firstChildElement
!= elementAfterChange
1392 && (!nodeBeforeChange
|| !nodeBeforeChange
->isElementNode()) && elementAfterChange
->affectedByFirstChildRules()) {
1393 elementAfterChange
->setNeedsStyleRecalc(SubtreeStyleChange
, StyleChangeReasonForTracing::create(StyleChangeReason::SiblingSelector
));
1396 // This is the first child element removal case.
1397 if (changeType
== SiblingElementRemoved
&& firstChildElement
== elementAfterChange
&& firstChildElement
&& firstChildElement
->affectedByFirstChildRules())
1398 firstChildElement
->setNeedsStyleRecalc(SubtreeStyleChange
, StyleChangeReasonForTracing::create(StyleChangeReason::SiblingSelector
));
1401 // :last-child. In the parser callback case, we don't have to check anything, since we were right the first time.
1402 // In the DOM case, we only need to do something if |afterChange| is not 0.
1403 if (childrenAffectedByLastChildRules() && nodeBeforeChange
) {
1404 // Find our new last child element.
1405 Element
* lastChildElement
= ElementTraversal::lastChild(*this);
1407 // Find the last element before the change.
1408 Element
* elementBeforeChange
= nodeBeforeChange
->isElementNode() ? toElement(nodeBeforeChange
) : ElementTraversal::previousSibling(*nodeBeforeChange
);
1410 // This is the element insertion as last child element case.
1411 if (changeType
== SiblingElementInserted
&& elementBeforeChange
&& lastChildElement
!= elementBeforeChange
1412 && (!nodeAfterChange
|| !nodeAfterChange
->isElementNode()) && elementBeforeChange
->affectedByLastChildRules()) {
1413 elementBeforeChange
->setNeedsStyleRecalc(SubtreeStyleChange
, StyleChangeReasonForTracing::create(StyleChangeReason::SiblingSelector
));
1416 // 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
1418 if ((changeType
== SiblingElementRemoved
|| changeType
== FinishedParsingChildren
) && lastChildElement
== elementBeforeChange
&& lastChildElement
&& lastChildElement
->affectedByLastChildRules())
1419 lastChildElement
->setNeedsStyleRecalc(SubtreeStyleChange
, StyleChangeReasonForTracing::create(StyleChangeReason::SiblingSelector
));
1422 // The + selector. We need to invalidate the first element following the change. It is the only possible element
1423 // that could be affected by this DOM change.
1424 if (childrenAffectedByDirectAdjacentRules() && nodeAfterChange
) {
1425 if (Element
* elementAfterChange
= nodeAfterChange
->isElementNode() ? toElement(nodeAfterChange
) : ElementTraversal::nextSibling(*nodeAfterChange
))
1426 elementAfterChange
->setNeedsStyleRecalc(SubtreeStyleChange
, StyleChangeReasonForTracing::create(StyleChangeReason::SiblingSelector
));
1430 void ContainerNode::invalidateNodeListCachesInAncestors(const QualifiedName
* attrName
, Element
* attributeOwnerElement
)
1432 if (hasRareData() && (!attrName
|| isAttributeNode())) {
1433 if (NodeListsNodeData
* lists
= rareData()->nodeLists()) {
1434 if (ChildNodeList
* childNodeList
= lists
->childNodeList(*this))
1435 childNodeList
->invalidateCache();
1439 // Modifications to attributes that are not associated with an Element can't invalidate NodeList caches.
1440 if (attrName
&& !attributeOwnerElement
)
1443 if (!document().shouldInvalidateNodeListCaches(attrName
))
1446 document().invalidateNodeListCaches(attrName
);
1448 for (ContainerNode
* node
= this; node
; node
= node
->parentNode()) {
1449 if (NodeListsNodeData
* lists
= node
->nodeLists())
1450 lists
->invalidateCaches(attrName
);
1454 PassRefPtrWillBeRawPtr
<TagCollection
> ContainerNode::getElementsByTagName(const AtomicString
& localName
)
1456 if (localName
.isNull())
1459 if (document().isHTMLDocument())
1460 return ensureCachedCollection
<HTMLTagCollection
>(HTMLTagCollectionType
, localName
);
1461 return ensureCachedCollection
<TagCollection
>(TagCollectionType
, localName
);
1464 PassRefPtrWillBeRawPtr
<TagCollection
> ContainerNode::getElementsByTagNameNS(const AtomicString
& namespaceURI
, const AtomicString
& localName
)
1466 if (localName
.isNull())
1469 if (namespaceURI
== starAtom
)
1470 return getElementsByTagName(localName
);
1472 return ensureCachedCollection
<TagCollection
>(TagCollectionType
, namespaceURI
.isEmpty() ? nullAtom
: namespaceURI
, localName
);
1475 // Takes an AtomicString in argument because it is common for elements to share the same name attribute.
1476 // Therefore, the NameNodeList factory function expects an AtomicString type.
1477 PassRefPtrWillBeRawPtr
<NameNodeList
> ContainerNode::getElementsByName(const AtomicString
& elementName
)
1479 return ensureCachedCollection
<NameNodeList
>(NameNodeListType
, elementName
);
1482 // Takes an AtomicString in argument because it is common for elements to share the same set of class names.
1483 // Therefore, the ClassNodeList factory function expects an AtomicString type.
1484 PassRefPtrWillBeRawPtr
<ClassCollection
> ContainerNode::getElementsByClassName(const AtomicString
& classNames
)
1486 return ensureCachedCollection
<ClassCollection
>(ClassCollectionType
, classNames
);
1489 PassRefPtrWillBeRawPtr
<RadioNodeList
> ContainerNode::radioNodeList(const AtomicString
& name
, bool onlyMatchImgElements
)
1491 ASSERT(isHTMLFormElement(this) || isHTMLFieldSetElement(this));
1492 CollectionType type
= onlyMatchImgElements
? RadioImgNodeListType
: RadioNodeListType
;
1493 return ensureCachedCollection
<RadioNodeList
>(type
, name
);
1496 Element
* ContainerNode::getElementById(const AtomicString
& id
) const
1498 if (isInTreeScope()) {
1499 // Fast path if we are in a tree scope: call getElementById() on tree scope
1500 // and check if the matching element is in our subtree.
1501 Element
* element
= treeScope().getElementById(id
);
1504 if (element
->isDescendantOf(this))
1508 // Fall back to traversing our subtree. In case of duplicate ids, the first element found will be returned.
1509 for (Element
& element
: ElementTraversal::descendantsOf(*this)) {
1510 if (element
.getIdAttribute() == id
)
1516 NodeListsNodeData
& ContainerNode::ensureNodeLists()
1518 return ensureRareData().ensureNodeLists();
1522 bool childAttachedAllowedWhenAttachingChildren(ContainerNode
* node
)
1524 if (node
->isShadowRoot())
1527 if (node
->isInsertionPoint())
1530 if (node
->isElementNode() && toElement(node
)->shadow())
1537 } // namespace blink