Bug 1803984 - Add tests for the interaction between speculative preloading and module...
[gecko.git] / dom / base / nsINode.cpp
blobf95fd2bef1f3573f9c99a7215564f14f505b7a84
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/. */
7 /*
8 * Base class for all DOM nodes.
9 */
11 #include "nsINode.h"
13 #include "AccessCheck.h"
14 #include "jsapi.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"
63 #include "nsDOMCID.h"
64 #include "nsDOMCSSAttrDeclaration.h"
65 #include "nsError.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"
74 #include "nsAtom.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"
83 #include "nsView.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"
93 #include "nsRange.h"
94 #include "nsString.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"
104 #include <algorithm>
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"
116 #ifdef ACCESSIBILITY
117 # include "mozilla/dom/AccessibleNode.h"
118 #endif
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.");
143 if (aNode == this) {
144 return true;
147 if (!aNode->HasFlag(NODE_MAY_HAVE_ELEMENT_CHILDREN)) {
148 return GetParentNode() == aNode;
151 for (nsINode* node : Ancestors(*this)) {
152 if (node == aNode) {
153 return true;
156 return false;
159 bool nsINode::IsInclusiveFlatTreeDescendantOf(const nsINode* aNode) const {
160 MOZ_ASSERT(aNode, "The node is nullptr.");
162 for (nsINode* node : InclusiveFlatTreeAncestors(*this)) {
163 if (node == aNode) {
164 return true;
167 return false;
170 bool nsINode::IsShadowIncludingInclusiveDescendantOf(
171 const nsINode* aNode) const {
172 MOZ_ASSERT(aNode, "The node is nullptr.");
174 if (this->GetComposedDoc() == aNode) {
175 return true;
178 const nsINode* node = this;
179 do {
180 if (node == aNode) {
181 return true;
184 node = node->GetParentOrShadowHostNode();
185 } while (node);
187 return false;
190 nsINode::nsSlots::nsSlots() : mWeakReference(nullptr) {}
192 nsINode::nsSlots::~nsSlots() {
193 if (mChildNodes) {
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&) {
208 if (mChildNodes) {
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)),
219 mParent(nullptr)
220 # ifndef BOOL_FLAGS_ON_WRAPPER_CACHE
222 mBoolFlags(0)
223 # endif
225 mChildCount(0),
226 mPreviousOrLastSibling(nullptr),
227 mSubtreeRoot(this),
228 mSlots(nullptr) {
230 #endif
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());
244 #endif
246 void* nsINode::GetProperty(const nsAtom* aPropertyName,
247 nsresult* aStatus) const {
248 if (!HasProperties()) { // a fast HasFlag() test
249 if (aStatus) {
250 *aStatus = NS_PROPTABLE_PROP_NOT_THERE;
252 return nullptr;
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);
265 return rv;
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) {
284 while (aNode &&
285 !aNode->IsClosestCommonInclusiveAncestorForRangeInSelection()) {
286 if (!aNode
287 ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
288 return nullptr;
290 aNode = aNode->GetParentNode();
292 return aNode;
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 {
300 public:
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)
305 : mNode(aNode),
306 mStartOffset(aStartOffset),
307 mEndOffset(aEndOffset),
308 mCache(aCache) {
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(),
315 nullptr, mCache);
316 if (cmp == 1) {
317 cmp = nsContentUtils::ComparePoints_Deprecated(
318 &mNode, mStartOffset, aRange->GetEndContainer(), aRange->EndOffset(),
319 nullptr, mCache);
320 if (cmp == -1) {
321 return 0;
323 return 1;
325 return -1;
328 private:
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();
350 if (!ranges) {
351 continue;
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).
370 size_t low = 0;
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);
378 if (result == 0) {
379 if (!range->Collapsed()) {
380 return true;
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) {
391 result = 1;
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) {
398 result = -1;
399 } else {
400 break;
404 if (result < 0) {
405 high = middle;
406 } else {
407 low = middle + 1;
412 return false;
415 Element* nsINode::GetAnonymousRootElementOfTextEditor(
416 TextEditor** aTextEditor) {
417 if (aTextEditor) {
418 *aTextEditor = nullptr;
420 RefPtr<TextControlElement> textControlElement;
421 if (IsInNativeAnonymousSubtree()) {
422 textControlElement = TextControlElement::FromNodeOrNull(
423 GetClosestNativeAnonymousSubtreeRootParentOrHost());
424 } else {
425 textControlElement = TextControlElement::FromNode(this);
427 if (!textControlElement) {
428 return nullptr;
430 RefPtr<TextEditor> textEditor = textControlElement->GetTextEditor();
431 if (!textEditor) {
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
435 // `TextEditor`.
436 return nullptr;
439 Element* rootElement = textEditor->GetRoot();
440 if (aTextEditor) {
441 textEditor.forget(aTextEditor);
443 return rootElement;
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()) {
458 return doc;
461 nsINode* node = this;
462 while (node) {
463 node = node->SubtreeRoot();
464 ShadowRoot* shadow = ShadowRoot::FromNode(node);
465 if (!shadow) {
466 break;
468 node = shadow->GetHost();
471 return node;
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())) {
491 node = iter;
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).
504 nsINode* node;
505 if (IsInUncomposedDoc()) {
506 node = OwnerDocAsNode();
507 } else if (IsContent()) {
508 ShadowRoot* containingShadow = AsContent()->GetContainingShadow();
509 node = containingShadow ? containingShadow : mSubtreeRoot;
510 if (!node) {
511 NS_WARNING("Using SubtreeRoot() on unlinked element?");
512 node = RootOfNode(this);
514 } else {
515 node = mSubtreeRoot;
517 MOZ_ASSERT(node, "Should always have a node here!");
518 #ifdef DEBUG
520 const nsINode* slowNode = RootOfNode(this);
521 MOZ_ASSERT(slowNode == node, "These should always be in sync!");
523 #endif
524 return node;
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()) {
556 return nullptr;
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
562 // selection root.
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();
574 if (presContext) {
575 HTMLEditor* htmlEditor = nsContentUtils::GetHTMLEditor(presContext);
576 if (htmlEditor) {
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)
583 ? 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();
594 if (!content) {
595 content = fs->GetAncestorLimiter();
596 if (!content) {
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();
616 return content;
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) {
638 return;
641 auto childNodes =
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()) {
653 return OwnerDoc();
656 if (IsInShadowTree()) {
657 return AsContent()->GetContainingShadow();
660 return nullptr;
663 DocumentOrShadowRoot* nsINode::GetUncomposedDocOrConnectedShadowRoot() const {
664 if (IsInUncomposedDoc()) {
665 return OwnerDoc();
668 if (IsInComposedDoc() && IsInShadowTree()) {
669 return AsContent()->GetContainingShadow();
672 return nullptr;
675 mozilla::SafeDoublyLinkedList<nsIMutationObserver>*
676 nsINode::GetMutationObservers() {
677 return HasSlots() ? &GetExistingSlots()->mMutationObservers : nullptr;
680 void nsINode::LastRelease() {
681 nsINode::nsSlots* slots = GetExistingSlots();
682 if (slots) {
683 if (!slots->mMutationObservers.isEmpty()) {
684 for (auto iter = slots->mMutationObservers.begin();
685 iter != slots->mMutationObservers.end(); ++iter) {
686 iter->NodeWillBeDestroyed(this);
690 if (IsContent()) {
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()) {
700 data->Unlink();
704 delete slots;
705 mSlots = nullptr;
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.
710 if (IsDocument()) {
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();
715 } else {
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)) {
739 #ifdef DEBUG
740 if (nsContentUtils::IsInitialized()) {
741 EventListenerManager* manager =
742 nsContentUtils::GetExistingListenerManagerForNode(this);
743 if (!manager) {
744 NS_ERROR(
745 "Huh, our bit says we have a listener manager list, "
746 "but there's nothing in the hash!?!!");
749 #endif
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;
763 while (curr) {
764 nsString id;
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());
775 } else {
776 elemDesc.Append(curr->NodeName());
779 if (!id.IsEmpty()) {
780 elemDesc = elemDesc + u"['"_ns + id + u"']"_ns;
783 if (curr->IsElement() &&
784 curr->AsElement()->HasAttr(nsGkAtoms::contenteditable)) {
785 nsAutoString val;
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()) {
807 return nullptr;
809 return AsContent()->GetContainingShadow();
812 nsIContent* nsINode::GetContainingShadowHost() const {
813 if (ShadowRoot* shadow = GetContainingShadow()) {
814 return shadow->GetHost();
816 return nullptr;
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
831 "an Element",
832 "an Attribute",
833 "a Text",
834 "a CDATASection",
835 "an EntityReference",
836 "an Entity",
837 "a ProcessingInstruction",
838 "a Comment",
839 "a Document",
840 "a DocumentType",
841 "a DocumentFragment",
842 "a Notation",
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");
858 return nullptr;
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");
872 return nullptr;
875 RemoveChildNode(aOldChild.AsContent(), true);
876 return &aOldChild;
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) {
887 canMerge = false;
888 continue;
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);
895 } else {
896 canMerge = true;
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()) {
905 return;
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
915 // no listeners
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()) {
922 // TODO: Bug 1622253
923 nsContentUtils::MaybeFireNodeRemoved(MOZ_KnownLive(node), parentNode);
928 mozAutoDocUpdate batch(doc, true);
930 // Merge and remove all nodes
931 nsAutoString tmpStr;
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();
938 NS_ASSERTION(
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);
944 if (text->Is2b()) {
945 t->AppendTextForNormalize(text->Get2b(), text->GetLength(), true,
946 node);
947 } else {
948 tmpStr.Truncate();
949 text->AppendTo(tmpStr);
950 t->AppendTextForNormalize(tmpStr.get(), tmpStr.Length(), true, node);
955 // Remove 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");
960 if (parent) {
961 parent->RemoveChildNode(node, true);
966 nsresult nsINode::GetBaseURI(nsAString& aURI) const {
967 nsIURI* baseURI = GetBaseURI();
969 nsAutoCString spec;
970 if (baseURI) {
971 nsresult rv = baseURI->GetSpec(spec);
972 NS_ENSURE_SUCCESS(rv, rv);
975 CopyUTF8toUTF16(spec, aURI);
976 return NS_OK;
979 void nsINode::GetBaseURIFromJS(nsAString& aURI, CallerType aCallerType,
980 ErrorResult& aRv) const {
981 nsIURI* baseURI = GetBaseURI(aCallerType == CallerType::System);
982 nsAutoCString spec;
983 if (baseURI) {
984 nsresult res = baseURI->GetSpec(spec);
985 if (NS_FAILED(res)) {
986 aRv.Throw(res);
987 return;
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
1012 // null.
1013 nsAtom* localName = name->LocalName();
1015 if (localName != nsGkAtoms::xmlns) {
1016 localName->ToString(aPrefix);
1017 } else {
1018 SetDOMStringToNull(aPrefix);
1020 return;
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) {
1033 return 0;
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);
1051 if (attr1) {
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
1055 if (elem) {
1056 node1 = elem;
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.
1066 uint32_t i;
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;
1084 if (elem) {
1085 node2 = elem;
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
1096 do {
1097 parents1.AppendElement(node1);
1098 node1 = node1->GetParentNode();
1099 } while (node1);
1100 do {
1101 parents2.AppendElement(node2);
1102 node2 = node2->GetParentNode();
1103 } while (node2);
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);
1110 if (top1 != top2) {
1111 return top1 < top2
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;
1122 uint32_t len;
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);
1136 } else {
1137 child1Index = parent->ComputeIndexOf(child1);
1140 Maybe<uint32_t> child2Index;
1141 bool cachedChild2Index = false;
1142 if (this == child2 && aThisIndex) {
1143 cachedChild2Index = true;
1144 child2Index =
1145 aThisIndex->isSome() ? *aThisIndex : parent->ComputeIndexOf(child2);
1146 } else {
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;
1161 return retVal;
1163 parent = child1;
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) {
1178 if (!aOther) {
1179 return false;
1182 // Might as well do a quick check to avoid walking our kids if we're
1183 // obviously the same.
1184 if (aOther == this) {
1185 return true;
1188 nsAutoString string1, string2;
1190 nsINode* node1 = this;
1191 nsINode* node2 = aOther;
1192 do {
1193 uint16_t nodeType = node1->NodeType();
1194 if (nodeType != node2->NodeType()) {
1195 return false;
1198 mozilla::dom::NodeInfo* nodeInfo1 = node1->mNodeInfo;
1199 mozilla::dom::NodeInfo* nodeInfo2 = node2->mNodeInfo;
1200 if (!nodeInfo1->Equals(nodeInfo2) ||
1201 nodeInfo1->GetExtraName() != nodeInfo2->GetExtraName()) {
1202 return false;
1205 switch (nodeType) {
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()) {
1213 return false;
1216 // Iterate over attributes.
1217 for (uint32_t i = 0; i < attrCount; ++i) {
1218 const nsAttrName* attrName = element1->GetAttrNameAt(i);
1219 #ifdef DEBUG
1220 bool hasAttr =
1221 #endif
1222 element1->GetAttr(attrName->NamespaceID(), attrName->LocalName(),
1223 string1);
1224 NS_ASSERTION(hasAttr, "Why don't we have an attr?");
1226 if (!element2->AttrValueIs(attrName->NamespaceID(),
1227 attrName->LocalName(), string1,
1228 eCaseMatters)) {
1229 return false;
1232 break;
1234 case TEXT_NODE:
1235 case COMMENT_NODE:
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)) {
1244 return false;
1247 break;
1249 case DOCUMENT_NODE:
1250 case DOCUMENT_FRAGMENT_NODE:
1251 break;
1252 case ATTRIBUTE_NODE: {
1253 NS_ASSERTION(node1 == this && node2 == aOther,
1254 "Did we come upon an attribute node while walking a "
1255 "subtree?");
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);
1268 // Public ID
1269 docType1->GetPublicId(string1);
1270 docType2->GetPublicId(string2);
1271 if (!string1.Equals(string2)) {
1272 return false;
1275 // System ID
1276 docType1->GetSystemId(string1);
1277 docType2->GetSystemId(string2);
1278 if (!string1.Equals(string2)) {
1279 return false;
1282 break;
1284 default:
1285 MOZ_ASSERT(false, "Unknown node type");
1288 nsINode* nextNode = node1->GetFirstChild();
1289 if (nextNode) {
1290 node1 = nextNode;
1291 node2 = node2->GetFirstChild();
1292 } else {
1293 if (node2->GetFirstChild()) {
1294 // node2 has a firstChild, but node1 doesn't
1295 return false;
1298 // Find next sibling, possibly walking parent chain.
1299 while (1) {
1300 if (node1 == this) {
1301 NS_ASSERTION(node2 == aOther,
1302 "Should have reached the start node "
1303 "for both trees at the same time");
1304 return true;
1307 nextNode = node1->GetNextSibling();
1308 if (nextNode) {
1309 node1 = nextNode;
1310 node2 = node2->GetNextSibling();
1311 break;
1314 if (node2->GetNextSibling()) {
1315 // node2 has a nextSibling, but node1 doesn't
1316 return false;
1319 node1 = node1->GetParentNode();
1320 node2 = node2->GetParentNode();
1321 NS_ASSERTION(node1 && node2, "no parent while walking subtree");
1324 } while (node2);
1326 return false;
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,
1363 ErrorResult& aRv) {
1364 return mozilla::ConvertQuadFromNode(this, aQuad, aFrom, aOptions, aCallerType,
1365 aRv);
1368 already_AddRefed<DOMQuad> nsINode::ConvertRectFromNode(
1369 DOMRectReadOnly& aRect, const GeometryNode& aFrom,
1370 const ConvertCoordinateOptions& aOptions, CallerType aCallerType,
1371 ErrorResult& aRv) {
1372 return mozilla::ConvertRectFromNode(this, aRect, aFrom, aOptions, aCallerType,
1373 aRv);
1376 already_AddRefed<DOMPoint> nsINode::ConvertPointFromNode(
1377 const DOMPointInit& aPoint, const GeometryNode& aFrom,
1378 const ConvertCoordinateOptions& aOptions, CallerType aCallerType,
1379 ErrorResult& aRv) {
1380 return mozilla::ConvertPointFromNode(this, aPoint, aFrom, aOptions,
1381 aCallerType, aRv);
1384 bool nsINode::DispatchEvent(Event& aEvent, CallerType aCallerType,
1385 ErrorResult& aRv) {
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
1391 if (!document) {
1392 return true;
1395 // Obtain a presentation shell
1396 RefPtr<nsPresContext> context = document->GetPresContext();
1398 nsEventStatus status = nsEventStatus_eIgnore;
1399 nsresult rv = EventDispatcher::DispatchDOMEvent(this, nullptr, &aEvent,
1400 context, &status);
1401 bool retval = !aEvent.DefaultPrevented(aCallerType);
1402 if (NS_FAILED(rv)) {
1403 aRv.Throw(rv);
1405 return retval;
1408 nsresult nsINode::PostHandleEvent(EventChainPostVisitor& /*aVisitor*/) {
1409 return NS_OK;
1412 EventListenerManager* nsINode::GetOrCreateListenerManager() {
1413 return nsContentUtils::GetListenerManagerForNode(this);
1416 EventListenerManager* nsINode::GetExistingListenerManager() const {
1417 return nsContentUtils::GetExistingListenerManagerForNode(this);
1420 nsPIDOMWindowOuter* nsINode::GetOwnerGlobalForBindingsInternal() {
1421 bool dummy;
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 {
1430 bool dummy;
1431 return OwnerDoc()->GetScriptHandlingObject(dummy);
1434 bool nsINode::UnoptimizableCCNode() const {
1435 return IsInNativeAnonymousSubtree() || IsAttr();
1438 /* static */
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())) {
1444 return false;
1447 if (nsCCUncollectableMarker::sGeneration) {
1448 // If we're black no need to traverse.
1449 if (tmp->HasKnownLiveWrapper() || tmp->InCCBlackTree()) {
1450 return false;
1453 if (!tmp->UnoptimizableCCNode()) {
1454 // If we're in a black document, return early.
1455 if ((currentDoc && currentDoc->HasKnownLiveWrapper())) {
1456 return false;
1458 // If we're not in anonymous content and we have a black parent,
1459 // return early.
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?");
1465 return false;
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();
1477 if (slots) {
1478 slots->Traverse(cb);
1481 if (tmp->HasProperties()) {
1482 nsCOMArray<nsISupports>* objects = static_cast<nsCOMArray<nsISupports>*>(
1483 tmp->GetProperty(nsGkAtoms::keepobjectsalive));
1484 if (objects) {
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));
1493 if (anode) {
1494 cb.NoteXPCOMChild(anode);
1496 #endif
1499 if (tmp->NodeType() != DOCUMENT_NODE &&
1500 tmp->HasFlag(NODE_HAS_LISTENERMANAGER)) {
1501 nsContentUtils::TraverseListenerManager(tmp, cb);
1504 return true;
1507 /* static */
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);
1536 #ifdef DEBUG
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!");
1543 #endif // DEBUG
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.
1549 AutoJSAPI jsapi;
1550 jsapi.Init();
1552 JSContext* cx = jsapi.cx();
1554 ErrorResult rv;
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();
1561 if (rv.Failed()) {
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
1566 // do that.
1567 return rv.StealNSResult();
1572 return NS_OK;
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.");
1580 return;
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())) {
1596 return;
1600 if (!aBeforeThis) {
1601 AppendChildToChildList(aKid);
1602 } else {
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);
1617 if (aRv.Failed()) {
1618 DisconnectChild(aKid);
1619 aKid->UnbindFromTree();
1620 return;
1623 // Invalidate cached array of child nodes
1624 InvalidateChildNodes();
1626 NS_ASSERTION(aKid->GetParentNode() == this,
1627 "Did we run script inappropriately?");
1629 if (aNotify) {
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);
1634 } else {
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) {
1652 return nullptr;
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);
1691 } else {
1692 *aChild = nullptr;
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) {
1705 MOZ_ASSERT(aKid);
1706 MOZ_ASSERT(!aKid->mNextSibling);
1708 RemoveFromCache(this);
1710 if (mFirstChild) {
1711 nsIContent* lastChild = GetLastChild();
1712 lastChild->mNextSibling = aKid;
1713 aKid->mPreviousOrLastSibling = lastChild;
1714 } else {
1715 mFirstChild = aKid;
1718 // Maintain link to the last child
1719 mFirstChild->mPreviousOrLastSibling = aKid;
1720 ++mChildCount;
1723 void nsINode::InsertChildToChildList(nsIContent* aKid,
1724 nsIContent* aNextSibling) {
1725 MOZ_ASSERT(aKid);
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);
1737 mFirstChild = aKid;
1738 } else {
1739 previousSibling->mNextSibling = aKid;
1742 ++mChildCount;
1745 void nsINode::DisconnectChild(nsIContent* aKid) {
1746 MOZ_ASSERT(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;
1756 } else {
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);
1764 } else {
1765 // aKid is the first child in the list
1766 mFirstChild = std::move(aKid->mNextSibling);
1769 --mChildCount;
1772 nsIContent* nsINode::GetChildAt_Deprecated(uint32_t aIndex) const {
1773 if (aIndex >= GetChildCount()) {
1774 return nullptr;
1777 nsIContent* child = mFirstChild;
1778 while (aIndex--) {
1779 child = child->GetNextSibling();
1782 return child;
1785 int32_t nsINode::ComputeIndexOf_Deprecated(
1786 const nsINode* aPossibleChild) const {
1787 Maybe<uint32_t> maybeIndex = ComputeIndexOf(aPossibleChild);
1788 if (!maybeIndex) {
1789 return -1;
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) {
1799 return Nothing();
1802 if (aPossibleChild->GetParentNode() != this) {
1803 return Nothing();
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);
1815 if (child) {
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();
1824 do {
1825 if (next) {
1826 MOZ_ASSERT(nextIndex < UINT32_MAX);
1827 ++nextIndex;
1828 if (next == aPossibleChild) {
1829 AddChildAndIndexToCache(this, aPossibleChild, nextIndex);
1830 return Some(nextIndex);
1832 next = next->GetNextSibling();
1834 if (prev) {
1835 MOZ_ASSERT(prevIndex > 0);
1836 --prevIndex;
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;
1849 while (current) {
1850 MOZ_ASSERT(current->GetParentNode() == this);
1851 if (current == aPossibleChild) {
1852 if (mChildCount >= CACHE_CHILD_LIMIT) {
1853 AddChildAndIndexToCache(this, current, index);
1855 return Some(index);
1857 current = current->GetNextSibling();
1858 MOZ_ASSERT(index < UINT32_MAX);
1859 ++index;
1862 return Nothing();
1865 Maybe<uint32_t> nsINode::ComputeIndexInParentNode() const {
1866 nsINode* parent = GetParentNode();
1867 if (MOZ_UNLIKELY(!parent)) {
1868 return Nothing();
1870 return parent->ComputeIndexOf(this);
1873 Maybe<uint32_t> nsINode::ComputeIndexInParentContent() const {
1874 nsIContent* parent = GetParent();
1875 if (MOZ_UNLIKELY(!parent)) {
1876 return Nothing();
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);
1914 if (aRv.Failed()) {
1915 return nullptr;
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;
1941 break;
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;
1958 break;
1962 return viableNextSibling;
1965 void nsINode::Before(const Sequence<OwningNodeOrString>& aNodes,
1966 ErrorResult& aRv) {
1967 nsCOMPtr<nsINode> parent = GetParentNode();
1968 if (!parent) {
1969 return;
1972 nsCOMPtr<nsINode> viablePreviousSibling =
1973 FindViablePreviousSibling(*this, aNodes);
1975 nsCOMPtr<Document> doc = OwnerDoc();
1976 nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv);
1977 if (aRv.Failed()) {
1978 return;
1981 viablePreviousSibling = viablePreviousSibling
1982 ? viablePreviousSibling->GetNextSibling()
1983 : parent->GetFirstChild();
1985 parent->InsertBefore(*node, viablePreviousSibling, aRv);
1988 void nsINode::After(const Sequence<OwningNodeOrString>& aNodes,
1989 ErrorResult& aRv) {
1990 nsCOMPtr<nsINode> parent = GetParentNode();
1991 if (!parent) {
1992 return;
1995 nsCOMPtr<nsINode> viableNextSibling = FindViableNextSibling(*this, aNodes);
1997 nsCOMPtr<Document> doc = OwnerDoc();
1998 nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv);
1999 if (aRv.Failed()) {
2000 return;
2003 parent->InsertBefore(*node, viableNextSibling, aRv);
2006 void nsINode::ReplaceWith(const Sequence<OwningNodeOrString>& aNodes,
2007 ErrorResult& aRv) {
2008 nsCOMPtr<nsINode> parent = GetParentNode();
2009 if (!parent) {
2010 return;
2013 nsCOMPtr<nsINode> viableNextSibling = FindViableNextSibling(*this, aNodes);
2015 nsCOMPtr<Document> doc = OwnerDoc();
2016 nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv);
2017 if (aRv.Failed()) {
2018 return;
2021 if (parent == GetParentNode()) {
2022 parent->ReplaceChild(*node, *this, aRv);
2023 } else {
2024 parent->InsertBefore(*node, viableNextSibling, aRv);
2028 void nsINode::Remove() {
2029 nsCOMPtr<nsINode> parent = GetParentNode();
2030 if (!parent) {
2031 return;
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();
2045 return nullptr;
2048 Element* nsINode::GetLastElementChild() const {
2049 for (nsIContent* child = GetLastChild(); child;
2050 child = child->GetPreviousSibling()) {
2051 if (child->IsElement()) {
2052 return child->AsElement();
2056 return nullptr;
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,
2068 eCaseMatters);
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);
2075 bool nameMatch;
2076 if (name->IsAtom()) {
2077 nameMatch = name->Atom() == aAttrName;
2078 } else if (aNamespaceID == kNameSpaceID_Wildcard) {
2079 nameMatch = name->NodeInfo()->Equals(aAttrName);
2080 } else {
2081 nameMatch = name->NodeInfo()->QualifiedNameEquals(aAttrName);
2084 if (nameMatch) {
2085 return attrValue->EqualsLiteral("*") ||
2086 aElement->AttrValueIs(name->NamespaceID(), name->LocalName(),
2087 *attrValue, eCaseMatters);
2091 return false;
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)) {
2114 aRv.Throw(rv);
2115 return nullptr;
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,
2126 ErrorResult& aRv) {
2127 nsCOMPtr<Document> doc = OwnerDoc();
2128 nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv);
2129 if (aRv.Failed()) {
2130 return;
2133 nsCOMPtr<nsIContent> refNode = mFirstChild;
2134 InsertBefore(*node, refNode, aRv);
2137 void nsINode::Append(const Sequence<OwningNodeOrString>& aNodes,
2138 ErrorResult& aRv) {
2139 nsCOMPtr<Document> doc = OwnerDoc();
2140 nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv);
2141 if (aRv.Failed()) {
2142 return;
2145 AppendChild(*node, aRv);
2148 // https://dom.spec.whatwg.org/#dom-parentnode-replacechildren
2149 void nsINode::ReplaceChildren(const Sequence<OwningNodeOrString>& aNodes,
2150 ErrorResult& aRv) {
2151 nsCOMPtr<Document> doc = OwnerDoc();
2152 nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv);
2153 if (aRv.Failed()) {
2154 return;
2156 MOZ_ASSERT(node);
2157 return ReplaceChildren(node, aRv);
2160 void nsINode::ReplaceChildren(nsINode* aNode, ErrorResult& aRv) {
2161 if (aNode) {
2162 EnsurePreInsertionValidity(*aNode, nullptr, aRv);
2163 if (aRv.Failed()) {
2164 return;
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();
2175 if (node) {
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);
2196 mb.RemovalDone();
2198 if (aNode) {
2199 AppendChild(*aNode, aRv);
2200 mb.NodesAdded();
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();
2225 if (aNotify) {
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,
2245 ErrorResult& aRv) {
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!");
2254 // Step 2.
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,
2267 aNewChild))) {
2268 aRv.ThrowHierarchyRequestError(
2269 "The new child is an ancestor of the parent");
2270 return;
2273 // Step 3.
2274 if (aRefChild && aRefChild->GetParentNode() != aParent) {
2275 if (aIsReplace) {
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.");
2280 } else {
2281 aRv.ThrowNotFoundError(
2282 "Child to be replaced is not a child of this node");
2284 } else {
2285 aRv.ThrowNotFoundError(
2286 "Child to insert before is not a child of this node");
2288 return;
2291 // Step 4.
2292 if (!aNewChild->IsContent()) {
2293 aRv.ThrowHierarchyRequestError(nsPrintfCString(
2294 "May not add %s as a child", NodeTypeAsString(aNewChild)));
2295 return;
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:
2303 // OK in both cases
2304 return;
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)));
2314 return;
2315 case nsINode::ELEMENT_NODE: {
2316 if (!aParent->IsDocument()) {
2317 // Always ok to have elements under other elements or document fragments
2318 return;
2321 Document* parentDocument = aParent->AsDocument();
2322 Element* rootElement = parentDocument->GetRootElement();
2323 if (rootElement) {
2324 // Already have a documentElement, so this is only OK if we're
2325 // replacing it.
2326 if (!aIsReplace || rootElement != aRefChild) {
2327 aRv.ThrowHierarchyRequestError(
2328 "Cannot have more than one Element child of a Document");
2330 return;
2333 // We don't have a documentElement yet. Our one remaining constraint is
2334 // that the documentElement must come after the doctype.
2335 if (!aRefChild) {
2336 // Appending is just fine.
2337 return;
2340 nsIContent* docTypeContent = parentDocument->GetDoctype();
2341 if (!docTypeContent) {
2342 // It's all good.
2343 return;
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);
2360 if (!ok) {
2361 aRv.ThrowHierarchyRequestError(
2362 "Cannot insert a root element before the doctype");
2364 return;
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)));
2372 return;
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");
2383 return;
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();
2389 if (!rootElement) {
2390 // It's all good
2391 return;
2394 if (!aRefChild) {
2395 // Trying to append a doctype, but have a documentElement
2396 aRv.ThrowHierarchyRequestError(
2397 "Cannot have a DocumentType node after the root element");
2398 return;
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");
2414 return;
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()) {
2423 // All good here
2424 return;
2427 bool sawElement = false;
2428 for (nsIContent* child = aNewChild->GetFirstChild(); child;
2429 child = child->GetNextSibling()) {
2430 if (child->IsElement()) {
2431 if (sawElement) {
2432 // Can't put two elements into a document
2433 aRv.ThrowHierarchyRequestError(
2434 "Cannot have more than one Element child of a Document");
2435 return;
2437 sawElement = true;
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);
2442 if (aRv.Failed()) {
2443 return;
2447 // Everything in the fragment checked out ok, so we can stick it in here
2448 return;
2450 default:
2452 * aNewChild is of invalid type.
2454 break;
2457 // XXXbz when can we reach this?
2458 aRv.ThrowHierarchyRequestError(nsPrintfCString("Cannot insert %s inside %s",
2459 NodeTypeAsString(aNewChild),
2460 NodeTypeAsString(aParent)));
2463 // Implements
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()) {
2469 return;
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)));
2482 return;
2486 void nsINode::EnsurePreInsertionValidity2(bool aReplace, nsINode& aNewChild,
2487 nsINode* aRefChild,
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
2492 // its way.
2493 aError.ThrowNotSupportedError(
2494 "Inserting anonymous content manually is not supported");
2495 return;
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,
2503 nsINode* aRefChild,
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
2513 // we plan to do.
2514 EnsurePreInsertionValidity1(aError);
2515 if (aError.Failed()) {
2516 return nullptr;
2519 EnsurePreInsertionValidity2(aReplace, *aNewChild, aRefChild, aError);
2520 if (aError.Failed()) {
2521 return nullptr;
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
2530 // might be stale
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
2541 // parent
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
2547 // fragment
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
2554 // the tree shape.
2555 EnsurePreInsertionValidity2(aReplace, *aNewChild, aRefChild, aError);
2556 if (aError.Failed()) {
2557 return nullptr;
2562 // Record the node to insert before, if any
2563 nsIContent* nodeToInsertBefore;
2564 if (aReplace) {
2565 nodeToInsertBefore = aRefChild->GetNextSibling();
2566 } else {
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();
2581 if (oldParent) {
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) {
2598 mb.RemovalDone();
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");
2612 return nullptr;
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,
2620 aError);
2621 if (aError.Failed()) {
2622 return nullptr;
2624 } else {
2625 EnsureAllowedAsChild(newContent, this, aReplace, aRefChild, aError);
2626 if (aError.Failed()) {
2627 return nullptr;
2630 // And recompute nodeToInsertBefore, just in case.
2631 if (aReplace) {
2632 nodeToInsertBefore = aRefChild->GetNextSibling();
2633 } else {
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
2642 // into the DOM.
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");
2681 return nullptr;
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");
2689 return nullptr;
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");
2701 return nullptr;
2704 // Recompute nodeToInsertBefore, just in case.
2705 if (aReplace) {
2706 nodeToInsertBefore = aRefChild->GetNextSibling();
2707 } else {
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.
2716 if (IsDocument()) {
2717 bool sawElement = false;
2718 for (uint32_t i = 0; i < count; ++i) {
2719 nsIContent* child = fragChildren->ElementAt(i);
2720 if (child->IsElement()) {
2721 if (sawElement) {
2722 // No good
2723 aError.ThrowHierarchyRequestError(
2724 "Cannot have more than one Element child of a Document");
2725 return nullptr;
2727 sawElement = true;
2729 EnsureAllowedAsChild(child, this, aReplace, aRefChild, aError);
2730 if (aError.Failed()) {
2731 return nullptr;
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()
2752 : GetLastChild();
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()) {
2767 return nullptr;
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()
2790 : GetLastChild());
2791 mutationBatch->SetNextSibling(nodeToInsertBefore);
2794 uint32_t count = fragChildren->Length();
2795 if (!count) {
2796 return result;
2799 bool appending = !IsDocument() && !nodeToInsertBefore;
2800 nsIContent* firstInsertedContent = fragChildren->ElementAt(0);
2802 // Iterate through the fragment's children, and insert them in the new
2803 // parent
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);
2815 return nullptr;
2819 if (mutationBatch && !appending) {
2820 mutationBatch->NodesAdded();
2823 // Notify and fire mutation events when appending
2824 if (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);
2836 } else {
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) {
2844 mb.RemovalDone();
2845 mb.SetPrevSibling(nodeToInsertBefore
2846 ? nodeToInsertBefore->GetPreviousSibling()
2847 : GetLastChild());
2848 mb.SetNextSibling(nodeToInsertBefore);
2850 InsertChildBefore(newContent, nodeToInsertBefore, true, aError);
2851 if (aError.Failed()) {
2852 return nullptr;
2856 return result;
2859 void nsINode::BindObject(nsISupports* aObject) {
2860 nsCOMArray<nsISupports>* objects = static_cast<nsCOMArray<nsISupports>*>(
2861 GetProperty(nsGkAtoms::keepobjectsalive));
2862 if (!objects) {
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));
2873 if (objects) {
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");
2891 return nullptr;
2894 return anode.forget();
2895 #else
2896 return nullptr;
2897 #endif
2900 void nsINode::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
2901 size_t* aNodeSize) const {
2902 EventListenerManager* elm = GetExistingListenerManager();
2903 if (elm) {
2904 *aNodeSize += elm->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf);
2907 // Measurement of the following members may be added later if DMD finds it is
2908 // worthwhile:
2909 // - mNodeInfo
2910 // - mSlots
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) {
2925 return true;
2928 if (!aOther || OwnerDoc() != aOther->OwnerDoc() ||
2929 IsInUncomposedDoc() != aOther->IsInUncomposedDoc() ||
2930 !aOther->IsContent() || !HasChildren()) {
2931 return false;
2934 if (IsDocument()) {
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()) {
2942 return false;
2945 if (IsInShadowTree() != aOther->IsInShadowTree() ||
2946 IsInNativeAnonymousSubtree() != aOther->IsInNativeAnonymousSubtree()) {
2947 return false;
2950 if (IsInNativeAnonymousSubtree()) {
2951 if (GetClosestNativeAnonymousSubtreeRoot() !=
2952 aOther->GetClosestNativeAnonymousSubtreeRoot()) {
2953 return false;
2957 if (IsInShadowTree()) {
2958 ShadowRoot* otherRoot = aOther->GetContainingShadow();
2959 if (IsShadowRoot()) {
2960 return otherRoot == this;
2962 if (otherRoot != GetContainingShadow()) {
2963 return false;
2967 return aOther->IsInclusiveDescendantOf(this);
2970 uint32_t nsINode::Length() const {
2971 switch (NodeType()) {
2972 case DOCUMENT_TYPE_NODE:
2973 return 0;
2975 case TEXT_NODE:
2976 case CDATA_SECTION_NODE:
2977 case PROCESSING_INSTRUCTION_NODE:
2978 case COMMENT_NODE:
2979 MOZ_ASSERT(IsContent());
2980 return AsContent()->TextLength();
2982 default:
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.
3001 return WrapUnique(
3002 Servo_SelectorList_Parse(&aSelectorString, doc->ChromeRulesEnabled()));
3005 if (!list) {
3006 // Invalid selector.
3007 aRv.ThrowSyntaxError("'"_ns + aSelectorString +
3008 "' is not a valid selector"_ns);
3011 return list;
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());
3020 MOZ_ASSERT(
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);
3026 if (!elements) {
3027 // Nothing to do; we're done
3028 return nullptr;
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)) {
3035 continue;
3038 if (!element->IsInclusiveDescendantOf(&aRoot)) {
3039 continue;
3042 // We have an element with the right id and it's a strict descendant
3043 // of aRoot.
3044 return element;
3047 return nullptr;
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);
3056 if (!list) {
3057 return nullptr;
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);
3071 if (!list) {
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()) {
3096 continue;
3098 nsAtom* id = kid->AsElement()->GetID();
3099 if (id && id->Equals(aId)) {
3100 return kid->AsElement();
3103 return nullptr;
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
3111 // to have had one,
3112 // (3) we are running a privileged script.
3113 // Event handling is possible only if (1). If (2) event handling is
3114 // prevented.
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);
3121 return nullptr;
3124 JS::Rooted<JSObject*> obj(aCx, WrapNode(aCx, aGivenProto));
3125 if (obj && ChromeOnlyAccess()) {
3126 MOZ_RELEASE_ASSERT(
3127 xpc::IsUnprivilegedJunkScope(JS::GetNonCCWObjectGlobal(obj)) ||
3128 xpc::IsInUAWidgetScope(obj) || xpc::AccessCheck::isChrome(obj));
3130 return obj;
3133 already_AddRefed<nsINode> nsINode::CloneNode(bool aDeep, ErrorResult& aError) {
3134 return Clone(aDeep, nullptr, aError);
3137 nsDOMAttributeMap* nsINode::GetAttributes() {
3138 if (!IsElement()) {
3139 return nullptr;
3141 return AsElement()->Attributes();
3144 Element* nsINode::GetParentElementCrossingShadowRoot() const {
3145 if (!mParent) {
3146 return nullptr;
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();
3158 return nullptr;
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() {
3169 if (!IsContent()) {
3170 return nullptr;
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.
3180 // Bail out.
3181 break;
3183 if (f->IsFlexItem()) {
3184 return f->GetParent()->GetContent()->AsElement();
3188 return nullptr;
3191 Element* nsINode::GetNearestInclusiveOpenPopover() const {
3192 for (auto* el : InclusiveFlatTreeAncestorsOfType<Element>()) {
3193 if (el->IsAutoPopover() && el->IsPopoverOpen()) {
3194 return el;
3197 return nullptr;
3200 Element* nsINode::GetNearestInclusiveTargetPopoverForInvoker() const {
3201 for (auto* el : InclusiveFlatTreeAncestorsOfType<Element>()) {
3202 if (auto* popover = el->GetEffectivePopoverTargetElement()) {
3203 if (popover->IsAutoPopover() && popover->IsPopoverOpen()) {
3204 return popover;
3208 return nullptr;
3211 nsGenericHTMLElement* nsINode::GetEffectivePopoverTargetElement() const {
3212 const auto* formControl =
3213 nsGenericHTMLFormControlElementWithState::FromNode(this);
3214 if (!formControl || formControl->IsDisabled() ||
3215 !formControl->IsButtonControl()) {
3216 return nullptr;
3218 if (auto* popover = nsGenericHTMLElement::FromNodeOrNull(
3219 formControl->GetPopoverTargetElement())) {
3220 if (popover->GetPopoverAttributeState() != PopoverAttributeState::None) {
3221 return popover;
3224 return nullptr;
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) {
3236 return el;
3239 return nullptr;
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;
3271 // aNode.
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);
3280 return nullptr;
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);
3291 return nullptr;
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;
3306 if (aClone) {
3307 nsresult rv = aNode->Clone(nodeInfo, getter_AddRefs(clone));
3308 if (NS_WARN_IF(NS_FAILED(rv))) {
3309 aError.Throw(rv);
3310 return nullptr;
3313 if (aParent) {
3314 // If we're cloning we need to insert the cloned children into the cloned
3315 // parent.
3316 aParent->AppendChildTo(static_cast<nsIContent*>(clone.get()), false,
3317 aError);
3318 if (NS_WARN_IF(aError.Failed())) {
3319 return nullptr;
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()
3333 : nullptr;
3335 Document* newDoc = nodeInfoManager->GetDocument();
3336 MOZ_ASSERT(newDoc);
3338 bool wasRegistered = false;
3339 if (elem) {
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);
3358 if (elem) {
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,
3368 elem, args);
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();
3380 if (elm) {
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();
3416 } else {
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) {
3422 auto ilc =
3423 static_cast<nsImageLoadingContent*>(imageLoadingContent.get());
3424 ilc->NotifyOwnerDocumentActivityChanged();
3429 if (oldDoc->MayHaveDOMMutationObservers()) {
3430 newDoc->SetMayHaveDOMMutationObservers();
3433 if (oldDoc->MayHaveAnimationObservers()) {
3434 newDoc->SetMayHaveAnimationObservers();
3437 if (elem) {
3438 elem->RecompileScriptEventListeners();
3441 if (aReparentScope) {
3442 AutoJSContext cx;
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());
3463 return nullptr;
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())) {
3494 return nullptr;
3499 if (aDeep && aNode->IsElement()) {
3500 if (aClone) {
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())) {
3525 return nullptr;
3530 } else {
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())) {
3536 return nullptr;
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())) {
3560 return nullptr;
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 "
3587 "is unsupported");
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,
3604 aError);
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;
3630 } else {
3631 MOZ_ASSERT(ShouldUseNACScope(this));
3632 p.mReflectionScope = ReflectionScope::NAC;
3634 } else {
3635 MOZ_ASSERT(!ShouldUseNACScope(this));
3636 MOZ_ASSERT(!ShouldUseUAWidgetScope(this));
3638 return p;
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)) {
3669 return;
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);
3687 if (node) {
3688 NS_ASSERTION(node->Slots()->mWeakReference == this,
3689 "Weak reference has wrong value");
3690 node->Slots()->mWeakReference = nullptr;
3694 NS_IMETHODIMP
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);