1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 * Base class for all DOM nodes.
13 #include "AccessCheck.h"
15 #include "js/ForOfIterator.h" // JS::ForOfIterator
16 #include "js/JSON.h" // JS_ParseJSON
17 #include "mozAutoDocUpdate.h"
18 #include "mozilla/AsyncEventDispatcher.h"
19 #include "mozilla/CORSMode.h"
20 #include "mozilla/EventDispatcher.h"
21 #include "mozilla/EventListenerManager.h"
22 #include "mozilla/HTMLEditor.h"
23 #include "mozilla/InternalMutationEvent.h"
24 #include "mozilla/Likely.h"
25 #include "mozilla/Maybe.h"
26 #include "mozilla/MemoryReporting.h"
27 #include "mozilla/PresShell.h"
28 #include "mozilla/ServoBindings.h"
29 #include "mozilla/Telemetry.h"
30 #include "mozilla/TextControlElement.h"
31 #include "mozilla/TextEditor.h"
32 #include "mozilla/TimeStamp.h"
33 #include "mozilla/dom/BindContext.h"
34 #include "mozilla/dom/CharacterData.h"
35 #include "mozilla/dom/ChildIterator.h"
36 #include "mozilla/dom/CustomElementRegistry.h"
37 #include "mozilla/dom/DebuggerNotificationBinding.h"
38 #include "mozilla/dom/DocumentType.h"
39 #include "mozilla/dom/Element.h"
40 #include "mozilla/dom/Event.h"
41 #include "mozilla/dom/Exceptions.h"
42 #include "mozilla/dom/Link.h"
43 #include "mozilla/dom/HTMLImageElement.h"
44 #include "mozilla/dom/HTMLMediaElement.h"
45 #include "mozilla/dom/HTMLTemplateElement.h"
46 #include "mozilla/dom/MutationObservers.h"
47 #include "mozilla/dom/Selection.h"
48 #include "mozilla/dom/ShadowRoot.h"
49 #include "mozilla/dom/SVGUseElement.h"
50 #include "mozilla/dom/ScriptSettings.h"
51 #include "mozilla/dom/L10nOverlays.h"
52 #include "mozilla/ProfilerLabels.h"
53 #include "mozilla/StaticPrefs_layout.h"
54 #include "nsAttrValueOrString.h"
55 #include "nsCCUncollectableMarker.h"
56 #include "nsContentCreatorFunctions.h"
57 #include "nsContentList.h"
58 #include "nsContentUtils.h"
59 #include "nsCOMArray.h"
60 #include "nsCycleCollectionParticipant.h"
61 #include "mozilla/dom/Attr.h"
62 #include "nsDOMAttributeMap.h"
64 #include "nsDOMCSSAttrDeclaration.h"
66 #include "nsDOMMutationObserver.h"
67 #include "nsDOMString.h"
68 #include "nsDOMTokenList.h"
69 #include "nsFocusManager.h"
70 #include "nsFrameSelection.h"
71 #include "nsGenericHTMLElement.h"
72 #include "nsGkAtoms.h"
73 #include "nsIAnonymousContentCreator.h"
75 #include "nsIContentInlines.h"
76 #include "mozilla/dom/Document.h"
77 #include "mozilla/dom/DocumentInlines.h"
78 #include "nsIFrameInlines.h"
79 #include "mozilla/dom/NodeInfo.h"
80 #include "mozilla/dom/NodeInfoInlines.h"
81 #include "nsIScriptGlobalObject.h"
82 #include "nsIScrollableFrame.h"
84 #include "nsViewManager.h"
85 #include "nsIWidget.h"
86 #include "nsLayoutUtils.h"
87 #include "nsNameSpaceManager.h"
88 #include "nsNodeInfoManager.h"
89 #include "nsObjectLoadingContent.h"
90 #include "nsPIDOMWindow.h"
91 #include "nsPresContext.h"
92 #include "nsPrintfCString.h"
95 #include "nsStyleConsts.h"
96 #include "nsTextNode.h"
97 #include "nsUnicharUtils.h"
98 #include "nsWindowSizes.h"
99 #include "mozilla/Preferences.h"
100 #include "xpcpublic.h"
101 #include "HTMLLegendElement.h"
102 #include "nsWrapperCacheInlines.h"
103 #include "WrapperFactory.h"
105 #include "nsGlobalWindow.h"
106 #include "GeometryUtils.h"
107 #include "nsIAnimationObserver.h"
108 #include "nsChildContentList.h"
109 #include "mozilla/dom/NodeBinding.h"
110 #include "mozilla/dom/BindingDeclarations.h"
111 #include "mozilla/dom/AncestorIterator.h"
112 #include "xpcprivate.h"
114 #include "XPathGenerator.h"
117 # include "mozilla/dom/AccessibleNode.h"
120 using namespace mozilla
;
121 using namespace mozilla::dom
;
123 static bool ShouldUseNACScope(const nsINode
* aNode
) {
124 return aNode
->IsInNativeAnonymousSubtree();
127 static bool ShouldUseUAWidgetScope(const nsINode
* aNode
) {
128 return aNode
->HasBeenInUAWidget();
131 void* nsINode::operator new(size_t aSize
, nsNodeInfoManager
* aManager
) {
132 if (StaticPrefs::dom_arena_allocator_enabled_AtStartup()) {
133 MOZ_ASSERT(aManager
, "nsNodeInfoManager needs to be initialized");
134 return aManager
->Allocate(aSize
);
136 return ::operator new(aSize
);
138 void nsINode::operator delete(void* aPtr
) { free_impl(aPtr
); }
140 bool nsINode::IsInclusiveDescendantOf(const nsINode
* aNode
) const {
141 MOZ_ASSERT(aNode
, "The node is nullptr.");
147 if (!aNode
->HasFlag(NODE_MAY_HAVE_ELEMENT_CHILDREN
)) {
148 return GetParentNode() == aNode
;
151 for (nsINode
* node
: Ancestors(*this)) {
159 bool nsINode::IsInclusiveFlatTreeDescendantOf(const nsINode
* aNode
) const {
160 MOZ_ASSERT(aNode
, "The node is nullptr.");
162 for (nsINode
* node
: InclusiveFlatTreeAncestors(*this)) {
170 bool nsINode::IsShadowIncludingInclusiveDescendantOf(
171 const nsINode
* aNode
) const {
172 MOZ_ASSERT(aNode
, "The node is nullptr.");
174 if (this->GetComposedDoc() == aNode
) {
178 const nsINode
* node
= this;
184 node
= node
->GetParentOrShadowHostNode();
190 nsINode::nsSlots::nsSlots() : mWeakReference(nullptr) {}
192 nsINode::nsSlots::~nsSlots() {
194 mChildNodes
->InvalidateCacheIfAvailable();
197 if (mWeakReference
) {
198 mWeakReference
->NoticeNodeDestruction();
202 void nsINode::nsSlots::Traverse(nsCycleCollectionTraversalCallback
& cb
) {
203 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mSlots->mChildNodes");
204 cb
.NoteXPCOMChild(mChildNodes
);
207 void nsINode::nsSlots::Unlink(nsINode
&) {
209 mChildNodes
->InvalidateCacheIfAvailable();
210 ImplCycleCollectionUnlink(mChildNodes
);
214 //----------------------------------------------------------------------
216 #ifdef MOZILLA_INTERNAL_API
217 nsINode::nsINode(already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
)
218 : mNodeInfo(std::move(aNodeInfo
)),
220 # ifndef BOOL_FLAGS_ON_WRAPPER_CACHE
226 mPreviousOrLastSibling(nullptr),
232 nsINode::~nsINode() {
233 MOZ_ASSERT(!HasSlots(), "LastRelease was not called?");
234 MOZ_ASSERT(mSubtreeRoot
== this, "Didn't restore state properly?");
237 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
238 void nsINode::AssertInvariantsOnNodeInfoChange() {
239 MOZ_DIAGNOSTIC_ASSERT(!IsInComposedDoc());
240 if (nsCOMPtr
<Link
> link
= do_QueryInterface(this)) {
241 MOZ_DIAGNOSTIC_ASSERT(!link
->HasPendingLinkUpdate());
246 void* nsINode::GetProperty(const nsAtom
* aPropertyName
,
247 nsresult
* aStatus
) const {
248 if (!HasProperties()) { // a fast HasFlag() test
250 *aStatus
= NS_PROPTABLE_PROP_NOT_THERE
;
254 return OwnerDoc()->PropertyTable().GetProperty(this, aPropertyName
, aStatus
);
257 nsresult
nsINode::SetProperty(nsAtom
* aPropertyName
, void* aValue
,
258 NSPropertyDtorFunc aDtor
, bool aTransfer
) {
259 nsresult rv
= OwnerDoc()->PropertyTable().SetProperty(
260 this, aPropertyName
, aValue
, aDtor
, nullptr, aTransfer
);
261 if (NS_SUCCEEDED(rv
)) {
262 SetFlags(NODE_HAS_PROPERTIES
);
268 void nsINode::RemoveProperty(const nsAtom
* aPropertyName
) {
269 OwnerDoc()->PropertyTable().RemoveProperty(this, aPropertyName
);
272 void* nsINode::TakeProperty(const nsAtom
* aPropertyName
, nsresult
* aStatus
) {
273 return OwnerDoc()->PropertyTable().TakeProperty(this, aPropertyName
, aStatus
);
276 nsIContentSecurityPolicy
* nsINode::GetCsp() const {
277 return OwnerDoc()->GetCsp();
280 nsINode::nsSlots
* nsINode::CreateSlots() { return new nsSlots(); }
282 static const nsINode
* GetClosestCommonInclusiveAncestorForRangeInSelection(
283 const nsINode
* aNode
) {
285 !aNode
->IsClosestCommonInclusiveAncestorForRangeInSelection()) {
287 ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
290 aNode
= aNode
->GetParentNode();
296 * A Comparator suitable for mozilla::BinarySearchIf for searching a collection
297 * of nsRange* for an overlap of (mNode, mStartOffset) .. (mNode, mEndOffset).
299 class IsItemInRangeComparator
{
301 // @param aStartOffset has to be less or equal to aEndOffset.
302 IsItemInRangeComparator(const nsINode
& aNode
, const uint32_t aStartOffset
,
303 const uint32_t aEndOffset
,
304 nsContentUtils::ComparePointsCache
* aCache
)
306 mStartOffset(aStartOffset
),
307 mEndOffset(aEndOffset
),
309 MOZ_ASSERT(aStartOffset
<= aEndOffset
);
312 int operator()(const AbstractRange
* const aRange
) const {
313 int32_t cmp
= nsContentUtils::ComparePoints_Deprecated(
314 &mNode
, mEndOffset
, aRange
->GetStartContainer(), aRange
->StartOffset(),
317 cmp
= nsContentUtils::ComparePoints_Deprecated(
318 &mNode
, mStartOffset
, aRange
->GetEndContainer(), aRange
->EndOffset(),
329 const nsINode
& mNode
;
330 const uint32_t mStartOffset
;
331 const uint32_t mEndOffset
;
332 nsContentUtils::ComparePointsCache
* mCache
;
335 bool nsINode::IsSelected(const uint32_t aStartOffset
,
336 const uint32_t aEndOffset
) const {
337 MOZ_ASSERT(aStartOffset
<= aEndOffset
);
339 const nsINode
* n
= GetClosestCommonInclusiveAncestorForRangeInSelection(this);
340 NS_ASSERTION(n
|| !IsMaybeSelected(),
341 "A node without a common inclusive ancestor for a range in "
342 "Selection is for sure not selected.");
344 // Collect the selection objects for potential ranges.
345 nsTHashSet
<Selection
*> ancestorSelections
;
346 for (; n
; n
= GetClosestCommonInclusiveAncestorForRangeInSelection(
347 n
->GetParentNode())) {
348 const LinkedList
<nsRange
>* ranges
=
349 n
->GetExistingClosestCommonInclusiveAncestorRanges();
353 for (const nsRange
* range
: *ranges
) {
354 MOZ_ASSERT(range
->IsInAnySelection(),
355 "Why is this range registered with a node?");
356 // Looks like that IsInSelection() assert fails sometimes...
357 if (range
->IsInAnySelection()) {
358 for (const WeakPtr
<Selection
>& selection
: range
->GetSelections()) {
359 ancestorSelections
.Insert(selection
);
365 nsContentUtils::ComparePointsCache cache
;
366 IsItemInRangeComparator comparator
{*this, aStartOffset
, aEndOffset
, &cache
};
367 for (Selection
* selection
: ancestorSelections
) {
368 // Binary search the sorted ranges in this selection.
369 // (Selection::GetRangeAt returns its ranges ordered).
371 size_t high
= selection
->RangeCount();
373 while (high
!= low
) {
374 size_t middle
= low
+ (high
- low
) / 2;
376 const AbstractRange
* const range
= selection
->GetAbstractRangeAt(middle
);
377 int result
= comparator(range
);
379 if (!range
->Collapsed()) {
383 const AbstractRange
* middlePlus1
;
384 const AbstractRange
* middleMinus1
;
385 // if node end > start of middle+1, result = 1
386 if (middle
+ 1 < high
&&
387 (middlePlus1
= selection
->GetAbstractRangeAt(middle
+ 1)) &&
388 nsContentUtils::ComparePoints_Deprecated(
389 this, aEndOffset
, middlePlus1
->GetStartContainer(),
390 middlePlus1
->StartOffset(), nullptr, &cache
) > 0) {
392 // if node start < end of middle - 1, result = -1
393 } else if (middle
>= 1 &&
394 (middleMinus1
= selection
->GetAbstractRangeAt(middle
- 1)) &&
395 nsContentUtils::ComparePoints_Deprecated(
396 this, aStartOffset
, middleMinus1
->GetEndContainer(),
397 middleMinus1
->EndOffset(), nullptr, &cache
) < 0) {
415 Element
* nsINode::GetAnonymousRootElementOfTextEditor(
416 TextEditor
** aTextEditor
) {
418 *aTextEditor
= nullptr;
420 RefPtr
<TextControlElement
> textControlElement
;
421 if (IsInNativeAnonymousSubtree()) {
422 textControlElement
= TextControlElement::FromNodeOrNull(
423 GetClosestNativeAnonymousSubtreeRootParentOrHost());
425 textControlElement
= TextControlElement::FromNode(this);
427 if (!textControlElement
) {
430 RefPtr
<TextEditor
> textEditor
= textControlElement
->GetTextEditor();
432 // The found `TextControlElement` may be an input element which is not a
433 // text control element. In this case, such element must not be in a
434 // native anonymous tree of a `TextEditor` so this node is not in any
439 Element
* rootElement
= textEditor
->GetRoot();
441 textEditor
.forget(aTextEditor
);
446 void nsINode::QueueDevtoolsAnonymousEvent(bool aIsRemove
) {
447 MOZ_ASSERT(IsRootOfNativeAnonymousSubtree());
448 MOZ_ASSERT(OwnerDoc()->DevToolsAnonymousAndShadowEventsEnabled());
449 AsyncEventDispatcher
* dispatcher
= new AsyncEventDispatcher(
450 this, aIsRemove
? u
"anonymousrootremoved"_ns
: u
"anonymousrootcreated"_ns
,
451 CanBubble::eYes
, ChromeOnlyDispatch::eYes
, Composed::eYes
);
452 dispatcher
->PostDOMEvent();
455 nsINode
* nsINode::GetRootNode(const GetRootNodeOptions
& aOptions
) {
456 if (aOptions
.mComposed
) {
457 if (Document
* doc
= GetComposedDoc()) {
461 nsINode
* node
= this;
463 node
= node
->SubtreeRoot();
464 ShadowRoot
* shadow
= ShadowRoot::FromNode(node
);
468 node
= shadow
->GetHost();
474 return SubtreeRoot();
477 nsIContent
* nsINode::GetFirstChildOfTemplateOrNode() {
478 if (IsTemplateElement()) {
479 DocumentFragment
* frag
= static_cast<HTMLTemplateElement
*>(this)->Content();
480 return frag
->GetFirstChild();
483 return GetFirstChild();
486 nsINode
* nsINode::SubtreeRoot() const {
487 auto RootOfNode
= [](const nsINode
* aStart
) -> nsINode
* {
488 const nsINode
* node
= aStart
;
489 const nsINode
* iter
= node
;
490 while ((iter
= iter
->GetParentNode())) {
493 return const_cast<nsINode
*>(node
);
496 // There are four cases of interest here. nsINodes that are really:
497 // 1. Document nodes - Are always in the document.
498 // 2.a nsIContent nodes not in a shadow tree - Are either in the document,
499 // or mSubtreeRoot is updated in BindToTree/UnbindFromTree.
500 // 2.b nsIContent nodes in a shadow tree - Are never in the document,
501 // ignore mSubtreeRoot and return the containing shadow root.
502 // 4. Attr nodes - Are never in the document, and mSubtreeRoot
503 // is always 'this' (as set in nsINode's ctor).
505 if (IsInUncomposedDoc()) {
506 node
= OwnerDocAsNode();
507 } else if (IsContent()) {
508 ShadowRoot
* containingShadow
= AsContent()->GetContainingShadow();
509 node
= containingShadow
? containingShadow
: mSubtreeRoot
;
511 NS_WARNING("Using SubtreeRoot() on unlinked element?");
512 node
= RootOfNode(this);
517 MOZ_ASSERT(node
, "Should always have a node here!");
520 const nsINode
* slowNode
= RootOfNode(this);
521 MOZ_ASSERT(slowNode
== node
, "These should always be in sync!");
527 static nsIContent
* GetRootForContentSubtree(nsIContent
* aContent
) {
528 NS_ENSURE_TRUE(aContent
, nullptr);
530 // Special case for ShadowRoot because the ShadowRoot itself is
531 // the root. This is necessary to prevent selection from crossing
532 // the ShadowRoot boundary.
534 // FIXME(emilio): The NAC check should probably be done before this? We can
535 // have NAC inside shadow DOM.
536 if (ShadowRoot
* containingShadow
= aContent
->GetContainingShadow()) {
537 return containingShadow
;
539 if (nsIContent
* nativeAnonRoot
=
540 aContent
->GetClosestNativeAnonymousSubtreeRoot()) {
541 return nativeAnonRoot
;
543 if (Document
* doc
= aContent
->GetUncomposedDoc()) {
544 return doc
->GetRootElement();
546 return nsIContent::FromNode(aContent
->SubtreeRoot());
549 nsIContent
* nsINode::GetSelectionRootContent(PresShell
* aPresShell
) {
550 NS_ENSURE_TRUE(aPresShell
, nullptr);
552 if (IsDocument()) return AsDocument()->GetRootElement();
553 if (!IsContent()) return nullptr;
555 if (GetComposedDoc() != aPresShell
->GetDocument()) {
559 if (AsContent()->HasIndependentSelection() || IsInNativeAnonymousSubtree()) {
560 // This node should be an inclusive descendant of input/textarea editor.
561 // In that case, the anonymous <div> for TextEditor should be always the
563 // FIXME: If Selection for the document is collapsed in <input> or
564 // <textarea>, returning anonymous <div> may make the callers confused.
565 // Perhaps, we should do this only when this is in the native anonymous
566 // subtree unless the callers explicitly want to retrieve the anonymous
567 // <div> from a text control element.
568 if (Element
* anonymousDivElement
= GetAnonymousRootElementOfTextEditor()) {
569 return anonymousDivElement
;
573 nsPresContext
* presContext
= aPresShell
->GetPresContext();
575 HTMLEditor
* htmlEditor
= nsContentUtils::GetHTMLEditor(presContext
);
577 // This node is in HTML editor.
578 if (!IsInComposedDoc() || IsInDesignMode() ||
579 !HasFlag(NODE_IS_EDITABLE
)) {
580 nsIContent
* editorRoot
= htmlEditor
->GetRoot();
581 NS_ENSURE_TRUE(editorRoot
, nullptr);
582 return nsContentUtils::IsInSameAnonymousTree(this, editorRoot
)
584 : GetRootForContentSubtree(AsContent());
586 // If the document isn't editable but this is editable, this is in
587 // contenteditable. Use the editing host element for selection root.
588 return static_cast<nsIContent
*>(this)->GetEditingHost();
592 RefPtr
<nsFrameSelection
> fs
= aPresShell
->FrameSelection();
593 nsIContent
* content
= fs
->GetLimiter();
595 content
= fs
->GetAncestorLimiter();
597 Document
* doc
= aPresShell
->GetDocument();
598 NS_ENSURE_TRUE(doc
, nullptr);
599 content
= doc
->GetRootElement();
600 if (!content
) return nullptr;
604 // This node might be in another subtree, if so, we should find this subtree's
605 // root. Otherwise, we can return the content simply.
606 NS_ENSURE_TRUE(content
, nullptr);
607 if (!nsContentUtils::IsInSameAnonymousTree(this, content
)) {
608 content
= GetRootForContentSubtree(AsContent());
609 // Fixup for ShadowRoot because the ShadowRoot itself does not have a frame.
610 // Use the host as the root.
611 if (ShadowRoot
* shadowRoot
= ShadowRoot::FromNode(content
)) {
612 content
= shadowRoot
->GetHost();
619 nsINodeList
* nsINode::ChildNodes() {
620 nsSlots
* slots
= Slots();
621 if (!slots
->mChildNodes
) {
622 slots
->mChildNodes
= IsAttr() ? new nsAttrChildContentList(this)
623 : new nsParentNodeChildContentList(this);
626 return slots
->mChildNodes
;
629 nsIContent
* nsINode::GetLastChild() const {
630 return mFirstChild
? mFirstChild
->mPreviousOrLastSibling
: nullptr;
633 void nsINode::InvalidateChildNodes() {
634 MOZ_ASSERT(!IsAttr());
636 nsSlots
* slots
= GetExistingSlots();
637 if (!slots
|| !slots
->mChildNodes
) {
642 static_cast<nsParentNodeChildContentList
*>(slots
->mChildNodes
.get());
643 childNodes
->InvalidateCache();
646 void nsINode::GetTextContentInternal(nsAString
& aTextContent
,
647 OOMReporter
& aError
) {
648 SetDOMStringToNull(aTextContent
);
651 DocumentOrShadowRoot
* nsINode::GetContainingDocumentOrShadowRoot() const {
652 if (IsInUncomposedDoc()) {
656 if (IsInShadowTree()) {
657 return AsContent()->GetContainingShadow();
663 DocumentOrShadowRoot
* nsINode::GetUncomposedDocOrConnectedShadowRoot() const {
664 if (IsInUncomposedDoc()) {
668 if (IsInComposedDoc() && IsInShadowTree()) {
669 return AsContent()->GetContainingShadow();
675 mozilla::SafeDoublyLinkedList
<nsIMutationObserver
>*
676 nsINode::GetMutationObservers() {
677 return HasSlots() ? &GetExistingSlots()->mMutationObservers
: nullptr;
680 void nsINode::LastRelease() {
681 nsINode::nsSlots
* slots
= GetExistingSlots();
683 if (!slots
->mMutationObservers
.isEmpty()) {
684 for (auto iter
= slots
->mMutationObservers
.begin();
685 iter
!= slots
->mMutationObservers
.end(); ++iter
) {
686 iter
->NodeWillBeDestroyed(this);
691 nsIContent
* content
= AsContent();
692 if (HTMLSlotElement
* slot
= content
->GetManualSlotAssignment()) {
693 content
->SetManualSlotAssignment(nullptr);
694 slot
->RemoveManuallyAssignedNode(*content
);
698 if (Element
* element
= Element::FromNode(this)) {
699 if (CustomElementData
* data
= element
->GetCustomElementData()) {
708 // Kill properties first since that may run external code, so we want to
709 // be in as complete state as possible at that time.
711 // Delete all properties before tearing down the document. Some of the
712 // properties are bound to nsINode objects and the destructor functions of
713 // the properties may want to use the owner document of the nsINode.
714 AsDocument()->RemoveAllProperties();
716 if (HasProperties()) {
717 // Strong reference to the document so that deleting properties can't
718 // delete the document.
719 nsCOMPtr
<Document
> document
= OwnerDoc();
720 document
->RemoveAllPropertiesFor(this);
723 if (HasFlag(ADDED_TO_FORM
)) {
724 if (nsGenericHTMLFormControlElement
* formControl
=
725 nsGenericHTMLFormControlElement::FromNode(this)) {
726 // Tell the form (if any) this node is going away. Don't
727 // notify, since we're being destroyed in any case.
728 formControl
->ClearForm(true, true);
729 } else if (HTMLImageElement
* imageElem
=
730 HTMLImageElement::FromNode(this)) {
731 imageElem
->ClearForm(true);
735 UnsetFlags(NODE_HAS_PROPERTIES
);
737 if (NodeType() != nsINode::DOCUMENT_NODE
&&
738 HasFlag(NODE_HAS_LISTENERMANAGER
)) {
740 if (nsContentUtils::IsInitialized()) {
741 EventListenerManager
* manager
=
742 nsContentUtils::GetExistingListenerManagerForNode(this);
745 "Huh, our bit says we have a listener manager list, "
746 "but there's nothing in the hash!?!!");
751 nsContentUtils::RemoveListenerManager(this);
752 UnsetFlags(NODE_HAS_LISTENERMANAGER
);
755 ReleaseWrapper(this);
757 FragmentOrElement::RemoveBlackMarkedNode(this);
760 std::ostream
& operator<<(std::ostream
& aStream
, const nsINode
& aNode
) {
761 nsAutoString elemDesc
;
762 const nsINode
* curr
= &aNode
;
765 if (curr
->IsElement()) {
766 curr
->AsElement()->GetId(id
);
769 if (!elemDesc
.IsEmpty()) {
770 elemDesc
= elemDesc
+ u
"."_ns
;
773 if (!curr
->LocalName().IsEmpty()) {
774 elemDesc
.Append(curr
->LocalName());
776 elemDesc
.Append(curr
->NodeName());
780 elemDesc
= elemDesc
+ u
"['"_ns
+ id
+ u
"']"_ns
;
783 if (curr
->IsElement() &&
784 curr
->AsElement()->HasAttr(nsGkAtoms::contenteditable
)) {
786 curr
->AsElement()->GetAttr(nsGkAtoms::contenteditable
, val
);
787 elemDesc
= elemDesc
+ u
"[contenteditable=\""_ns
+ val
+ u
"\"]"_ns
;
789 if (curr
->IsDocument() && curr
->IsInDesignMode()) {
790 elemDesc
.Append(u
"[designMode=\"on\"]"_ns
);
793 curr
= curr
->GetParentNode();
796 NS_ConvertUTF16toUTF8
str(elemDesc
);
797 return aStream
<< str
.get();
800 nsIContent
* nsINode::DoGetShadowHost() const {
801 MOZ_ASSERT(IsShadowRoot());
802 return static_cast<const ShadowRoot
*>(this)->GetHost();
805 ShadowRoot
* nsINode::GetContainingShadow() const {
806 if (!IsInShadowTree()) {
809 return AsContent()->GetContainingShadow();
812 nsIContent
* nsINode::GetContainingShadowHost() const {
813 if (ShadowRoot
* shadow
= GetContainingShadow()) {
814 return shadow
->GetHost();
819 SVGUseElement
* nsINode::DoGetContainingSVGUseShadowHost() const {
820 MOZ_ASSERT(IsInShadowTree());
821 return SVGUseElement::FromNodeOrNull(GetContainingShadowHost());
824 void nsINode::GetNodeValueInternal(nsAString
& aNodeValue
) {
825 SetDOMStringToNull(aNodeValue
);
828 static const char* NodeTypeAsString(nsINode
* aNode
) {
829 static const char* NodeTypeStrings
[] = {
830 "", // No nodes of type 0
835 "an EntityReference",
837 "a ProcessingInstruction",
841 "a DocumentFragment",
844 static_assert(ArrayLength(NodeTypeStrings
) == nsINode::MAX_NODE_TYPE
+ 1,
845 "Max node type out of range for our array");
847 uint16_t nodeType
= aNode
->NodeType();
848 MOZ_RELEASE_ASSERT(nodeType
< ArrayLength(NodeTypeStrings
),
849 "Uknown out-of-range node type");
850 return NodeTypeStrings
[nodeType
];
853 nsINode
* nsINode::RemoveChild(nsINode
& aOldChild
, ErrorResult
& aError
) {
854 if (!aOldChild
.IsContent()) {
855 // aOldChild can't be one of our children.
856 aError
.ThrowNotFoundError(
857 "The node to be removed is not a child of this node");
861 if (aOldChild
.GetParentNode() == this) {
862 nsContentUtils::MaybeFireNodeRemoved(&aOldChild
, this);
865 // Check again, we may not be the child's parent anymore.
866 // Can be triggered by dom/base/crashtests/293388-1.html
867 if (aOldChild
.IsRootOfNativeAnonymousSubtree() ||
868 aOldChild
.GetParentNode() != this) {
869 // aOldChild isn't one of our children.
870 aError
.ThrowNotFoundError(
871 "The node to be removed is not a child of this node");
875 RemoveChildNode(aOldChild
.AsContent(), true);
879 void nsINode::Normalize() {
880 // First collect list of nodes to be removed
881 AutoTArray
<nsCOMPtr
<nsIContent
>, 50> nodes
;
883 bool canMerge
= false;
884 for (nsIContent
* node
= this->GetFirstChild(); node
;
885 node
= node
->GetNextNode(this)) {
886 if (node
->NodeType() != TEXT_NODE
) {
891 if (canMerge
|| node
->TextLength() == 0) {
892 // No need to touch canMerge. That way we can merge across empty
893 // textnodes if and only if the node before is a textnode
894 nodes
.AppendElement(node
);
899 // If there's no following sibling, then we need to ensure that we don't
900 // collect following siblings of our (grand)parent as to-be-removed
901 canMerge
= canMerge
&& !!node
->GetNextSibling();
904 if (nodes
.IsEmpty()) {
908 // We're relying on mozAutoSubtreeModified to keep the doc alive here.
909 RefPtr
<Document
> doc
= OwnerDoc();
911 // Batch possible DOMSubtreeModified events.
912 mozAutoSubtreeModified
subtree(doc
, nullptr);
914 // Fire all DOMNodeRemoved events. Optimize the common case of there being
916 bool hasRemoveListeners
= nsContentUtils::HasMutationListeners(
917 doc
, NS_EVENT_BITS_MUTATION_NODEREMOVED
);
918 if (hasRemoveListeners
) {
919 for (nsCOMPtr
<nsIContent
>& node
: nodes
) {
920 // Node may have already been removed.
921 if (nsCOMPtr
<nsINode
> parentNode
= node
->GetParentNode()) {
923 nsContentUtils::MaybeFireNodeRemoved(MOZ_KnownLive(node
), parentNode
);
928 mozAutoDocUpdate
batch(doc
, true);
930 // Merge and remove all nodes
932 for (uint32_t i
= 0; i
< nodes
.Length(); ++i
) {
933 nsIContent
* node
= nodes
[i
];
934 // Merge with previous node unless empty
935 const nsTextFragment
* text
= node
->GetText();
936 if (text
->GetLength()) {
937 nsIContent
* target
= node
->GetPreviousSibling();
939 (target
&& target
->NodeType() == TEXT_NODE
) || hasRemoveListeners
,
940 "Should always have a previous text sibling unless "
941 "mutation events messed us up");
942 if (!hasRemoveListeners
|| (target
&& target
->NodeType() == TEXT_NODE
)) {
943 nsTextNode
* t
= static_cast<nsTextNode
*>(target
);
945 t
->AppendTextForNormalize(text
->Get2b(), text
->GetLength(), true,
949 text
->AppendTo(tmpStr
);
950 t
->AppendTextForNormalize(tmpStr
.get(), tmpStr
.Length(), true, node
);
956 nsCOMPtr
<nsINode
> parent
= node
->GetParentNode();
957 NS_ASSERTION(parent
|| hasRemoveListeners
,
958 "Should always have a parent unless "
959 "mutation events messed us up");
961 parent
->RemoveChildNode(node
, true);
966 nsresult
nsINode::GetBaseURI(nsAString
& aURI
) const {
967 nsIURI
* baseURI
= GetBaseURI();
971 nsresult rv
= baseURI
->GetSpec(spec
);
972 NS_ENSURE_SUCCESS(rv
, rv
);
975 CopyUTF8toUTF16(spec
, aURI
);
979 void nsINode::GetBaseURIFromJS(nsAString
& aURI
, CallerType aCallerType
,
980 ErrorResult
& aRv
) const {
981 nsIURI
* baseURI
= GetBaseURI(aCallerType
== CallerType::System
);
984 nsresult res
= baseURI
->GetSpec(spec
);
985 if (NS_FAILED(res
)) {
990 CopyUTF8toUTF16(spec
, aURI
);
993 nsIURI
* nsINode::GetBaseURIObject() const { return GetBaseURI(true); }
995 void nsINode::LookupPrefix(const nsAString
& aNamespaceURI
, nsAString
& aPrefix
) {
996 if (Element
* nsElement
= GetNameSpaceElement()) {
997 // XXX Waiting for DOM spec to list error codes.
999 // Trace up the content parent chain looking for the namespace
1000 // declaration that defines the aNamespaceURI namespace. Once found,
1001 // return the prefix (i.e. the attribute localName).
1002 for (Element
* element
: nsElement
->InclusiveAncestorsOfType
<Element
>()) {
1003 uint32_t attrCount
= element
->GetAttrCount();
1005 for (uint32_t i
= 0; i
< attrCount
; ++i
) {
1006 const nsAttrName
* name
= element
->GetAttrNameAt(i
);
1008 if (name
->NamespaceEquals(kNameSpaceID_XMLNS
) &&
1009 element
->AttrValueIs(kNameSpaceID_XMLNS
, name
->LocalName(),
1010 aNamespaceURI
, eCaseMatters
)) {
1011 // If the localName is "xmlns", the prefix we output should be
1013 nsAtom
* localName
= name
->LocalName();
1015 if (localName
!= nsGkAtoms::xmlns
) {
1016 localName
->ToString(aPrefix
);
1018 SetDOMStringToNull(aPrefix
);
1026 SetDOMStringToNull(aPrefix
);
1029 uint16_t nsINode::CompareDocumentPosition(nsINode
& aOtherNode
,
1030 Maybe
<uint32_t>* aThisIndex
,
1031 Maybe
<uint32_t>* aOtherIndex
) const {
1032 if (this == &aOtherNode
) {
1035 if (GetPreviousSibling() == &aOtherNode
) {
1036 MOZ_ASSERT(GetParentNode() == aOtherNode
.GetParentNode());
1037 return Node_Binding::DOCUMENT_POSITION_PRECEDING
;
1039 if (GetNextSibling() == &aOtherNode
) {
1040 MOZ_ASSERT(GetParentNode() == aOtherNode
.GetParentNode());
1041 return Node_Binding::DOCUMENT_POSITION_FOLLOWING
;
1044 AutoTArray
<const nsINode
*, 32> parents1
, parents2
;
1046 const nsINode
* node1
= &aOtherNode
;
1047 const nsINode
* node2
= this;
1049 // Check if either node is an attribute
1050 const Attr
* attr1
= Attr::FromNode(node1
);
1052 const Element
* elem
= attr1
->GetElement();
1053 // If there is an owner element add the attribute
1054 // to the chain and walk up to the element
1057 parents1
.AppendElement(attr1
);
1060 if (auto* attr2
= Attr::FromNode(node2
)) {
1061 const Element
* elem
= attr2
->GetElement();
1062 if (elem
== node1
&& attr1
) {
1063 // Both nodes are attributes on the same element.
1064 // Compare position between the attributes.
1067 const nsAttrName
* attrName
;
1068 for (i
= 0; (attrName
= elem
->GetAttrNameAt(i
)); ++i
) {
1069 if (attrName
->Equals(attr1
->NodeInfo())) {
1070 NS_ASSERTION(!attrName
->Equals(attr2
->NodeInfo()),
1071 "Different attrs at same position");
1072 return Node_Binding::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC
|
1073 Node_Binding::DOCUMENT_POSITION_PRECEDING
;
1075 if (attrName
->Equals(attr2
->NodeInfo())) {
1076 return Node_Binding::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC
|
1077 Node_Binding::DOCUMENT_POSITION_FOLLOWING
;
1080 MOZ_ASSERT_UNREACHABLE("neither attribute in the element");
1081 return Node_Binding::DOCUMENT_POSITION_DISCONNECTED
;
1086 parents2
.AppendElement(attr2
);
1090 // We now know that both nodes are either nsIContents or Documents.
1091 // If either node started out as an attribute, that attribute will have
1092 // the same relative position as its ownerElement, except if the
1093 // ownerElement ends up being the container for the other node
1095 // Build the chain of parents
1097 parents1
.AppendElement(node1
);
1098 node1
= node1
->GetParentNode();
1101 parents2
.AppendElement(node2
);
1102 node2
= node2
->GetParentNode();
1105 // Check if the nodes are disconnected.
1106 uint32_t pos1
= parents1
.Length();
1107 uint32_t pos2
= parents2
.Length();
1108 const nsINode
* top1
= parents1
.ElementAt(--pos1
);
1109 const nsINode
* top2
= parents2
.ElementAt(--pos2
);
1112 ? (Node_Binding::DOCUMENT_POSITION_PRECEDING
|
1113 Node_Binding::DOCUMENT_POSITION_DISCONNECTED
|
1114 Node_Binding::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC
)
1115 : (Node_Binding::DOCUMENT_POSITION_FOLLOWING
|
1116 Node_Binding::DOCUMENT_POSITION_DISCONNECTED
|
1117 Node_Binding::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC
);
1120 // Find where the parent chain differs and check indices in the parent.
1121 const nsINode
* parent
= top1
;
1123 for (len
= std::min(pos1
, pos2
); len
> 0; --len
) {
1124 const nsINode
* child1
= parents1
.ElementAt(--pos1
);
1125 const nsINode
* child2
= parents2
.ElementAt(--pos2
);
1126 if (child1
!= child2
) {
1127 // child1 or child2 can be an attribute here. This will work fine since
1128 // ComputeIndexOf will return Nothing for the attribute making the
1129 // attribute be considered before any child.
1130 Maybe
<uint32_t> child1Index
;
1131 bool cachedChild1Index
= false;
1132 if (&aOtherNode
== child1
&& aOtherIndex
) {
1133 cachedChild1Index
= true;
1134 child1Index
= aOtherIndex
->isSome() ? *aOtherIndex
1135 : parent
->ComputeIndexOf(child1
);
1137 child1Index
= parent
->ComputeIndexOf(child1
);
1140 Maybe
<uint32_t> child2Index
;
1141 bool cachedChild2Index
= false;
1142 if (this == child2
&& aThisIndex
) {
1143 cachedChild2Index
= true;
1145 aThisIndex
->isSome() ? *aThisIndex
: parent
->ComputeIndexOf(child2
);
1147 child2Index
= parent
->ComputeIndexOf(child2
);
1150 uint16_t retVal
= child1Index
< child2Index
1151 ? Node_Binding::DOCUMENT_POSITION_PRECEDING
1152 : Node_Binding::DOCUMENT_POSITION_FOLLOWING
;
1154 if (cachedChild1Index
) {
1155 *aOtherIndex
= child1Index
;
1157 if (cachedChild2Index
) {
1158 *aThisIndex
= child2Index
;
1166 // We hit the end of one of the parent chains without finding a difference
1167 // between the chains. That must mean that one node is an ancestor of the
1168 // other. The one with the shortest chain must be the ancestor.
1169 return pos1
< pos2
? (Node_Binding::DOCUMENT_POSITION_PRECEDING
|
1170 Node_Binding::DOCUMENT_POSITION_CONTAINS
)
1171 : (Node_Binding::DOCUMENT_POSITION_FOLLOWING
|
1172 Node_Binding::DOCUMENT_POSITION_CONTAINED_BY
);
1175 bool nsINode::IsSameNode(nsINode
* other
) { return other
== this; }
1177 bool nsINode::IsEqualNode(nsINode
* aOther
) {
1182 // Might as well do a quick check to avoid walking our kids if we're
1183 // obviously the same.
1184 if (aOther
== this) {
1188 nsAutoString string1
, string2
;
1190 nsINode
* node1
= this;
1191 nsINode
* node2
= aOther
;
1193 uint16_t nodeType
= node1
->NodeType();
1194 if (nodeType
!= node2
->NodeType()) {
1198 mozilla::dom::NodeInfo
* nodeInfo1
= node1
->mNodeInfo
;
1199 mozilla::dom::NodeInfo
* nodeInfo2
= node2
->mNodeInfo
;
1200 if (!nodeInfo1
->Equals(nodeInfo2
) ||
1201 nodeInfo1
->GetExtraName() != nodeInfo2
->GetExtraName()) {
1206 case ELEMENT_NODE
: {
1207 // Both are elements (we checked that their nodeinfos are equal). Do the
1208 // check on attributes.
1209 Element
* element1
= node1
->AsElement();
1210 Element
* element2
= node2
->AsElement();
1211 uint32_t attrCount
= element1
->GetAttrCount();
1212 if (attrCount
!= element2
->GetAttrCount()) {
1216 // Iterate over attributes.
1217 for (uint32_t i
= 0; i
< attrCount
; ++i
) {
1218 const nsAttrName
* attrName
= element1
->GetAttrNameAt(i
);
1222 element1
->GetAttr(attrName
->NamespaceID(), attrName
->LocalName(),
1224 NS_ASSERTION(hasAttr
, "Why don't we have an attr?");
1226 if (!element2
->AttrValueIs(attrName
->NamespaceID(),
1227 attrName
->LocalName(), string1
,
1236 case CDATA_SECTION_NODE
:
1237 case PROCESSING_INSTRUCTION_NODE
: {
1238 MOZ_ASSERT(node1
->IsCharacterData());
1239 MOZ_ASSERT(node2
->IsCharacterData());
1240 auto* data1
= static_cast<CharacterData
*>(node1
);
1241 auto* data2
= static_cast<CharacterData
*>(node2
);
1243 if (!data1
->TextEquals(data2
)) {
1250 case DOCUMENT_FRAGMENT_NODE
:
1252 case ATTRIBUTE_NODE
: {
1253 NS_ASSERTION(node1
== this && node2
== aOther
,
1254 "Did we come upon an attribute node while walking a "
1256 node1
->GetNodeValue(string1
);
1257 node2
->GetNodeValue(string2
);
1259 // Returning here as to not bother walking subtree. And there is no
1260 // risk that we're half way through walking some other subtree since
1261 // attribute nodes doesn't appear in subtrees.
1262 return string1
.Equals(string2
);
1264 case DOCUMENT_TYPE_NODE
: {
1265 DocumentType
* docType1
= static_cast<DocumentType
*>(node1
);
1266 DocumentType
* docType2
= static_cast<DocumentType
*>(node2
);
1269 docType1
->GetPublicId(string1
);
1270 docType2
->GetPublicId(string2
);
1271 if (!string1
.Equals(string2
)) {
1276 docType1
->GetSystemId(string1
);
1277 docType2
->GetSystemId(string2
);
1278 if (!string1
.Equals(string2
)) {
1285 MOZ_ASSERT(false, "Unknown node type");
1288 nsINode
* nextNode
= node1
->GetFirstChild();
1291 node2
= node2
->GetFirstChild();
1293 if (node2
->GetFirstChild()) {
1294 // node2 has a firstChild, but node1 doesn't
1298 // Find next sibling, possibly walking parent chain.
1300 if (node1
== this) {
1301 NS_ASSERTION(node2
== aOther
,
1302 "Should have reached the start node "
1303 "for both trees at the same time");
1307 nextNode
= node1
->GetNextSibling();
1310 node2
= node2
->GetNextSibling();
1314 if (node2
->GetNextSibling()) {
1315 // node2 has a nextSibling, but node1 doesn't
1319 node1
= node1
->GetParentNode();
1320 node2
= node2
->GetParentNode();
1321 NS_ASSERTION(node1
&& node2
, "no parent while walking subtree");
1329 void nsINode::LookupNamespaceURI(const nsAString
& aNamespacePrefix
,
1330 nsAString
& aNamespaceURI
) {
1331 Element
* element
= GetNameSpaceElement();
1332 if (!element
|| NS_FAILED(element
->LookupNamespaceURIInternal(
1333 aNamespacePrefix
, aNamespaceURI
))) {
1334 SetDOMStringToNull(aNamespaceURI
);
1338 mozilla::Maybe
<mozilla::dom::EventCallbackDebuggerNotificationType
>
1339 nsINode::GetDebuggerNotificationType() const {
1340 return mozilla::Some(
1341 mozilla::dom::EventCallbackDebuggerNotificationType::Node
);
1344 bool nsINode::ComputeDefaultWantsUntrusted(ErrorResult
& aRv
) {
1345 return !nsContentUtils::IsChromeDoc(OwnerDoc());
1348 void nsINode::GetBoxQuads(const BoxQuadOptions
& aOptions
,
1349 nsTArray
<RefPtr
<DOMQuad
>>& aResult
,
1350 CallerType aCallerType
, mozilla::ErrorResult
& aRv
) {
1351 mozilla::GetBoxQuads(this, aOptions
, aResult
, aCallerType
, aRv
);
1354 void nsINode::GetBoxQuadsFromWindowOrigin(const BoxQuadOptions
& aOptions
,
1355 nsTArray
<RefPtr
<DOMQuad
>>& aResult
,
1356 mozilla::ErrorResult
& aRv
) {
1357 mozilla::GetBoxQuadsFromWindowOrigin(this, aOptions
, aResult
, aRv
);
1360 already_AddRefed
<DOMQuad
> nsINode::ConvertQuadFromNode(
1361 DOMQuad
& aQuad
, const GeometryNode
& aFrom
,
1362 const ConvertCoordinateOptions
& aOptions
, CallerType aCallerType
,
1364 return mozilla::ConvertQuadFromNode(this, aQuad
, aFrom
, aOptions
, aCallerType
,
1368 already_AddRefed
<DOMQuad
> nsINode::ConvertRectFromNode(
1369 DOMRectReadOnly
& aRect
, const GeometryNode
& aFrom
,
1370 const ConvertCoordinateOptions
& aOptions
, CallerType aCallerType
,
1372 return mozilla::ConvertRectFromNode(this, aRect
, aFrom
, aOptions
, aCallerType
,
1376 already_AddRefed
<DOMPoint
> nsINode::ConvertPointFromNode(
1377 const DOMPointInit
& aPoint
, const GeometryNode
& aFrom
,
1378 const ConvertCoordinateOptions
& aOptions
, CallerType aCallerType
,
1380 return mozilla::ConvertPointFromNode(this, aPoint
, aFrom
, aOptions
,
1384 bool nsINode::DispatchEvent(Event
& aEvent
, CallerType aCallerType
,
1386 // XXX sXBL/XBL2 issue -- do we really want the owner here? What
1387 // if that's the XBL document? Would we want its presshell? Or what?
1388 nsCOMPtr
<Document
> document
= OwnerDoc();
1390 // Do nothing if the element does not belong to a document
1395 // Obtain a presentation shell
1396 RefPtr
<nsPresContext
> context
= document
->GetPresContext();
1398 nsEventStatus status
= nsEventStatus_eIgnore
;
1399 nsresult rv
= EventDispatcher::DispatchDOMEvent(this, nullptr, &aEvent
,
1401 bool retval
= !aEvent
.DefaultPrevented(aCallerType
);
1402 if (NS_FAILED(rv
)) {
1408 nsresult
nsINode::PostHandleEvent(EventChainPostVisitor
& /*aVisitor*/) {
1412 EventListenerManager
* nsINode::GetOrCreateListenerManager() {
1413 return nsContentUtils::GetListenerManagerForNode(this);
1416 EventListenerManager
* nsINode::GetExistingListenerManager() const {
1417 return nsContentUtils::GetExistingListenerManagerForNode(this);
1420 nsPIDOMWindowOuter
* nsINode::GetOwnerGlobalForBindingsInternal() {
1422 // FIXME(bz): This cast is a bit bogus. See
1423 // https://bugzilla.mozilla.org/show_bug.cgi?id=1515709
1424 auto* window
= static_cast<nsGlobalWindowInner
*>(
1425 OwnerDoc()->GetScriptHandlingObject(dummy
));
1426 return window
? nsPIDOMWindowOuter::GetFromCurrentInner(window
) : nullptr;
1429 nsIGlobalObject
* nsINode::GetOwnerGlobal() const {
1431 return OwnerDoc()->GetScriptHandlingObject(dummy
);
1434 bool nsINode::UnoptimizableCCNode() const {
1435 return IsInNativeAnonymousSubtree() || IsAttr();
1439 bool nsINode::Traverse(nsINode
* tmp
, nsCycleCollectionTraversalCallback
& cb
) {
1440 if (MOZ_LIKELY(!cb
.WantAllTraces())) {
1441 Document
* currentDoc
= tmp
->GetComposedDoc();
1442 if (currentDoc
&& nsCCUncollectableMarker::InGeneration(
1443 currentDoc
->GetMarkedCCGeneration())) {
1447 if (nsCCUncollectableMarker::sGeneration
) {
1448 // If we're black no need to traverse.
1449 if (tmp
->HasKnownLiveWrapper() || tmp
->InCCBlackTree()) {
1453 if (!tmp
->UnoptimizableCCNode()) {
1454 // If we're in a black document, return early.
1455 if ((currentDoc
&& currentDoc
->HasKnownLiveWrapper())) {
1458 // If we're not in anonymous content and we have a black parent,
1460 nsIContent
* parent
= tmp
->GetParent();
1461 if (parent
&& !parent
->UnoptimizableCCNode() &&
1462 parent
->HasKnownLiveWrapper()) {
1463 MOZ_ASSERT(parent
->ComputeIndexOf(tmp
).isSome(),
1464 "Parent doesn't own us?");
1471 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfo
)
1472 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstChild
)
1473 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNextSibling
)
1474 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(GetParent())
1476 nsSlots
* slots
= tmp
->GetExistingSlots();
1478 slots
->Traverse(cb
);
1481 if (tmp
->HasProperties()) {
1482 nsCOMArray
<nsISupports
>* objects
= static_cast<nsCOMArray
<nsISupports
>*>(
1483 tmp
->GetProperty(nsGkAtoms::keepobjectsalive
));
1485 for (int32_t i
= 0; i
< objects
->Count(); ++i
) {
1486 cb
.NoteXPCOMChild(objects
->ObjectAt(i
));
1490 #ifdef ACCESSIBILITY
1491 AccessibleNode
* anode
= static_cast<AccessibleNode
*>(
1492 tmp
->GetProperty(nsGkAtoms::accessiblenode
));
1494 cb
.NoteXPCOMChild(anode
);
1499 if (tmp
->NodeType() != DOCUMENT_NODE
&&
1500 tmp
->HasFlag(NODE_HAS_LISTENERMANAGER
)) {
1501 nsContentUtils::TraverseListenerManager(tmp
, cb
);
1508 void nsINode::Unlink(nsINode
* tmp
) {
1509 tmp
->ReleaseWrapper(tmp
);
1511 if (nsSlots
* slots
= tmp
->GetExistingSlots()) {
1512 slots
->Unlink(*tmp
);
1515 if (tmp
->NodeType() != DOCUMENT_NODE
&&
1516 tmp
->HasFlag(NODE_HAS_LISTENERMANAGER
)) {
1517 nsContentUtils::RemoveListenerManager(tmp
);
1518 tmp
->UnsetFlags(NODE_HAS_LISTENERMANAGER
);
1521 if (tmp
->HasProperties()) {
1522 tmp
->RemoveProperty(nsGkAtoms::keepobjectsalive
);
1523 tmp
->RemoveProperty(nsGkAtoms::accessiblenode
);
1527 static void AdoptNodeIntoOwnerDoc(nsINode
* aParent
, nsINode
* aNode
,
1528 ErrorResult
& aError
) {
1529 NS_ASSERTION(!aNode
->GetParentNode(),
1530 "Should have removed from parent already");
1532 Document
* doc
= aParent
->OwnerDoc();
1534 DebugOnly
<nsINode
*> adoptedNode
= doc
->AdoptNode(*aNode
, aError
);
1537 if (!aError
.Failed()) {
1538 MOZ_ASSERT(aParent
->OwnerDoc() == doc
, "ownerDoc chainged while adopting");
1539 MOZ_ASSERT(adoptedNode
== aNode
, "Uh, adopt node changed nodes?");
1540 MOZ_ASSERT(aParent
->OwnerDoc() == aNode
->OwnerDoc(),
1541 "ownerDocument changed again after adopting!");
1546 static nsresult
UpdateGlobalsInSubtree(nsIContent
* aRoot
) {
1547 MOZ_ASSERT(ShouldUseNACScope(aRoot
));
1548 // Start off with no global so we don't fire any error events on failure.
1552 JSContext
* cx
= jsapi
.cx();
1555 JS::Rooted
<JSObject
*> reflector(cx
);
1556 for (nsIContent
* cur
= aRoot
; cur
; cur
= cur
->GetNextNode(aRoot
)) {
1557 if ((reflector
= cur
->GetWrapper())) {
1558 JSAutoRealm
ar(cx
, reflector
);
1559 UpdateReflectorGlobal(cx
, reflector
, rv
);
1560 rv
.WouldReportJSException();
1562 // We _could_ consider BlastSubtreeToPieces here, but it's not really
1563 // needed. Having some nodes in here accessible to content while others
1564 // are not is probably OK. We just need to fail out of the actual
1565 // insertion, so they're not in the DOM. Returning a failure here will
1567 return rv
.StealNSResult();
1575 void nsINode::InsertChildBefore(nsIContent
* aKid
, nsIContent
* aBeforeThis
,
1576 bool aNotify
, ErrorResult
& aRv
) {
1577 if (!IsContainerNode()) {
1578 aRv
.ThrowHierarchyRequestError(
1579 "Parent is not a Document, DocumentFragment, or Element node.");
1583 MOZ_ASSERT(!aKid
->GetParentNode(), "Inserting node that already has parent");
1584 MOZ_ASSERT(!IsAttr());
1586 // The id-handling code, and in the future possibly other code, need to
1587 // react to unexpected attribute changes.
1588 nsMutationGuard::DidMutate();
1590 // Do this before checking the child-count since this could cause mutations
1591 mozAutoDocUpdate
updateBatch(GetComposedDoc(), aNotify
);
1593 if (OwnerDoc() != aKid
->OwnerDoc()) {
1594 AdoptNodeIntoOwnerDoc(this, aKid
, aRv
);
1595 if (NS_WARN_IF(aRv
.Failed())) {
1601 AppendChildToChildList(aKid
);
1603 InsertChildToChildList(aKid
, aBeforeThis
);
1606 nsIContent
* parent
= IsContent() ? AsContent() : nullptr;
1608 // XXXbz Do we even need this code anymore?
1609 bool wasInNACScope
= ShouldUseNACScope(aKid
);
1610 BindContext
context(*this);
1611 aRv
= aKid
->BindToTree(context
, *this);
1612 if (!aRv
.Failed() && !wasInNACScope
&& ShouldUseNACScope(aKid
)) {
1613 MOZ_ASSERT(ShouldUseNACScope(this),
1614 "Why does the kid need to use an the anonymous content scope?");
1615 aRv
= UpdateGlobalsInSubtree(aKid
);
1618 DisconnectChild(aKid
);
1619 aKid
->UnbindFromTree();
1623 // Invalidate cached array of child nodes
1624 InvalidateChildNodes();
1626 NS_ASSERTION(aKid
->GetParentNode() == this,
1627 "Did we run script inappropriately?");
1630 // Note that we always want to call ContentInserted when things are added
1631 // as kids to documents
1632 if (parent
&& !aBeforeThis
) {
1633 MutationObservers::NotifyContentAppended(parent
, aKid
);
1635 MutationObservers::NotifyContentInserted(this, aKid
);
1638 if (nsContentUtils::HasMutationListeners(
1639 aKid
, NS_EVENT_BITS_MUTATION_NODEINSERTED
, this)) {
1640 InternalMutationEvent
mutation(true, eLegacyNodeInserted
);
1641 mutation
.mRelatedNode
= this;
1643 mozAutoSubtreeModified
subtree(OwnerDoc(), this);
1644 AsyncEventDispatcher::RunDOMEventWhenSafe(*aKid
, mutation
);
1649 nsIContent
* nsINode::GetPreviousSibling() const {
1650 // Do not expose circular linked list
1651 if (mPreviousOrLastSibling
&& !mPreviousOrLastSibling
->mNextSibling
) {
1654 return mPreviousOrLastSibling
;
1657 // CACHE_POINTER_SHIFT indicates how many steps to downshift the |this| pointer.
1658 // It should be small enough to not cause collisions between adjecent objects,
1659 // and large enough to make sure that all indexes are used.
1660 #define CACHE_POINTER_SHIFT 6
1661 #define CACHE_NUM_SLOTS 128
1662 #define CACHE_CHILD_LIMIT 10
1664 #define CACHE_GET_INDEX(_parent) \
1665 ((NS_PTR_TO_INT32(_parent) >> CACHE_POINTER_SHIFT) & (CACHE_NUM_SLOTS - 1))
1667 struct IndexCacheSlot
{
1668 const nsINode
* mParent
;
1669 const nsINode
* mChild
;
1670 uint32_t mChildIndex
;
1673 static IndexCacheSlot sIndexCache
[CACHE_NUM_SLOTS
];
1675 static inline void AddChildAndIndexToCache(const nsINode
* aParent
,
1676 const nsINode
* aChild
,
1677 uint32_t aChildIndex
) {
1678 uint32_t index
= CACHE_GET_INDEX(aParent
);
1679 sIndexCache
[index
].mParent
= aParent
;
1680 sIndexCache
[index
].mChild
= aChild
;
1681 sIndexCache
[index
].mChildIndex
= aChildIndex
;
1684 static inline void GetChildAndIndexFromCache(const nsINode
* aParent
,
1685 const nsINode
** aChild
,
1686 Maybe
<uint32_t>* aChildIndex
) {
1687 uint32_t index
= CACHE_GET_INDEX(aParent
);
1688 if (sIndexCache
[index
].mParent
== aParent
) {
1689 *aChild
= sIndexCache
[index
].mChild
;
1690 *aChildIndex
= Some(sIndexCache
[index
].mChildIndex
);
1693 *aChildIndex
= Nothing();
1697 static inline void RemoveFromCache(const nsINode
* aParent
) {
1698 uint32_t index
= CACHE_GET_INDEX(aParent
);
1699 if (sIndexCache
[index
].mParent
== aParent
) {
1700 sIndexCache
[index
] = {nullptr, nullptr, UINT32_MAX
};
1704 void nsINode::AppendChildToChildList(nsIContent
* aKid
) {
1706 MOZ_ASSERT(!aKid
->mNextSibling
);
1708 RemoveFromCache(this);
1711 nsIContent
* lastChild
= GetLastChild();
1712 lastChild
->mNextSibling
= aKid
;
1713 aKid
->mPreviousOrLastSibling
= lastChild
;
1718 // Maintain link to the last child
1719 mFirstChild
->mPreviousOrLastSibling
= aKid
;
1723 void nsINode::InsertChildToChildList(nsIContent
* aKid
,
1724 nsIContent
* aNextSibling
) {
1726 MOZ_ASSERT(aNextSibling
);
1728 RemoveFromCache(this);
1730 nsIContent
* previousSibling
= aNextSibling
->mPreviousOrLastSibling
;
1731 aNextSibling
->mPreviousOrLastSibling
= aKid
;
1732 aKid
->mPreviousOrLastSibling
= previousSibling
;
1733 aKid
->mNextSibling
= aNextSibling
;
1735 if (aNextSibling
== mFirstChild
) {
1736 MOZ_ASSERT(!previousSibling
->mNextSibling
);
1739 previousSibling
->mNextSibling
= aKid
;
1745 void nsINode::DisconnectChild(nsIContent
* aKid
) {
1747 MOZ_ASSERT(GetChildCount() > 0);
1749 RemoveFromCache(this);
1751 nsIContent
* previousSibling
= aKid
->GetPreviousSibling();
1752 nsCOMPtr
<nsIContent
> ref
= aKid
;
1754 if (aKid
->mNextSibling
) {
1755 aKid
->mNextSibling
->mPreviousOrLastSibling
= aKid
->mPreviousOrLastSibling
;
1757 // aKid is the last child in the list
1758 mFirstChild
->mPreviousOrLastSibling
= aKid
->mPreviousOrLastSibling
;
1760 aKid
->mPreviousOrLastSibling
= nullptr;
1762 if (previousSibling
) {
1763 previousSibling
->mNextSibling
= std::move(aKid
->mNextSibling
);
1765 // aKid is the first child in the list
1766 mFirstChild
= std::move(aKid
->mNextSibling
);
1772 nsIContent
* nsINode::GetChildAt_Deprecated(uint32_t aIndex
) const {
1773 if (aIndex
>= GetChildCount()) {
1777 nsIContent
* child
= mFirstChild
;
1779 child
= child
->GetNextSibling();
1785 int32_t nsINode::ComputeIndexOf_Deprecated(
1786 const nsINode
* aPossibleChild
) const {
1787 Maybe
<uint32_t> maybeIndex
= ComputeIndexOf(aPossibleChild
);
1791 MOZ_ASSERT(*maybeIndex
<= INT32_MAX
,
1792 "ComputeIndexOf_Deprecated() returns unsupported index value, use "
1793 "ComputeIndex() instead");
1794 return static_cast<int32_t>(*maybeIndex
);
1797 Maybe
<uint32_t> nsINode::ComputeIndexOf(const nsINode
* aPossibleChild
) const {
1798 if (!aPossibleChild
) {
1802 if (aPossibleChild
->GetParentNode() != this) {
1806 if (aPossibleChild
== GetLastChild()) {
1807 MOZ_ASSERT(GetChildCount());
1808 return Some(GetChildCount() - 1);
1811 if (mChildCount
>= CACHE_CHILD_LIMIT
) {
1812 const nsINode
* child
;
1813 Maybe
<uint32_t> maybeChildIndex
;
1814 GetChildAndIndexFromCache(this, &child
, &maybeChildIndex
);
1816 if (child
== aPossibleChild
) {
1817 return maybeChildIndex
;
1820 uint32_t nextIndex
= *maybeChildIndex
;
1821 uint32_t prevIndex
= *maybeChildIndex
;
1822 nsINode
* prev
= child
->GetPreviousSibling();
1823 nsINode
* next
= child
->GetNextSibling();
1826 MOZ_ASSERT(nextIndex
< UINT32_MAX
);
1828 if (next
== aPossibleChild
) {
1829 AddChildAndIndexToCache(this, aPossibleChild
, nextIndex
);
1830 return Some(nextIndex
);
1832 next
= next
->GetNextSibling();
1835 MOZ_ASSERT(prevIndex
> 0);
1837 if (prev
== aPossibleChild
) {
1838 AddChildAndIndexToCache(this, aPossibleChild
, prevIndex
);
1839 return Some(prevIndex
);
1841 prev
= prev
->GetPreviousSibling();
1843 } while (prev
|| next
);
1847 uint32_t index
= 0u;
1848 nsINode
* current
= mFirstChild
;
1850 MOZ_ASSERT(current
->GetParentNode() == this);
1851 if (current
== aPossibleChild
) {
1852 if (mChildCount
>= CACHE_CHILD_LIMIT
) {
1853 AddChildAndIndexToCache(this, current
, index
);
1857 current
= current
->GetNextSibling();
1858 MOZ_ASSERT(index
< UINT32_MAX
);
1865 Maybe
<uint32_t> nsINode::ComputeIndexInParentNode() const {
1866 nsINode
* parent
= GetParentNode();
1867 if (MOZ_UNLIKELY(!parent
)) {
1870 return parent
->ComputeIndexOf(this);
1873 Maybe
<uint32_t> nsINode::ComputeIndexInParentContent() const {
1874 nsIContent
* parent
= GetParent();
1875 if (MOZ_UNLIKELY(!parent
)) {
1878 return parent
->ComputeIndexOf(this);
1881 static already_AddRefed
<nsINode
> GetNodeFromNodeOrString(
1882 const OwningNodeOrString
& aNode
, Document
* aDocument
) {
1883 if (aNode
.IsNode()) {
1884 nsCOMPtr
<nsINode
> node
= aNode
.GetAsNode();
1885 return node
.forget();
1888 if (aNode
.IsString()) {
1889 RefPtr
<nsTextNode
> textNode
=
1890 aDocument
->CreateTextNode(aNode
.GetAsString());
1891 return textNode
.forget();
1894 MOZ_CRASH("Impossible type");
1898 * Implement the algorithm specified at
1899 * https://dom.spec.whatwg.org/#converting-nodes-into-a-node for |prepend()|,
1900 * |append()|, |before()|, |after()|, and |replaceWith()| APIs.
1902 MOZ_CAN_RUN_SCRIPT
static already_AddRefed
<nsINode
>
1903 ConvertNodesOrStringsIntoNode(const Sequence
<OwningNodeOrString
>& aNodes
,
1904 Document
* aDocument
, ErrorResult
& aRv
) {
1905 if (aNodes
.Length() == 1) {
1906 return GetNodeFromNodeOrString(aNodes
[0], aDocument
);
1909 nsCOMPtr
<nsINode
> fragment
= aDocument
->CreateDocumentFragment();
1911 for (const auto& node
: aNodes
) {
1912 nsCOMPtr
<nsINode
> childNode
= GetNodeFromNodeOrString(node
, aDocument
);
1913 fragment
->AppendChild(*childNode
, aRv
);
1919 return fragment
.forget();
1922 static void InsertNodesIntoHashset(const Sequence
<OwningNodeOrString
>& aNodes
,
1923 nsTHashSet
<nsINode
*>& aHashset
) {
1924 for (const auto& node
: aNodes
) {
1925 if (node
.IsNode()) {
1926 aHashset
.Insert(node
.GetAsNode());
1931 static nsINode
* FindViablePreviousSibling(
1932 const nsINode
& aNode
, const Sequence
<OwningNodeOrString
>& aNodes
) {
1933 nsTHashSet
<nsINode
*> nodeSet(16);
1934 InsertNodesIntoHashset(aNodes
, nodeSet
);
1936 nsINode
* viablePreviousSibling
= nullptr;
1937 for (nsINode
* sibling
= aNode
.GetPreviousSibling(); sibling
;
1938 sibling
= sibling
->GetPreviousSibling()) {
1939 if (!nodeSet
.Contains(sibling
)) {
1940 viablePreviousSibling
= sibling
;
1945 return viablePreviousSibling
;
1948 static nsINode
* FindViableNextSibling(
1949 const nsINode
& aNode
, const Sequence
<OwningNodeOrString
>& aNodes
) {
1950 nsTHashSet
<nsINode
*> nodeSet(16);
1951 InsertNodesIntoHashset(aNodes
, nodeSet
);
1953 nsINode
* viableNextSibling
= nullptr;
1954 for (nsINode
* sibling
= aNode
.GetNextSibling(); sibling
;
1955 sibling
= sibling
->GetNextSibling()) {
1956 if (!nodeSet
.Contains(sibling
)) {
1957 viableNextSibling
= sibling
;
1962 return viableNextSibling
;
1965 void nsINode::Before(const Sequence
<OwningNodeOrString
>& aNodes
,
1967 nsCOMPtr
<nsINode
> parent
= GetParentNode();
1972 nsCOMPtr
<nsINode
> viablePreviousSibling
=
1973 FindViablePreviousSibling(*this, aNodes
);
1975 nsCOMPtr
<Document
> doc
= OwnerDoc();
1976 nsCOMPtr
<nsINode
> node
= ConvertNodesOrStringsIntoNode(aNodes
, doc
, aRv
);
1981 viablePreviousSibling
= viablePreviousSibling
1982 ? viablePreviousSibling
->GetNextSibling()
1983 : parent
->GetFirstChild();
1985 parent
->InsertBefore(*node
, viablePreviousSibling
, aRv
);
1988 void nsINode::After(const Sequence
<OwningNodeOrString
>& aNodes
,
1990 nsCOMPtr
<nsINode
> parent
= GetParentNode();
1995 nsCOMPtr
<nsINode
> viableNextSibling
= FindViableNextSibling(*this, aNodes
);
1997 nsCOMPtr
<Document
> doc
= OwnerDoc();
1998 nsCOMPtr
<nsINode
> node
= ConvertNodesOrStringsIntoNode(aNodes
, doc
, aRv
);
2003 parent
->InsertBefore(*node
, viableNextSibling
, aRv
);
2006 void nsINode::ReplaceWith(const Sequence
<OwningNodeOrString
>& aNodes
,
2008 nsCOMPtr
<nsINode
> parent
= GetParentNode();
2013 nsCOMPtr
<nsINode
> viableNextSibling
= FindViableNextSibling(*this, aNodes
);
2015 nsCOMPtr
<Document
> doc
= OwnerDoc();
2016 nsCOMPtr
<nsINode
> node
= ConvertNodesOrStringsIntoNode(aNodes
, doc
, aRv
);
2021 if (parent
== GetParentNode()) {
2022 parent
->ReplaceChild(*node
, *this, aRv
);
2024 parent
->InsertBefore(*node
, viableNextSibling
, aRv
);
2028 void nsINode::Remove() {
2029 nsCOMPtr
<nsINode
> parent
= GetParentNode();
2034 parent
->RemoveChild(*this, IgnoreErrors());
2037 Element
* nsINode::GetFirstElementChild() const {
2038 for (nsIContent
* child
= GetFirstChild(); child
;
2039 child
= child
->GetNextSibling()) {
2040 if (child
->IsElement()) {
2041 return child
->AsElement();
2048 Element
* nsINode::GetLastElementChild() const {
2049 for (nsIContent
* child
= GetLastChild(); child
;
2050 child
= child
->GetPreviousSibling()) {
2051 if (child
->IsElement()) {
2052 return child
->AsElement();
2059 static bool MatchAttribute(Element
* aElement
, int32_t aNamespaceID
,
2060 nsAtom
* aAttrName
, void* aData
) {
2061 MOZ_ASSERT(aElement
, "Must have content node to work with!");
2062 nsString
* attrValue
= static_cast<nsString
*>(aData
);
2063 if (aNamespaceID
!= kNameSpaceID_Unknown
&&
2064 aNamespaceID
!= kNameSpaceID_Wildcard
) {
2065 return attrValue
->EqualsLiteral("*")
2066 ? aElement
->HasAttr(aNamespaceID
, aAttrName
)
2067 : aElement
->AttrValueIs(aNamespaceID
, aAttrName
, *attrValue
,
2071 // Qualified name match. This takes more work.
2072 uint32_t count
= aElement
->GetAttrCount();
2073 for (uint32_t i
= 0; i
< count
; ++i
) {
2074 const nsAttrName
* name
= aElement
->GetAttrNameAt(i
);
2076 if (name
->IsAtom()) {
2077 nameMatch
= name
->Atom() == aAttrName
;
2078 } else if (aNamespaceID
== kNameSpaceID_Wildcard
) {
2079 nameMatch
= name
->NodeInfo()->Equals(aAttrName
);
2081 nameMatch
= name
->NodeInfo()->QualifiedNameEquals(aAttrName
);
2085 return attrValue
->EqualsLiteral("*") ||
2086 aElement
->AttrValueIs(name
->NamespaceID(), name
->LocalName(),
2087 *attrValue
, eCaseMatters
);
2094 already_AddRefed
<nsIHTMLCollection
> nsINode::GetElementsByAttribute(
2095 const nsAString
& aAttribute
, const nsAString
& aValue
) {
2096 RefPtr
<nsAtom
> attrAtom(NS_Atomize(aAttribute
));
2097 RefPtr
<nsContentList
> list
= new nsContentList(
2098 this, MatchAttribute
, nsContentUtils::DestroyMatchString
,
2099 new nsString(aValue
), true, attrAtom
, kNameSpaceID_Unknown
);
2101 return list
.forget();
2104 already_AddRefed
<nsIHTMLCollection
> nsINode::GetElementsByAttributeNS(
2105 const nsAString
& aNamespaceURI
, const nsAString
& aAttribute
,
2106 const nsAString
& aValue
, ErrorResult
& aRv
) {
2107 RefPtr
<nsAtom
> attrAtom(NS_Atomize(aAttribute
));
2109 int32_t nameSpaceId
= kNameSpaceID_Wildcard
;
2110 if (!aNamespaceURI
.EqualsLiteral("*")) {
2111 nsresult rv
= nsNameSpaceManager::GetInstance()->RegisterNameSpace(
2112 aNamespaceURI
, nameSpaceId
);
2113 if (NS_FAILED(rv
)) {
2119 RefPtr
<nsContentList
> list
= new nsContentList(
2120 this, MatchAttribute
, nsContentUtils::DestroyMatchString
,
2121 new nsString(aValue
), true, attrAtom
, nameSpaceId
);
2122 return list
.forget();
2125 void nsINode::Prepend(const Sequence
<OwningNodeOrString
>& aNodes
,
2127 nsCOMPtr
<Document
> doc
= OwnerDoc();
2128 nsCOMPtr
<nsINode
> node
= ConvertNodesOrStringsIntoNode(aNodes
, doc
, aRv
);
2133 nsCOMPtr
<nsIContent
> refNode
= mFirstChild
;
2134 InsertBefore(*node
, refNode
, aRv
);
2137 void nsINode::Append(const Sequence
<OwningNodeOrString
>& aNodes
,
2139 nsCOMPtr
<Document
> doc
= OwnerDoc();
2140 nsCOMPtr
<nsINode
> node
= ConvertNodesOrStringsIntoNode(aNodes
, doc
, aRv
);
2145 AppendChild(*node
, aRv
);
2148 // https://dom.spec.whatwg.org/#dom-parentnode-replacechildren
2149 void nsINode::ReplaceChildren(const Sequence
<OwningNodeOrString
>& aNodes
,
2151 nsCOMPtr
<Document
> doc
= OwnerDoc();
2152 nsCOMPtr
<nsINode
> node
= ConvertNodesOrStringsIntoNode(aNodes
, doc
, aRv
);
2157 return ReplaceChildren(node
, aRv
);
2160 void nsINode::ReplaceChildren(nsINode
* aNode
, ErrorResult
& aRv
) {
2162 EnsurePreInsertionValidity(*aNode
, nullptr, aRv
);
2167 nsCOMPtr
<nsINode
> node
= aNode
;
2169 // Batch possible DOMSubtreeModified events.
2170 mozAutoSubtreeModified
subtree(OwnerDoc(), nullptr);
2172 if (nsContentUtils::HasMutationListeners(
2173 OwnerDoc(), NS_EVENT_BITS_MUTATION_NODEREMOVED
)) {
2174 FireNodeRemovedForChildren();
2176 if (node
->NodeType() == DOCUMENT_FRAGMENT_NODE
) {
2177 node
->FireNodeRemovedForChildren();
2178 } else if (nsCOMPtr
<nsINode
> parent
= node
->GetParentNode()) {
2179 nsContentUtils::MaybeFireNodeRemoved(node
, parent
);
2184 // Needed when used in combination with contenteditable (maybe)
2185 mozAutoDocUpdate
updateBatch(OwnerDoc(), true);
2187 nsAutoMutationBatch
mb(this, true, true);
2189 // The code above explicitly dispatched DOMNodeRemoved events if needed.
2190 nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker
;
2192 // Replace all with node within this.
2193 while (mFirstChild
) {
2194 RemoveChildNode(mFirstChild
, true);
2199 AppendChild(*aNode
, aRv
);
2204 void nsINode::RemoveChildNode(nsIContent
* aKid
, bool aNotify
) {
2205 // NOTE: This function must not trigger any calls to
2206 // Document::GetRootElement() calls until *after* it has removed aKid from
2207 // aChildArray. Any calls before then could potentially restore a stale
2208 // value for our cached root element, per note in
2209 // Document::RemoveChildNode().
2210 MOZ_ASSERT(aKid
&& aKid
->GetParentNode() == this, "Bogus aKid");
2211 MOZ_ASSERT(!IsAttr());
2213 nsMutationGuard::DidMutate();
2214 mozAutoDocUpdate
updateBatch(GetComposedDoc(), aNotify
);
2216 nsIContent
* previousSibling
= aKid
->GetPreviousSibling();
2218 // Since aKid is use also after DisconnectChild, ensure it stays alive.
2219 nsCOMPtr
<nsIContent
> kungfuDeathGrip
= aKid
;
2220 DisconnectChild(aKid
);
2222 // Invalidate cached array of child nodes
2223 InvalidateChildNodes();
2226 MutationObservers::NotifyContentRemoved(this, aKid
, previousSibling
);
2229 aKid
->UnbindFromTree();
2232 // When replacing, aRefChild is the content being replaced; when
2233 // inserting it's the content before which we're inserting. In the
2234 // latter case it may be null.
2236 // If aRv is a failure after this call, the insertion should not happen.
2238 // This implements the parts of
2239 // https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity and
2240 // the checks in https://dom.spec.whatwg.org/#concept-node-replace that
2241 // depend on the child nodes or come after steps that depend on the child nodes
2242 // (steps 2-6 in both cases).
2243 static void EnsureAllowedAsChild(nsINode
* aNewChild
, nsINode
* aParent
,
2244 bool aIsReplace
, nsINode
* aRefChild
,
2246 MOZ_ASSERT(aNewChild
, "Must have new child");
2247 MOZ_ASSERT_IF(aIsReplace
, aRefChild
);
2248 MOZ_ASSERT(aParent
);
2249 MOZ_ASSERT(aParent
->IsDocument() || aParent
->IsDocumentFragment() ||
2250 aParent
->IsElement(),
2251 "Nodes that are not documents, document fragments or elements "
2252 "can't be parents!");
2255 // A common case is that aNewChild has no kids, in which case
2256 // aParent can't be a descendant of aNewChild unless they're
2257 // actually equal to each other. Fast-path that case, since aParent
2258 // could be pretty deep in the DOM tree.
2259 if (aNewChild
== aParent
||
2260 ((aNewChild
->GetFirstChild() ||
2261 // HTML template elements and ShadowRoot hosts need
2262 // to be checked to ensure that they are not inserted into
2263 // the hosted content.
2264 aNewChild
->NodeInfo()->NameAtom() == nsGkAtoms::_template
||
2265 (aNewChild
->IsElement() && aNewChild
->AsElement()->GetShadowRoot())) &&
2266 nsContentUtils::ContentIsHostIncludingDescendantOf(aParent
,
2268 aRv
.ThrowHierarchyRequestError(
2269 "The new child is an ancestor of the parent");
2274 if (aRefChild
&& aRefChild
->GetParentNode() != aParent
) {
2276 if (aNewChild
->GetParentNode() == aParent
) {
2277 aRv
.ThrowNotFoundError(
2278 "New child already has this parent and old child does not. Please "
2279 "check the order of replaceChild's arguments.");
2281 aRv
.ThrowNotFoundError(
2282 "Child to be replaced is not a child of this node");
2285 aRv
.ThrowNotFoundError(
2286 "Child to insert before is not a child of this node");
2292 if (!aNewChild
->IsContent()) {
2293 aRv
.ThrowHierarchyRequestError(nsPrintfCString(
2294 "May not add %s as a child", NodeTypeAsString(aNewChild
)));
2298 // Steps 5 and 6 combined.
2299 // The allowed child nodes differ for documents and elements
2300 switch (aNewChild
->NodeType()) {
2301 case nsINode::COMMENT_NODE
:
2302 case nsINode::PROCESSING_INSTRUCTION_NODE
:
2305 case nsINode::TEXT_NODE
:
2306 case nsINode::CDATA_SECTION_NODE
:
2307 case nsINode::ENTITY_REFERENCE_NODE
:
2308 // Allowed under Elements and DocumentFragments
2309 if (aParent
->NodeType() == nsINode::DOCUMENT_NODE
) {
2310 aRv
.ThrowHierarchyRequestError(
2311 nsPrintfCString("Cannot insert %s as a child of a Document",
2312 NodeTypeAsString(aNewChild
)));
2315 case nsINode::ELEMENT_NODE
: {
2316 if (!aParent
->IsDocument()) {
2317 // Always ok to have elements under other elements or document fragments
2321 Document
* parentDocument
= aParent
->AsDocument();
2322 Element
* rootElement
= parentDocument
->GetRootElement();
2324 // Already have a documentElement, so this is only OK if we're
2326 if (!aIsReplace
|| rootElement
!= aRefChild
) {
2327 aRv
.ThrowHierarchyRequestError(
2328 "Cannot have more than one Element child of a Document");
2333 // We don't have a documentElement yet. Our one remaining constraint is
2334 // that the documentElement must come after the doctype.
2336 // Appending is just fine.
2340 nsIContent
* docTypeContent
= parentDocument
->GetDoctype();
2341 if (!docTypeContent
) {
2346 // The docTypeContent is retrived from the child list of the Document
2347 // node so that doctypeIndex is never Nothing.
2348 const Maybe
<uint32_t> doctypeIndex
=
2349 aParent
->ComputeIndexOf(docTypeContent
);
2350 MOZ_ASSERT(doctypeIndex
.isSome());
2351 // If aRefChild is an NAC, its index can be Nothing.
2352 const Maybe
<uint32_t> insertIndex
= aParent
->ComputeIndexOf(aRefChild
);
2354 // Now we're OK in the following two cases only:
2355 // 1) We're replacing something that's not before the doctype
2356 // 2) We're inserting before something that comes after the doctype
2357 const bool ok
= MOZ_LIKELY(insertIndex
.isSome()) &&
2358 (aIsReplace
? *insertIndex
>= *doctypeIndex
2359 : *insertIndex
> *doctypeIndex
);
2361 aRv
.ThrowHierarchyRequestError(
2362 "Cannot insert a root element before the doctype");
2366 case nsINode::DOCUMENT_TYPE_NODE
: {
2367 if (!aParent
->IsDocument()) {
2368 // doctypes only allowed under documents
2369 aRv
.ThrowHierarchyRequestError(
2370 nsPrintfCString("Cannot insert a DocumentType as a child of %s",
2371 NodeTypeAsString(aParent
)));
2375 Document
* parentDocument
= aParent
->AsDocument();
2376 nsIContent
* docTypeContent
= parentDocument
->GetDoctype();
2377 if (docTypeContent
) {
2378 // Already have a doctype, so this is only OK if we're replacing it
2379 if (!aIsReplace
|| docTypeContent
!= aRefChild
) {
2380 aRv
.ThrowHierarchyRequestError(
2381 "Cannot have more than one DocumentType child of a Document");
2386 // We don't have a doctype yet. Our one remaining constraint is
2387 // that the doctype must come before the documentElement.
2388 Element
* rootElement
= parentDocument
->GetRootElement();
2395 // Trying to append a doctype, but have a documentElement
2396 aRv
.ThrowHierarchyRequestError(
2397 "Cannot have a DocumentType node after the root element");
2401 // rootElement is now in the child list of the Document node so that
2402 // ComputeIndexOf must success to find it.
2403 const Maybe
<uint32_t> rootIndex
= aParent
->ComputeIndexOf(rootElement
);
2404 MOZ_ASSERT(rootIndex
.isSome());
2405 const Maybe
<uint32_t> insertIndex
= aParent
->ComputeIndexOf(aRefChild
);
2407 // Now we're OK if and only if insertIndex <= rootIndex. Indeed, either
2408 // we end up replacing aRefChild or we end up before it. Either one is
2409 // ok as long as aRefChild is not after rootElement.
2410 if (MOZ_LIKELY(insertIndex
.isSome()) && *insertIndex
> *rootIndex
) {
2411 aRv
.ThrowHierarchyRequestError(
2412 "Cannot have a DocumentType node after the root element");
2416 case nsINode::DOCUMENT_FRAGMENT_NODE
: {
2417 // Note that for now we only allow nodes inside document fragments if
2418 // they're allowed inside elements. If we ever change this to allow
2419 // doctype nodes in document fragments, we'll need to update this code.
2420 // Also, there's a version of this code in ReplaceOrInsertBefore. If you
2421 // change this code, change that too.
2422 if (!aParent
->IsDocument()) {
2427 bool sawElement
= false;
2428 for (nsIContent
* child
= aNewChild
->GetFirstChild(); child
;
2429 child
= child
->GetNextSibling()) {
2430 if (child
->IsElement()) {
2432 // Can't put two elements into a document
2433 aRv
.ThrowHierarchyRequestError(
2434 "Cannot have more than one Element child of a Document");
2439 // If we can put this content at the right place, we might be ok;
2440 // if not, we bail out.
2441 EnsureAllowedAsChild(child
, aParent
, aIsReplace
, aRefChild
, aRv
);
2447 // Everything in the fragment checked out ok, so we can stick it in here
2452 * aNewChild is of invalid type.
2457 // XXXbz when can we reach this?
2458 aRv
.ThrowHierarchyRequestError(nsPrintfCString("Cannot insert %s inside %s",
2459 NodeTypeAsString(aNewChild
),
2460 NodeTypeAsString(aParent
)));
2464 // https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity
2465 void nsINode::EnsurePreInsertionValidity(nsINode
& aNewChild
, nsINode
* aRefChild
,
2466 ErrorResult
& aError
) {
2467 EnsurePreInsertionValidity1(aError
);
2468 if (aError
.Failed()) {
2471 EnsurePreInsertionValidity2(false, aNewChild
, aRefChild
, aError
);
2474 // Implements the parts of
2475 // https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity and
2476 // the checks in https://dom.spec.whatwg.org/#concept-node-replace that can be
2477 // evaluated before ever looking at the child nodes (step 1 in both cases).
2478 void nsINode::EnsurePreInsertionValidity1(ErrorResult
& aError
) {
2479 if (!IsDocument() && !IsDocumentFragment() && !IsElement()) {
2480 aError
.ThrowHierarchyRequestError(
2481 nsPrintfCString("Cannot add children to %s", NodeTypeAsString(this)));
2486 void nsINode::EnsurePreInsertionValidity2(bool aReplace
, nsINode
& aNewChild
,
2488 ErrorResult
& aError
) {
2489 if (aNewChild
.IsRootOfNativeAnonymousSubtree()) {
2490 // This is anonymous content. Don't allow its insertion
2491 // anywhere, since it might have UnbindFromTree calls coming
2493 aError
.ThrowNotSupportedError(
2494 "Inserting anonymous content manually is not supported");
2498 // Make sure that the inserted node is allowed as a child of its new parent.
2499 EnsureAllowedAsChild(&aNewChild
, this, aReplace
, aRefChild
, aError
);
2502 nsINode
* nsINode::ReplaceOrInsertBefore(bool aReplace
, nsINode
* aNewChild
,
2504 ErrorResult
& aError
) {
2505 // XXXbz I wish I could assert that nsContentUtils::IsSafeToRunScript() so we
2506 // could rely on scriptblockers going out of scope to actually run XBL
2507 // teardown, but various crud adds nodes under scriptblockers (e.g. native
2508 // anonymous content). The only good news is those insertions can't trigger
2509 // the bad XBL cases.
2510 MOZ_ASSERT_IF(aReplace
, aRefChild
);
2512 // Before firing DOMNodeRemoved events, make sure this is actually an insert
2514 EnsurePreInsertionValidity1(aError
);
2515 if (aError
.Failed()) {
2519 EnsurePreInsertionValidity2(aReplace
, *aNewChild
, aRefChild
, aError
);
2520 if (aError
.Failed()) {
2524 uint16_t nodeType
= aNewChild
->NodeType();
2526 // Before we do anything else, fire all DOMNodeRemoved mutation events
2527 // We do this up front as to avoid having to deal with script running
2528 // at random places further down.
2529 // Scope firing mutation events so that we don't carry any state that
2532 nsMutationGuard guard
;
2534 // If we're replacing, fire for node-to-be-replaced.
2535 // If aRefChild == aNewChild then we'll fire for it in check below
2536 if (aReplace
&& aRefChild
!= aNewChild
) {
2537 nsContentUtils::MaybeFireNodeRemoved(aRefChild
, this);
2540 // If the new node already has a parent, fire for removing from old
2542 if (nsCOMPtr
<nsINode
> oldParent
= aNewChild
->GetParentNode()) {
2543 nsContentUtils::MaybeFireNodeRemoved(aNewChild
, oldParent
);
2546 // If we're inserting a fragment, fire for all the children of the
2548 if (nodeType
== DOCUMENT_FRAGMENT_NODE
) {
2549 static_cast<FragmentOrElement
*>(aNewChild
)->FireNodeRemovedForChildren();
2552 if (guard
.Mutated(0)) {
2553 // Re-check the parts of our pre-insertion validity that might depend on
2555 EnsurePreInsertionValidity2(aReplace
, *aNewChild
, aRefChild
, aError
);
2556 if (aError
.Failed()) {
2562 // Record the node to insert before, if any
2563 nsIContent
* nodeToInsertBefore
;
2565 nodeToInsertBefore
= aRefChild
->GetNextSibling();
2567 // Since aRefChild is our child, it must be an nsIContent object.
2568 nodeToInsertBefore
= aRefChild
? aRefChild
->AsContent() : nullptr;
2570 if (nodeToInsertBefore
== aNewChild
) {
2571 // We're going to remove aNewChild from its parent, so use its next sibling
2572 // as the node to insert before.
2573 nodeToInsertBefore
= nodeToInsertBefore
->GetNextSibling();
2576 Maybe
<AutoTArray
<nsCOMPtr
<nsIContent
>, 50>> fragChildren
;
2578 // Remove the new child from the old parent if one exists
2579 nsIContent
* newContent
= aNewChild
->AsContent();
2580 nsCOMPtr
<nsINode
> oldParent
= newContent
->GetParentNode();
2582 // Hold a strong ref to nodeToInsertBefore across the removal of newContent
2583 nsCOMPtr
<nsINode
> kungFuDeathGrip
= nodeToInsertBefore
;
2585 // Removing a child can run script, via XBL destructors.
2586 nsMutationGuard guard
;
2588 // Scope for the mutation batch and scriptblocker, so they go away
2589 // while kungFuDeathGrip is still alive.
2591 mozAutoDocUpdate
batch(newContent
->GetComposedDoc(), true);
2592 nsAutoMutationBatch
mb(oldParent
, true, true);
2593 // ScriptBlocker ensures previous and next stay alive.
2594 nsIContent
* previous
= aNewChild
->GetPreviousSibling();
2595 nsIContent
* next
= aNewChild
->GetNextSibling();
2596 oldParent
->RemoveChildNode(aNewChild
->AsContent(), true);
2597 if (nsAutoMutationBatch::GetCurrentBatch() == &mb
) {
2599 mb
.SetPrevSibling(previous
);
2600 mb
.SetNextSibling(next
);
2604 // We expect one mutation (the removal) to have happened.
2605 if (guard
.Mutated(1)) {
2606 // XBL destructors, yuck.
2608 // Verify that newContent has no parent.
2609 if (newContent
->GetParentNode()) {
2610 aError
.ThrowHierarchyRequestError(
2611 "New child was inserted somewhere else");
2615 // And verify that newContent is still allowed as our child.
2616 if (aNewChild
== aRefChild
) {
2617 // We've already removed aRefChild. So even if we were doing a replace,
2618 // now we're doing a simple insert before nodeToInsertBefore.
2619 EnsureAllowedAsChild(newContent
, this, false, nodeToInsertBefore
,
2621 if (aError
.Failed()) {
2625 EnsureAllowedAsChild(newContent
, this, aReplace
, aRefChild
, aError
);
2626 if (aError
.Failed()) {
2630 // And recompute nodeToInsertBefore, just in case.
2632 nodeToInsertBefore
= aRefChild
->GetNextSibling();
2634 nodeToInsertBefore
= aRefChild
? aRefChild
->AsContent() : nullptr;
2638 } else if (nodeType
== DOCUMENT_FRAGMENT_NODE
) {
2639 // Make sure to remove all the fragment's kids. We need to do this before
2640 // we start inserting anything, so we will run out XBL destructors and
2641 // binding teardown (GOD, I HATE THESE THINGS) before we insert anything
2643 uint32_t count
= newContent
->GetChildCount();
2645 fragChildren
.emplace();
2647 // Copy the children into a separate array to avoid having to deal with
2648 // mutations to the fragment later on here.
2649 fragChildren
->SetCapacity(count
);
2650 for (nsIContent
* child
= newContent
->GetFirstChild(); child
;
2651 child
= child
->GetNextSibling()) {
2652 NS_ASSERTION(child
->GetUncomposedDoc() == nullptr,
2653 "How did we get a child with a current doc?");
2654 fragChildren
->AppendElement(child
);
2657 // Hold a strong ref to nodeToInsertBefore across the removals
2658 nsCOMPtr
<nsINode
> kungFuDeathGrip
= nodeToInsertBefore
;
2660 nsMutationGuard guard
;
2662 // Scope for the mutation batch and scriptblocker, so they go away
2663 // while kungFuDeathGrip is still alive.
2665 mozAutoDocUpdate
batch(newContent
->GetComposedDoc(), true);
2666 nsAutoMutationBatch
mb(newContent
, false, true);
2668 while (newContent
->HasChildren()) {
2669 newContent
->RemoveChildNode(newContent
->GetLastChild(), true);
2673 // We expect |count| removals
2674 if (guard
.Mutated(count
)) {
2675 // XBL destructors, yuck.
2677 // Verify that nodeToInsertBefore, if non-null, is still our child. If
2678 // it's not, there's no way we can do this insert sanely; just bail out.
2679 if (nodeToInsertBefore
&& nodeToInsertBefore
->GetParent() != this) {
2680 aError
.ThrowHierarchyRequestError("Don't know where to insert child");
2684 // Verify that all the things in fragChildren have no parent.
2685 for (uint32_t i
= 0; i
< count
; ++i
) {
2686 if (fragChildren
->ElementAt(i
)->GetParentNode()) {
2687 aError
.ThrowHierarchyRequestError(
2688 "New child was inserted somewhere else");
2693 // Note that unlike the single-element case above, none of our kids can
2694 // be aRefChild, so we can always pass through aReplace in the
2695 // EnsureAllowedAsChild checks below and don't have to worry about whether
2696 // recomputing nodeToInsertBefore is OK.
2698 // Verify that our aRefChild is still sensible
2699 if (aRefChild
&& aRefChild
->GetParent() != this) {
2700 aError
.ThrowHierarchyRequestError("Don't know where to insert child");
2704 // Recompute nodeToInsertBefore, just in case.
2706 nodeToInsertBefore
= aRefChild
->GetNextSibling();
2708 // If aRefChild has 'this' as a parent, it must be an nsIContent.
2709 nodeToInsertBefore
= aRefChild
? aRefChild
->AsContent() : nullptr;
2712 // And verify that newContent is still allowed as our child. Sadly, we
2713 // need to reimplement the relevant part of EnsureAllowedAsChild() because
2714 // now our nodes are in an array and all. If you change this code,
2715 // change the code there.
2717 bool sawElement
= false;
2718 for (uint32_t i
= 0; i
< count
; ++i
) {
2719 nsIContent
* child
= fragChildren
->ElementAt(i
);
2720 if (child
->IsElement()) {
2723 aError
.ThrowHierarchyRequestError(
2724 "Cannot have more than one Element child of a Document");
2729 EnsureAllowedAsChild(child
, this, aReplace
, aRefChild
, aError
);
2730 if (aError
.Failed()) {
2738 mozAutoDocUpdate
batch(GetComposedDoc(), true);
2739 nsAutoMutationBatch mb
;
2741 // If we're replacing and we haven't removed aRefChild yet, do so now
2742 if (aReplace
&& aRefChild
!= aNewChild
) {
2743 mb
.Init(this, true, true);
2745 // Since aRefChild is never null in the aReplace case, we know that at
2746 // this point nodeToInsertBefore is the next sibling of aRefChild.
2747 NS_ASSERTION(aRefChild
->GetNextSibling() == nodeToInsertBefore
,
2748 "Unexpected nodeToInsertBefore");
2750 nsIContent
* toBeRemoved
= nodeToInsertBefore
2751 ? nodeToInsertBefore
->GetPreviousSibling()
2753 MOZ_ASSERT(toBeRemoved
);
2755 RemoveChildNode(toBeRemoved
, true);
2758 // Move new child over to our document if needed. Do this after removing
2759 // it from its parent so that AdoptNode doesn't fire DOMNodeRemoved
2760 // DocumentType nodes are the only nodes that can have a null
2761 // ownerDocument according to the DOM spec, and we need to allow
2762 // inserting them w/o calling AdoptNode().
2763 Document
* doc
= OwnerDoc();
2764 if (doc
!= newContent
->OwnerDoc()) {
2765 AdoptNodeIntoOwnerDoc(this, aNewChild
, aError
);
2766 if (aError
.Failed()) {
2772 * Check if we're inserting a document fragment. If we are, we need
2773 * to actually add its children individually (i.e. we don't add the
2774 * actual document fragment).
2776 nsINode
* result
= aReplace
? aRefChild
: aNewChild
;
2777 if (nodeType
== DOCUMENT_FRAGMENT_NODE
) {
2778 nsAutoMutationBatch
* mutationBatch
= nsAutoMutationBatch::GetCurrentBatch();
2779 if (mutationBatch
&& mutationBatch
!= &mb
) {
2780 mutationBatch
= nullptr;
2781 } else if (!aReplace
) {
2782 mb
.Init(this, true, true);
2783 mutationBatch
= nsAutoMutationBatch::GetCurrentBatch();
2786 if (mutationBatch
) {
2787 mutationBatch
->RemovalDone();
2788 mutationBatch
->SetPrevSibling(
2789 nodeToInsertBefore
? nodeToInsertBefore
->GetPreviousSibling()
2791 mutationBatch
->SetNextSibling(nodeToInsertBefore
);
2794 uint32_t count
= fragChildren
->Length();
2799 bool appending
= !IsDocument() && !nodeToInsertBefore
;
2800 nsIContent
* firstInsertedContent
= fragChildren
->ElementAt(0);
2802 // Iterate through the fragment's children, and insert them in the new
2804 for (uint32_t i
= 0; i
< count
; ++i
) {
2805 // XXXbz how come no reparenting here? That seems odd...
2806 // Insert the child.
2807 InsertChildBefore(fragChildren
->ElementAt(i
), nodeToInsertBefore
,
2808 !appending
, aError
);
2809 if (aError
.Failed()) {
2810 // Make sure to notify on any children that we did succeed to insert
2811 if (appending
&& i
!= 0) {
2812 MutationObservers::NotifyContentAppended(
2813 static_cast<nsIContent
*>(this), firstInsertedContent
);
2819 if (mutationBatch
&& !appending
) {
2820 mutationBatch
->NodesAdded();
2823 // Notify and fire mutation events when appending
2825 MutationObservers::NotifyContentAppended(static_cast<nsIContent
*>(this),
2826 firstInsertedContent
);
2827 if (mutationBatch
) {
2828 mutationBatch
->NodesAdded();
2830 // Optimize for the case when there are no listeners
2831 if (nsContentUtils::HasMutationListeners(
2832 doc
, NS_EVENT_BITS_MUTATION_NODEINSERTED
)) {
2833 Element::FireNodeInserted(doc
, this, *fragChildren
);
2837 // Not inserting a fragment but rather a single node.
2839 // FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=544654
2840 // We need to reparent here for nodes for which the parent of their
2841 // wrapper is not the wrapper for their ownerDocument (XUL elements,
2842 // form controls, ...). Also applies in the fragment code above.
2843 if (nsAutoMutationBatch::GetCurrentBatch() == &mb
) {
2845 mb
.SetPrevSibling(nodeToInsertBefore
2846 ? nodeToInsertBefore
->GetPreviousSibling()
2848 mb
.SetNextSibling(nodeToInsertBefore
);
2850 InsertChildBefore(newContent
, nodeToInsertBefore
, true, aError
);
2851 if (aError
.Failed()) {
2859 void nsINode::BindObject(nsISupports
* aObject
) {
2860 nsCOMArray
<nsISupports
>* objects
= static_cast<nsCOMArray
<nsISupports
>*>(
2861 GetProperty(nsGkAtoms::keepobjectsalive
));
2863 objects
= new nsCOMArray
<nsISupports
>();
2864 SetProperty(nsGkAtoms::keepobjectsalive
, objects
,
2865 nsINode::DeleteProperty
<nsCOMArray
<nsISupports
>>, true);
2867 objects
->AppendObject(aObject
);
2870 void nsINode::UnbindObject(nsISupports
* aObject
) {
2871 nsCOMArray
<nsISupports
>* objects
= static_cast<nsCOMArray
<nsISupports
>*>(
2872 GetProperty(nsGkAtoms::keepobjectsalive
));
2874 objects
->RemoveObject(aObject
);
2878 already_AddRefed
<AccessibleNode
> nsINode::GetAccessibleNode() {
2879 #ifdef ACCESSIBILITY
2880 nsresult rv
= NS_OK
;
2882 RefPtr
<AccessibleNode
> anode
=
2883 static_cast<AccessibleNode
*>(GetProperty(nsGkAtoms::accessiblenode
, &rv
));
2884 if (NS_FAILED(rv
)) {
2885 anode
= new AccessibleNode(this);
2886 RefPtr
<AccessibleNode
> temp
= anode
;
2887 rv
= SetProperty(nsGkAtoms::accessiblenode
, temp
.forget().take(),
2888 nsPropertyTable::SupportsDtorFunc
, true);
2889 if (NS_FAILED(rv
)) {
2890 NS_WARNING("SetProperty failed");
2894 return anode
.forget();
2900 void nsINode::AddSizeOfExcludingThis(nsWindowSizes
& aSizes
,
2901 size_t* aNodeSize
) const {
2902 EventListenerManager
* elm
= GetExistingListenerManager();
2904 *aNodeSize
+= elm
->SizeOfIncludingThis(aSizes
.mState
.mMallocSizeOf
);
2907 // Measurement of the following members may be added later if DMD finds it is
2912 // The following members are not measured:
2913 // - mParent, mNextSibling, mPreviousOrLastSibling, mFirstChild: because
2914 // they're non-owning, from "exclusive ownership" point of view.
2917 void nsINode::AddSizeOfIncludingThis(nsWindowSizes
& aSizes
,
2918 size_t* aNodeSize
) const {
2919 *aNodeSize
+= aSizes
.mState
.mMallocSizeOf(this);
2920 AddSizeOfExcludingThis(aSizes
, aNodeSize
);
2923 bool nsINode::Contains(const nsINode
* aOther
) const {
2924 if (aOther
== this) {
2928 if (!aOther
|| OwnerDoc() != aOther
->OwnerDoc() ||
2929 IsInUncomposedDoc() != aOther
->IsInUncomposedDoc() ||
2930 !aOther
->IsContent() || !HasChildren()) {
2935 // document.contains(aOther) returns true if aOther is in the document,
2936 // but is not in any anonymous subtree.
2937 // IsInUncomposedDoc() check is done already before this.
2938 return !aOther
->IsInNativeAnonymousSubtree();
2941 if (!IsElement() && !IsDocumentFragment()) {
2945 if (IsInShadowTree() != aOther
->IsInShadowTree() ||
2946 IsInNativeAnonymousSubtree() != aOther
->IsInNativeAnonymousSubtree()) {
2950 if (IsInNativeAnonymousSubtree()) {
2951 if (GetClosestNativeAnonymousSubtreeRoot() !=
2952 aOther
->GetClosestNativeAnonymousSubtreeRoot()) {
2957 if (IsInShadowTree()) {
2958 ShadowRoot
* otherRoot
= aOther
->GetContainingShadow();
2959 if (IsShadowRoot()) {
2960 return otherRoot
== this;
2962 if (otherRoot
!= GetContainingShadow()) {
2967 return aOther
->IsInclusiveDescendantOf(this);
2970 uint32_t nsINode::Length() const {
2971 switch (NodeType()) {
2972 case DOCUMENT_TYPE_NODE
:
2976 case CDATA_SECTION_NODE
:
2977 case PROCESSING_INSTRUCTION_NODE
:
2979 MOZ_ASSERT(IsContent());
2980 return AsContent()->TextLength();
2983 return GetChildCount();
2987 const StyleSelectorList
* nsINode::ParseSelectorList(
2988 const nsACString
& aSelectorString
, ErrorResult
& aRv
) {
2989 Document
* doc
= OwnerDoc();
2991 Document::SelectorCache
& cache
= doc
->GetSelectorCache();
2992 StyleSelectorList
* list
= cache
.GetListOrInsertFrom(aSelectorString
, [&] {
2993 // Note that we want to cache even if null was returned, because we
2994 // want to cache the "This is not a valid selector" result.
2996 // NOTE(emilio): Off-hand, getting a CallerType here might seem like a
2997 // better idea than using ChromeRulesEnabled(), but that would mean
2998 // that we'd need to key the selector cache by that.
2999 // ChromeRulesEnabled() gives us the same semantics as any inline
3000 // style associated to a document, which seems reasonable.
3002 Servo_SelectorList_Parse(&aSelectorString
, doc
->ChromeRulesEnabled()));
3006 // Invalid selector.
3007 aRv
.ThrowSyntaxError("'"_ns
+ aSelectorString
+
3008 "' is not a valid selector"_ns
);
3014 // Given an id, find first element with that id under aRoot.
3015 // If none found, return nullptr. aRoot must be in the document.
3016 inline static Element
* FindMatchingElementWithId(
3017 const nsAString
& aId
, const Element
& aRoot
,
3018 const DocumentOrShadowRoot
& aContainingDocOrShadowRoot
) {
3019 MOZ_ASSERT(aRoot
.SubtreeRoot() == &aContainingDocOrShadowRoot
.AsNode());
3021 aRoot
.IsInUncomposedDoc() || aRoot
.IsInShadowTree(),
3022 "Don't call me if the root is not in the document or in a shadow tree");
3024 const nsTArray
<Element
*>* elements
=
3025 aContainingDocOrShadowRoot
.GetAllElementsForId(aId
);
3027 // Nothing to do; we're done
3031 // XXXbz: Should we fall back to the tree walk if |elements| is long,
3032 // for some value of "long"?
3033 for (Element
* element
: *elements
) {
3034 if (MOZ_UNLIKELY(element
== &aRoot
)) {
3038 if (!element
->IsInclusiveDescendantOf(&aRoot
)) {
3042 // We have an element with the right id and it's a strict descendant
3050 Element
* nsINode::QuerySelector(const nsACString
& aSelector
,
3051 ErrorResult
& aResult
) {
3052 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("nsINode::QuerySelector",
3053 LAYOUT_SelectorQuery
, aSelector
);
3055 const StyleSelectorList
* list
= ParseSelectorList(aSelector
, aResult
);
3059 const bool useInvalidation
= false;
3060 return const_cast<Element
*>(
3061 Servo_SelectorList_QueryFirst(this, list
, useInvalidation
));
3064 already_AddRefed
<nsINodeList
> nsINode::QuerySelectorAll(
3065 const nsACString
& aSelector
, ErrorResult
& aResult
) {
3066 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("nsINode::QuerySelectorAll",
3067 LAYOUT_SelectorQuery
, aSelector
);
3069 RefPtr
<nsSimpleContentList
> contentList
= new nsSimpleContentList(this);
3070 const StyleSelectorList
* list
= ParseSelectorList(aSelector
, aResult
);
3072 return contentList
.forget();
3075 const bool useInvalidation
= false;
3076 Servo_SelectorList_QueryAll(this, list
, contentList
.get(), useInvalidation
);
3077 return contentList
.forget();
3080 Element
* nsINode::GetElementById(const nsAString
& aId
) {
3081 MOZ_ASSERT(!IsShadowRoot(), "Should use the faster version");
3082 MOZ_ASSERT(IsElement() || IsDocumentFragment(),
3083 "Bogus this object for GetElementById call");
3084 if (IsInUncomposedDoc()) {
3085 MOZ_ASSERT(IsElement(), "Huh? A fragment in a document?");
3086 return FindMatchingElementWithId(aId
, *AsElement(), *OwnerDoc());
3089 if (ShadowRoot
* containingShadow
= AsContent()->GetContainingShadow()) {
3090 MOZ_ASSERT(IsElement(), "Huh? A fragment in a ShadowRoot?");
3091 return FindMatchingElementWithId(aId
, *AsElement(), *containingShadow
);
3094 for (nsIContent
* kid
= GetFirstChild(); kid
; kid
= kid
->GetNextNode(this)) {
3095 if (!kid
->IsElement()) {
3098 nsAtom
* id
= kid
->AsElement()->GetID();
3099 if (id
&& id
->Equals(aId
)) {
3100 return kid
->AsElement();
3106 JSObject
* nsINode::WrapObject(JSContext
* aCx
,
3107 JS::Handle
<JSObject
*> aGivenProto
) {
3108 // Make sure one of these is true
3109 // (1) our owner document has a script handling object,
3110 // (2) Our owner document has had a script handling object, or has been marked
3112 // (3) we are running a privileged script.
3113 // Event handling is possible only if (1). If (2) event handling is
3115 // If the document has never had a script handling object, untrusted
3116 // scripts (3) shouldn't touch it!
3117 bool hasHadScriptHandlingObject
= false;
3118 if (!OwnerDoc()->GetScriptHandlingObject(hasHadScriptHandlingObject
) &&
3119 !hasHadScriptHandlingObject
&& !nsContentUtils::IsSystemCaller(aCx
)) {
3120 Throw(aCx
, NS_ERROR_UNEXPECTED
);
3124 JS::Rooted
<JSObject
*> obj(aCx
, WrapNode(aCx
, aGivenProto
));
3125 if (obj
&& ChromeOnlyAccess()) {
3127 xpc::IsUnprivilegedJunkScope(JS::GetNonCCWObjectGlobal(obj
)) ||
3128 xpc::IsInUAWidgetScope(obj
) || xpc::AccessCheck::isChrome(obj
));
3133 already_AddRefed
<nsINode
> nsINode::CloneNode(bool aDeep
, ErrorResult
& aError
) {
3134 return Clone(aDeep
, nullptr, aError
);
3137 nsDOMAttributeMap
* nsINode::GetAttributes() {
3141 return AsElement()->Attributes();
3144 Element
* nsINode::GetParentElementCrossingShadowRoot() const {
3149 if (mParent
->IsElement()) {
3150 return mParent
->AsElement();
3153 if (ShadowRoot
* shadowRoot
= ShadowRoot::FromNode(mParent
)) {
3154 MOZ_ASSERT(shadowRoot
->GetHost(), "ShowRoots should always have a host");
3155 return shadowRoot
->GetHost();
3161 bool nsINode::HasBoxQuadsSupport(JSContext
* aCx
, JSObject
* /* unused */) {
3162 return xpc::AccessCheck::isChrome(js::GetContextCompartment(aCx
)) ||
3163 StaticPrefs::layout_css_getBoxQuads_enabled();
3166 nsINode
* nsINode::GetScopeChainParent() const { return nullptr; }
3168 Element
* nsINode::GetParentFlexElement() {
3173 nsIFrame
* primaryFrame
= AsContent()->GetPrimaryFrame(FlushType::Frames
);
3175 // Walk up the parent chain and pierce through any anonymous boxes
3176 // that might be between this frame and a possible flex parent.
3177 for (nsIFrame
* f
= primaryFrame
; f
; f
= f
->GetParent()) {
3178 if (f
!= primaryFrame
&& !f
->Style()->IsAnonBox()) {
3179 // We hit a non-anonymous ancestor before finding a flex item.
3183 if (f
->IsFlexItem()) {
3184 return f
->GetParent()->GetContent()->AsElement();
3191 Element
* nsINode::GetNearestInclusiveOpenPopover() const {
3192 for (auto* el
: InclusiveFlatTreeAncestorsOfType
<Element
>()) {
3193 if (el
->IsAutoPopover() && el
->IsPopoverOpen()) {
3200 Element
* nsINode::GetNearestInclusiveTargetPopoverForInvoker() const {
3201 for (auto* el
: InclusiveFlatTreeAncestorsOfType
<Element
>()) {
3202 if (auto* popover
= el
->GetEffectivePopoverTargetElement()) {
3203 if (popover
->IsAutoPopover() && popover
->IsPopoverOpen()) {
3211 nsGenericHTMLElement
* nsINode::GetEffectivePopoverTargetElement() const {
3212 const auto* formControl
=
3213 nsGenericHTMLFormControlElementWithState::FromNode(this);
3214 if (!formControl
|| formControl
->IsDisabled() ||
3215 !formControl
->IsButtonControl()) {
3218 if (auto* popover
= nsGenericHTMLElement::FromNodeOrNull(
3219 formControl
->GetPopoverTargetElement())) {
3220 if (popover
->GetPopoverAttributeState() != PopoverAttributeState::None
) {
3227 Element
* nsINode::GetTopmostClickedPopover() const {
3228 Element
* clickedPopover
= GetNearestInclusiveOpenPopover();
3229 Element
* invokedPopover
= GetNearestInclusiveTargetPopoverForInvoker();
3230 if (!clickedPopover
) {
3231 return invokedPopover
;
3233 auto autoPopoverList
= clickedPopover
->OwnerDoc()->AutoPopoverList();
3234 for (Element
* el
: Reversed(autoPopoverList
)) {
3235 if (el
== clickedPopover
|| el
== invokedPopover
) {
3242 void nsINode::AddAnimationObserver(nsIAnimationObserver
* aAnimationObserver
) {
3243 AddMutationObserver(aAnimationObserver
);
3244 OwnerDoc()->SetMayHaveAnimationObservers();
3247 void nsINode::AddAnimationObserverUnlessExists(
3248 nsIAnimationObserver
* aAnimationObserver
) {
3249 AddMutationObserverUnlessExists(aAnimationObserver
);
3250 OwnerDoc()->SetMayHaveAnimationObservers();
3253 already_AddRefed
<nsINode
> nsINode::CloneAndAdopt(
3254 nsINode
* aNode
, bool aClone
, bool aDeep
,
3255 nsNodeInfoManager
* aNewNodeInfoManager
,
3256 JS::Handle
<JSObject
*> aReparentScope
, nsINode
* aParent
,
3257 ErrorResult
& aError
) {
3258 MOZ_ASSERT((!aClone
&& aNewNodeInfoManager
) || !aReparentScope
,
3259 "If cloning or not getting a new nodeinfo we shouldn't rewrap");
3260 MOZ_ASSERT(!aParent
|| aNode
->IsContent(),
3261 "Can't insert document or attribute nodes into a parent");
3263 // First deal with aNode and walk its attributes (and their children). Then,
3264 // if aDeep is true, deal with aNode's children (and recurse into their
3265 // attributes and children).
3267 nsAutoScriptBlocker scriptBlocker
;
3269 nsNodeInfoManager
* nodeInfoManager
= aNewNodeInfoManager
;
3272 class NodeInfo
* nodeInfo
= aNode
->mNodeInfo
;
3273 RefPtr
<class NodeInfo
> newNodeInfo
;
3274 if (nodeInfoManager
) {
3275 // Don't allow importing/adopting nodes from non-privileged "scriptable"
3276 // documents to "non-scriptable" documents.
3277 Document
* newDoc
= nodeInfoManager
->GetDocument();
3278 if (NS_WARN_IF(!newDoc
)) {
3279 aError
.Throw(NS_ERROR_UNEXPECTED
);
3282 bool hasHadScriptHandlingObject
= false;
3283 if (!newDoc
->GetScriptHandlingObject(hasHadScriptHandlingObject
) &&
3284 !hasHadScriptHandlingObject
) {
3285 Document
* currentDoc
= aNode
->OwnerDoc();
3286 if (NS_WARN_IF(!nsContentUtils::IsChromeDoc(currentDoc
) &&
3287 (currentDoc
->GetScriptHandlingObject(
3288 hasHadScriptHandlingObject
) ||
3289 hasHadScriptHandlingObject
))) {
3290 aError
.Throw(NS_ERROR_UNEXPECTED
);
3295 newNodeInfo
= nodeInfoManager
->GetNodeInfo(
3296 nodeInfo
->NameAtom(), nodeInfo
->GetPrefixAtom(),
3297 nodeInfo
->NamespaceID(), nodeInfo
->NodeType(),
3298 nodeInfo
->GetExtraName());
3300 nodeInfo
= newNodeInfo
;
3303 Element
* elem
= Element::FromNode(aNode
);
3305 nsCOMPtr
<nsINode
> clone
;
3307 nsresult rv
= aNode
->Clone(nodeInfo
, getter_AddRefs(clone
));
3308 if (NS_WARN_IF(NS_FAILED(rv
))) {
3314 // If we're cloning we need to insert the cloned children into the cloned
3316 aParent
->AppendChildTo(static_cast<nsIContent
*>(clone
.get()), false,
3318 if (NS_WARN_IF(aError
.Failed())) {
3321 } else if (aDeep
&& clone
->IsDocument()) {
3322 // After cloning the document itself, we want to clone the children into
3323 // the cloned document (somewhat like cloning and importing them into the
3324 // cloned document).
3325 nodeInfoManager
= clone
->mNodeInfo
->NodeInfoManager();
3327 } else if (nodeInfoManager
) {
3328 Document
* oldDoc
= aNode
->OwnerDoc();
3330 DOMArena
* domArenaToStore
=
3331 !aNode
->HasFlag(NODE_KEEPS_DOMARENA
)
3332 ? aNode
->NodeInfo()->NodeInfoManager()->GetArenaAllocator()
3335 Document
* newDoc
= nodeInfoManager
->GetDocument();
3338 bool wasRegistered
= false;
3340 wasRegistered
= oldDoc
->UnregisterActivityObserver(elem
);
3343 const bool hadProperties
= aNode
->HasProperties();
3344 if (hadProperties
) {
3345 // NOTE: We want this to happen before NodeInfoChanged so that
3346 // NodeInfoChanged can use node properties normally.
3348 // When this fails, it removes all properties for the node anyway, so no
3349 // extra error handling needed.
3350 Unused
<< oldDoc
->PropertyTable().TransferOrRemoveAllPropertiesFor(
3351 aNode
, newDoc
->PropertyTable());
3354 aNode
->mNodeInfo
.swap(newNodeInfo
);
3355 aNode
->NodeInfoChanged(oldDoc
);
3357 MOZ_ASSERT(newDoc
!= oldDoc
);
3359 // Adopted callback must be enqueued whenever a node’s
3360 // shadow-including inclusive descendants that is custom.
3361 CustomElementData
* data
= elem
->GetCustomElementData();
3362 if (data
&& data
->mState
== CustomElementData::State::eCustom
) {
3363 LifecycleCallbackArgs args
;
3364 args
.mOldDocument
= oldDoc
;
3365 args
.mNewDocument
= newDoc
;
3367 nsContentUtils::EnqueueLifecycleCallback(ElementCallbackType::eAdopted
,
3372 // XXX what if oldDoc is null, we don't know if this should be
3373 // registered or not! Can that really happen?
3374 if (wasRegistered
) {
3375 newDoc
->RegisterActivityObserver(aNode
->AsElement());
3378 if (nsPIDOMWindowInner
* window
= newDoc
->GetInnerWindow()) {
3379 EventListenerManager
* elm
= aNode
->GetExistingListenerManager();
3381 window
->SetMutationListeners(elm
->MutationListenerBits());
3382 if (elm
->MayHavePaintEventListener()) {
3383 window
->SetHasPaintEventListeners();
3385 if (elm
->MayHaveTouchEventListener()) {
3386 window
->SetHasTouchEventListeners();
3388 if (elm
->MayHaveMouseEnterLeaveEventListener()) {
3389 window
->SetHasMouseEnterLeaveEventListeners();
3391 if (elm
->MayHavePointerEnterLeaveEventListener()) {
3392 window
->SetHasPointerEnterLeaveEventListeners();
3394 if (elm
->MayHaveSelectionChangeEventListener()) {
3395 window
->SetHasSelectionChangeEventListeners();
3397 if (elm
->MayHaveFormSelectEventListener()) {
3398 window
->SetHasFormSelectEventListeners();
3400 if (elm
->MayHaveTransitionEventListener()) {
3401 window
->SetHasTransitionEventListeners();
3405 if (wasRegistered
) {
3406 nsIContent
* content
= aNode
->AsContent();
3407 if (auto* mediaElem
= HTMLMediaElement::FromNodeOrNull(content
)) {
3408 mediaElem
->NotifyOwnerDocumentActivityChanged();
3410 nsCOMPtr
<nsIObjectLoadingContent
> objectLoadingContent(
3411 do_QueryInterface(aNode
));
3412 if (objectLoadingContent
) {
3413 nsObjectLoadingContent
* olc
=
3414 static_cast<nsObjectLoadingContent
*>(objectLoadingContent
.get());
3415 olc
->NotifyOwnerDocumentActivityChanged();
3417 // HTMLImageElement::FromNode is insufficient since we need this for
3418 // <svg:image> as well.
3419 nsCOMPtr
<nsIImageLoadingContent
> imageLoadingContent(
3420 do_QueryInterface(aNode
));
3421 if (imageLoadingContent
) {
3423 static_cast<nsImageLoadingContent
*>(imageLoadingContent
.get());
3424 ilc
->NotifyOwnerDocumentActivityChanged();
3429 if (oldDoc
->MayHaveDOMMutationObservers()) {
3430 newDoc
->SetMayHaveDOMMutationObservers();
3433 if (oldDoc
->MayHaveAnimationObservers()) {
3434 newDoc
->SetMayHaveAnimationObservers();
3438 elem
->RecompileScriptEventListeners();
3441 if (aReparentScope
) {
3443 JS::Rooted
<JSObject
*> wrapper(cx
);
3444 if ((wrapper
= aNode
->GetWrapper())) {
3445 MOZ_ASSERT(IsDOMObject(wrapper
));
3446 JSAutoRealm
ar(cx
, wrapper
);
3447 UpdateReflectorGlobal(cx
, wrapper
, aError
);
3448 if (aError
.Failed()) {
3449 if (wasRegistered
) {
3450 newDoc
->UnregisterActivityObserver(aNode
->AsElement());
3452 if (hadProperties
) {
3453 // NOTE: When it fails it removes all properties for the node
3454 // anyway, so no extra error handling needed.
3455 Unused
<< newDoc
->PropertyTable().TransferOrRemoveAllPropertiesFor(
3456 aNode
, oldDoc
->PropertyTable());
3458 aNode
->mNodeInfo
.swap(newNodeInfo
);
3459 aNode
->NodeInfoChanged(newDoc
);
3460 if (wasRegistered
) {
3461 oldDoc
->RegisterActivityObserver(aNode
->AsElement());
3468 // At this point, a new node is added to the document, and this
3469 // node isn't allocated by the NodeInfoManager of this document,
3470 // so we need to do this SetArenaAllocator logic to bypass
3471 // the !HasChildren() check in NodeInfoManager::Allocate.
3472 if (mozilla::StaticPrefs::dom_arena_allocator_enabled_AtStartup()) {
3473 if (!newDoc
->NodeInfoManager()->HasAllocated()) {
3474 if (DocGroup
* docGroup
= newDoc
->GetDocGroup()) {
3475 newDoc
->NodeInfoManager()->SetArenaAllocator(
3476 docGroup
->ArenaAllocator());
3480 if (domArenaToStore
&& newDoc
->GetDocGroup() != oldDoc
->GetDocGroup()) {
3481 nsContentUtils::AddEntryToDOMArenaTable(aNode
, domArenaToStore
);
3486 if (aDeep
&& (!aClone
|| !aNode
->IsAttr())) {
3487 // aNode's children.
3488 for (nsIContent
* cloneChild
= aNode
->GetFirstChild(); cloneChild
;
3489 cloneChild
= cloneChild
->GetNextSibling()) {
3490 nsCOMPtr
<nsINode
> child
=
3491 CloneAndAdopt(cloneChild
, aClone
, true, nodeInfoManager
,
3492 aReparentScope
, clone
, aError
);
3493 if (NS_WARN_IF(aError
.Failed())) {
3499 if (aDeep
&& aNode
->IsElement()) {
3501 if (nodeInfo
->GetDocument()->IsStaticDocument()) {
3502 // Clone any animations to the node in the static document, including
3503 // the current timing. They will need to be paused later after the new
3504 // document's pres shell gets initialized.
3506 // This needs to be done here rather than in Element::CopyInnerTo
3507 // because the animations clone code relies on the target (that is,
3508 // `clone`) being connected already.
3509 clone
->AsElement()->CloneAnimationsFrom(*aNode
->AsElement());
3511 // Clone the Shadow DOM
3512 ShadowRoot
* originalShadowRoot
= aNode
->AsElement()->GetShadowRoot();
3513 if (originalShadowRoot
) {
3514 RefPtr
<ShadowRoot
> newShadowRoot
=
3515 clone
->AsElement()->AttachShadowWithoutNameChecks(
3516 originalShadowRoot
->Mode());
3518 newShadowRoot
->CloneInternalDataFrom(originalShadowRoot
);
3519 for (nsIContent
* origChild
= originalShadowRoot
->GetFirstChild();
3520 origChild
; origChild
= origChild
->GetNextSibling()) {
3521 nsCOMPtr
<nsINode
> child
=
3522 CloneAndAdopt(origChild
, aClone
, aDeep
, nodeInfoManager
,
3523 aReparentScope
, newShadowRoot
, aError
);
3524 if (NS_WARN_IF(aError
.Failed())) {
3531 if (ShadowRoot
* shadowRoot
= aNode
->AsElement()->GetShadowRoot()) {
3532 nsCOMPtr
<nsINode
> child
=
3533 CloneAndAdopt(shadowRoot
, aClone
, aDeep
, nodeInfoManager
,
3534 aReparentScope
, clone
, aError
);
3535 if (NS_WARN_IF(aError
.Failed())) {
3542 // Cloning template element.
3543 if (aDeep
&& aClone
&& aNode
->IsTemplateElement()) {
3544 DocumentFragment
* origContent
=
3545 static_cast<HTMLTemplateElement
*>(aNode
)->Content();
3546 DocumentFragment
* cloneContent
=
3547 static_cast<HTMLTemplateElement
*>(clone
.get())->Content();
3549 // Clone the children into the clone's template content owner
3550 // document's nodeinfo manager.
3551 nsNodeInfoManager
* ownerNodeInfoManager
=
3552 cloneContent
->mNodeInfo
->NodeInfoManager();
3554 for (nsIContent
* cloneChild
= origContent
->GetFirstChild(); cloneChild
;
3555 cloneChild
= cloneChild
->GetNextSibling()) {
3556 nsCOMPtr
<nsINode
> child
=
3557 CloneAndAdopt(cloneChild
, aClone
, aDeep
, ownerNodeInfoManager
,
3558 aReparentScope
, cloneContent
, aError
);
3559 if (NS_WARN_IF(aError
.Failed())) {
3565 return clone
.forget();
3568 void nsINode::Adopt(nsNodeInfoManager
* aNewNodeInfoManager
,
3569 JS::Handle
<JSObject
*> aReparentScope
,
3570 mozilla::ErrorResult
& aError
) {
3571 if (aNewNodeInfoManager
) {
3572 Document
* beforeAdoptDoc
= OwnerDoc();
3573 Document
* afterAdoptDoc
= aNewNodeInfoManager
->GetDocument();
3575 MOZ_ASSERT(beforeAdoptDoc
);
3576 MOZ_ASSERT(afterAdoptDoc
);
3577 MOZ_ASSERT(beforeAdoptDoc
!= afterAdoptDoc
);
3579 if (afterAdoptDoc
->GetDocGroup() != beforeAdoptDoc
->GetDocGroup()) {
3580 // This is a temporary solution for Bug 1590526 to only limit
3581 // the restriction to chrome level documents because web extensions
3582 // rely on content to content node adoption.
3583 if (nsContentUtils::IsChromeDoc(afterAdoptDoc
) ||
3584 nsContentUtils::IsChromeDoc(beforeAdoptDoc
)) {
3585 return aError
.ThrowSecurityError(
3586 "Adopting nodes across docgroups in chrome documents "
3592 // Just need to store the return value of CloneAndAdopt in a
3593 // temporary nsCOMPtr to make sure we release it.
3594 nsCOMPtr
<nsINode
> node
= CloneAndAdopt(this, false, true, aNewNodeInfoManager
,
3595 aReparentScope
, nullptr, aError
);
3597 nsMutationGuard::DidMutate();
3600 already_AddRefed
<nsINode
> nsINode::Clone(bool aDeep
,
3601 nsNodeInfoManager
* aNewNodeInfoManager
,
3602 ErrorResult
& aError
) {
3603 return CloneAndAdopt(this, true, aDeep
, aNewNodeInfoManager
, nullptr, nullptr,
3607 void nsINode::GenerateXPath(nsAString
& aResult
) {
3608 XPathGenerator::Generate(this, aResult
);
3611 bool nsINode::IsApzAware() const { return IsNodeApzAware(); }
3613 bool nsINode::IsNodeApzAwareInternal() const {
3614 return EventTarget::IsApzAware();
3617 DocGroup
* nsINode::GetDocGroup() const { return OwnerDoc()->GetDocGroup(); }
3619 nsINode
* nsINode::GetFlattenedTreeParentNodeNonInline() const {
3620 return GetFlattenedTreeParentNode();
3623 ParentObject
nsINode::GetParentObject() const {
3624 ParentObject
p(OwnerDoc());
3625 // Note that mReflectionScope is a no-op for chrome, and other places where we
3626 // don't check this value.
3627 if (IsInNativeAnonymousSubtree()) {
3628 if (ShouldUseUAWidgetScope(this)) {
3629 p
.mReflectionScope
= ReflectionScope::UAWidget
;
3631 MOZ_ASSERT(ShouldUseNACScope(this));
3632 p
.mReflectionScope
= ReflectionScope::NAC
;
3635 MOZ_ASSERT(!ShouldUseNACScope(this));
3636 MOZ_ASSERT(!ShouldUseUAWidgetScope(this));
3641 void nsINode::AddMutationObserver(
3642 nsMultiMutationObserver
* aMultiMutationObserver
) {
3643 if (aMultiMutationObserver
) {
3644 NS_ASSERTION(!aMultiMutationObserver
->ContainsNode(this),
3645 "Observer already in the list");
3646 aMultiMutationObserver
->AddMutationObserverToNode(this);
3650 void nsINode::AddMutationObserverUnlessExists(
3651 nsMultiMutationObserver
* aMultiMutationObserver
) {
3652 if (aMultiMutationObserver
&& !aMultiMutationObserver
->ContainsNode(this)) {
3653 aMultiMutationObserver
->AddMutationObserverToNode(this);
3657 void nsINode::RemoveMutationObserver(
3658 nsMultiMutationObserver
* aMultiMutationObserver
) {
3659 if (aMultiMutationObserver
) {
3660 aMultiMutationObserver
->RemoveMutationObserverFromNode(this);
3664 void nsINode::FireNodeRemovedForChildren() {
3665 Document
* doc
= OwnerDoc();
3666 // Optimize the common case
3667 if (!nsContentUtils::HasMutationListeners(
3668 doc
, NS_EVENT_BITS_MUTATION_NODEREMOVED
)) {
3672 nsCOMPtr
<nsINode
> child
;
3673 for (child
= GetFirstChild(); child
&& child
->GetParentNode() == this;
3674 child
= child
->GetNextSibling()) {
3675 nsContentUtils::MaybeFireNodeRemoved(child
, this);
3679 NS_IMPL_ISUPPORTS(nsNodeWeakReference
, nsIWeakReference
)
3681 nsNodeWeakReference::nsNodeWeakReference(nsINode
* aNode
)
3682 : nsIWeakReference(aNode
) {}
3684 nsNodeWeakReference::~nsNodeWeakReference() {
3685 nsINode
* node
= static_cast<nsINode
*>(mObject
);
3688 NS_ASSERTION(node
->Slots()->mWeakReference
== this,
3689 "Weak reference has wrong value");
3690 node
->Slots()->mWeakReference
= nullptr;
3695 nsNodeWeakReference::QueryReferentFromScript(const nsIID
& aIID
,
3696 void** aInstancePtr
) {
3697 return QueryReferent(aIID
, aInstancePtr
);
3700 size_t nsNodeWeakReference::SizeOfOnlyThis(
3701 mozilla::MallocSizeOf aMallocSizeOf
) {
3702 return aMallocSizeOf(this);