tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsINode.cpp (143745B)


      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/. */
      6 
      7 /*
      8 * Base class for all DOM nodes.
      9 */
     10 
     11 #include "nsINode.h"
     12 
     13 #include <algorithm>
     14 
     15 #include "AccessCheck.h"
     16 #include "GeometryUtils.h"
     17 #include "HTMLLegendElement.h"
     18 #include "WrapperFactory.h"
     19 #include "XPathGenerator.h"
     20 #include "js/ForOfIterator.h"  // JS::ForOfIterator
     21 #include "js/JSON.h"           // JS_ParseJSON
     22 #include "jsapi.h"
     23 #include "mozAutoDocUpdate.h"
     24 #include "mozilla/AsyncEventDispatcher.h"
     25 #include "mozilla/CORSMode.h"
     26 #include "mozilla/ClearOnShutdown.h"
     27 #include "mozilla/EventDispatcher.h"
     28 #include "mozilla/EventListenerManager.h"
     29 #include "mozilla/HTMLEditor.h"
     30 #include "mozilla/Likely.h"
     31 #include "mozilla/Maybe.h"
     32 #include "mozilla/MemoryReporting.h"
     33 #include "mozilla/Preferences.h"
     34 #include "mozilla/PresShell.h"
     35 #include "mozilla/ProfilerLabels.h"
     36 #include "mozilla/ServoBindings.h"
     37 #include "mozilla/StaticPrefs_layout.h"
     38 #include "mozilla/TextControlElement.h"
     39 #include "mozilla/TextControlState.h"
     40 #include "mozilla/TextEditor.h"
     41 #include "mozilla/TimeStamp.h"
     42 #include "mozilla/dom/AncestorIterator.h"
     43 #include "mozilla/dom/Attr.h"
     44 #include "mozilla/dom/BindContext.h"
     45 #include "mozilla/dom/BindingDeclarations.h"
     46 #include "mozilla/dom/CharacterData.h"
     47 #include "mozilla/dom/ChildIterator.h"
     48 #include "mozilla/dom/CustomElementRegistry.h"
     49 #include "mozilla/dom/DebuggerNotificationBinding.h"
     50 #include "mozilla/dom/Document.h"
     51 #include "mozilla/dom/DocumentInlines.h"
     52 #include "mozilla/dom/DocumentType.h"
     53 #include "mozilla/dom/Element.h"
     54 #include "mozilla/dom/ElementBinding.h"
     55 #include "mozilla/dom/Event.h"
     56 #include "mozilla/dom/Exceptions.h"
     57 #include "mozilla/dom/HTMLButtonElement.h"
     58 #include "mozilla/dom/HTMLDetailsElement.h"
     59 #include "mozilla/dom/HTMLDialogElement.h"
     60 #include "mozilla/dom/HTMLImageElement.h"
     61 #include "mozilla/dom/HTMLMediaElement.h"
     62 #include "mozilla/dom/HTMLTemplateElement.h"
     63 #include "mozilla/dom/L10nOverlays.h"
     64 #include "mozilla/dom/Link.h"
     65 #include "mozilla/dom/MutationObservers.h"
     66 #include "mozilla/dom/NodeBinding.h"
     67 #include "mozilla/dom/NodeInfo.h"
     68 #include "mozilla/dom/NodeInfoInlines.h"
     69 #include "mozilla/dom/PolicyContainer.h"
     70 #include "mozilla/dom/SVGUseElement.h"
     71 #include "mozilla/dom/ScriptSettings.h"
     72 #include "mozilla/dom/Selection.h"
     73 #include "mozilla/dom/ShadowRoot.h"
     74 #include "nsAtom.h"
     75 #include "nsAttrValueOrString.h"
     76 #include "nsCCUncollectableMarker.h"
     77 #include "nsCOMArray.h"
     78 #include "nsChildContentList.h"
     79 #include "nsContentCreatorFunctions.h"
     80 #include "nsContentList.h"
     81 #include "nsContentUtils.h"
     82 #include "nsCycleCollectionParticipant.h"
     83 #include "nsDOMAttributeMap.h"
     84 #include "nsDOMCID.h"
     85 #include "nsDOMCSSAttrDeclaration.h"
     86 #include "nsDOMMutationObserver.h"
     87 #include "nsDOMString.h"
     88 #include "nsDOMTokenList.h"
     89 #include "nsError.h"
     90 #include "nsExpirationTracker.h"
     91 #include "nsFocusManager.h"
     92 #include "nsFrameSelection.h"
     93 #include "nsGenericHTMLElement.h"
     94 #include "nsGkAtoms.h"
     95 #include "nsGlobalWindowInner.h"
     96 #include "nsIAnimationObserver.h"
     97 #include "nsIAnonymousContentCreator.h"
     98 #include "nsIContentInlines.h"
     99 #include "nsIFrameInlines.h"
    100 #include "nsIScriptGlobalObject.h"
    101 #include "nsIWidget.h"
    102 #include "nsLayoutUtils.h"
    103 #include "nsNameSpaceManager.h"
    104 #include "nsNodeInfoManager.h"
    105 #include "nsObjectLoadingContent.h"
    106 #include "nsPIDOMWindow.h"
    107 #include "nsPresContext.h"
    108 #include "nsPrintfCString.h"
    109 #include "nsRange.h"
    110 #include "nsString.h"
    111 #include "nsStyleConsts.h"
    112 #include "nsTextNode.h"
    113 #include "nsUnicharUtils.h"
    114 #include "nsWindowSizes.h"
    115 #include "nsWrapperCacheInlines.h"
    116 #include "xpcprivate.h"
    117 #include "xpcpublic.h"
    118 
    119 #ifdef ACCESSIBILITY
    120 #  include "mozilla/dom/AccessibleNode.h"
    121 #endif
    122 
    123 using namespace mozilla;
    124 using namespace mozilla::dom;
    125 
    126 static bool ShouldUseNACScope(const nsINode* aNode) {
    127  return aNode->IsInNativeAnonymousSubtree();
    128 }
    129 
    130 static bool ShouldUseUAWidgetScope(const nsINode* aNode) {
    131  return aNode->HasBeenInUAWidget();
    132 }
    133 
    134 void* nsINode::operator new(size_t aSize, nsNodeInfoManager* aManager) {
    135  if (StaticPrefs::dom_arena_allocator_enabled_AtStartup()) {
    136    MOZ_ASSERT(aManager, "nsNodeInfoManager needs to be initialized");
    137    return aManager->Allocate(aSize);
    138  }
    139  return ::operator new(aSize);
    140 }
    141 void nsINode::operator delete(void* aPtr) { free_impl(aPtr); }
    142 
    143 bool nsINode::IsInclusiveDescendantOf(const nsINode* aNode) const {
    144  MOZ_ASSERT(aNode, "The node is nullptr.");
    145 
    146  if (aNode == this) {
    147    return true;
    148  }
    149 
    150  if (!aNode->HasFlag(NODE_MAY_HAVE_ELEMENT_CHILDREN)) {
    151    return GetParentNode() == aNode;
    152  }
    153 
    154  for (nsINode* node : Ancestors(*this)) {
    155    if (node == aNode) {
    156      return true;
    157    }
    158  }
    159  return false;
    160 }
    161 
    162 bool nsINode::IsInclusiveFlatTreeDescendantOf(const nsINode* aNode) const {
    163  MOZ_ASSERT(aNode, "The node is nullptr.");
    164 
    165  for (nsINode* node : InclusiveFlatTreeAncestors(*this)) {
    166    if (node == aNode) {
    167      return true;
    168    }
    169  }
    170  return false;
    171 }
    172 
    173 bool nsINode::IsShadowIncludingDescendantOf(const nsINode* aNode) const {
    174  MOZ_ASSERT(aNode, "The node is nullptr.");
    175 
    176  const nsINode* node = this;
    177  while ((node = node->GetParentOrShadowHostNode())) {
    178    if (node == aNode) {
    179      return true;
    180    }
    181  }
    182 
    183  return false;
    184 }
    185 
    186 bool nsINode::IsShadowIncludingInclusiveDescendantOf(
    187    const nsINode* aNode) const {
    188  MOZ_ASSERT(aNode, "The node is nullptr.");
    189 
    190  if (this->GetComposedDoc() == aNode || this == aNode) {
    191    return true;
    192  }
    193 
    194  return IsShadowIncludingDescendantOf(aNode);
    195 }
    196 
    197 nsINode::nsSlots::nsSlots() : mWeakReference(nullptr) {}
    198 
    199 nsINode::nsSlots::~nsSlots() {
    200  if (mChildNodes) {
    201    mChildNodes->InvalidateCacheIfAvailable();
    202  }
    203 
    204  if (mWeakReference) {
    205    mWeakReference->NoticeNodeDestruction();
    206  }
    207 }
    208 
    209 void nsINode::nsSlots::Traverse(nsCycleCollectionTraversalCallback& cb) {
    210  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mChildNodes");
    211  cb.NoteXPCOMChild(mChildNodes);
    212  for (auto& object : mBoundObjects) {
    213    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mBoundObjects[i]");
    214    cb.NoteXPCOMChild(object.mObject);
    215  }
    216 }
    217 
    218 static void ClearBoundObjects(nsINode::nsSlots& aSlots, nsINode& aNode) {
    219  auto objects = std::move(aSlots.mBoundObjects);
    220  for (auto& object : objects) {
    221    if (object.mDtor) {
    222      object.mDtor(object.mObject, &aNode);
    223    }
    224  }
    225  MOZ_ASSERT(aSlots.mBoundObjects.IsEmpty());
    226 }
    227 
    228 void nsINode::nsSlots::Unlink(nsINode& aNode) {
    229  if (mChildNodes) {
    230    mChildNodes->InvalidateCacheIfAvailable();
    231    ImplCycleCollectionUnlink(mChildNodes);
    232  }
    233  ClearBoundObjects(*this, aNode);
    234 }
    235 
    236 //----------------------------------------------------------------------
    237 
    238 #ifdef MOZILLA_INTERNAL_API
    239 nsINode::nsINode(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
    240    : mNodeInfo(std::move(aNodeInfo)),
    241      mParent(nullptr)
    242 #  ifndef BOOL_FLAGS_ON_WRAPPER_CACHE
    243      ,
    244      mBoolFlags(0)
    245 #  endif
    246      ,
    247      mChildCount(0),
    248      mPreviousOrLastSibling(nullptr),
    249      mSubtreeRoot(this),
    250      mSlots(nullptr) {
    251  SetIsOnMainThread();
    252 }
    253 #endif
    254 
    255 nsINode::~nsINode() {
    256  MOZ_ASSERT(!HasSlots(), "LastRelease was not called?");
    257  MOZ_ASSERT(mSubtreeRoot == this, "Didn't restore state properly?");
    258 }
    259 
    260 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    261 void nsINode::AssertInvariantsOnNodeInfoChange() {
    262  MOZ_DIAGNOSTIC_ASSERT(!IsInComposedDoc());
    263  if (nsCOMPtr<Link> link = do_QueryInterface(this)) {
    264    MOZ_DIAGNOSTIC_ASSERT(!link->HasPendingLinkUpdate());
    265  }
    266 }
    267 #endif
    268 
    269 #ifdef DEBUG
    270 void nsINode::AssertIsRootElementSlow(bool aIsRoot) const {
    271  auto* root = OwnerDoc()->GetRootElement();
    272  const bool isRootSlow = this == root;
    273  // If we're mid unbind of the root element, IsRootElement() might return true
    274  // but the document might not be able to reach the root element anymore.
    275  MOZ_ASSERT(aIsRoot == isRootSlow || !root);
    276 }
    277 #endif
    278 
    279 void* nsINode::GetProperty(const nsAtom* aPropertyName,
    280                           nsresult* aStatus) const {
    281  if (!HasProperties()) {  // a fast HasFlag() test
    282    if (aStatus) {
    283      *aStatus = NS_PROPTABLE_PROP_NOT_THERE;
    284    }
    285    return nullptr;
    286  }
    287  return OwnerDoc()->PropertyTable().GetProperty(this, aPropertyName, aStatus);
    288 }
    289 
    290 nsresult nsINode::SetProperty(nsAtom* aPropertyName, void* aValue,
    291                              NSPropertyDtorFunc aDtor, bool aTransfer) {
    292  nsresult rv = OwnerDoc()->PropertyTable().SetProperty(
    293      this, aPropertyName, aValue, aDtor, nullptr, aTransfer);
    294  if (NS_SUCCEEDED(rv)) {
    295    SetFlags(NODE_HAS_PROPERTIES);
    296  }
    297 
    298  return rv;
    299 }
    300 
    301 void nsINode::RemoveProperty(const nsAtom* aPropertyName) {
    302  OwnerDoc()->PropertyTable().RemoveProperty(this, aPropertyName);
    303 }
    304 
    305 void* nsINode::TakeProperty(const nsAtom* aPropertyName, nsresult* aStatus) {
    306  return OwnerDoc()->PropertyTable().TakeProperty(this, aPropertyName, aStatus);
    307 }
    308 
    309 nsIPolicyContainer* nsINode::GetPolicyContainer() const {
    310  return OwnerDoc()->GetPolicyContainer();
    311 }
    312 
    313 nsINode::nsSlots* nsINode::CreateSlots() { return new nsSlots(); }
    314 
    315 static const nsINode* GetClosestCommonInclusiveAncestorForRangeInSelection(
    316    const nsINode* aNode) {
    317  while (aNode &&
    318         !aNode->IsClosestCommonInclusiveAncestorForRangeInSelection()) {
    319    const bool isNodeInFlattenedShadowTree =
    320        StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() &&
    321        (aNode->IsInShadowTree() ||
    322         (aNode->IsContent() && aNode->AsContent()->GetAssignedSlot()));
    323 
    324    if (!aNode
    325             ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection() &&
    326        !isNodeInFlattenedShadowTree) {
    327      return nullptr;
    328    }
    329 
    330    if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
    331      if (aNode->IsContent() && aNode->AsContent()->GetAssignedSlot()) {
    332        aNode = aNode->AsContent()->GetAssignedSlot();
    333      } else {
    334        aNode = aNode->GetParentOrShadowHostNode();
    335      }
    336      continue;
    337    }
    338    aNode = aNode->GetParentNode();
    339  }
    340  return aNode;
    341 }
    342 
    343 /**
    344 * A Comparator suitable for mozilla::BinarySearchIf for searching a collection
    345 * of nsRange* for an overlap of (mNode, mStartOffset) .. (mNode, mEndOffset).
    346 */
    347 class IsItemInRangeComparator {
    348 public:
    349  // @param aStartOffset has to be less or equal to aEndOffset.
    350  IsItemInRangeComparator(const nsINode& aNode, const uint32_t aStartOffset,
    351                          const uint32_t aEndOffset,
    352                          nsContentUtils::NodeIndexCache* aCache)
    353      : mNode(aNode),
    354        mStartOffset(aStartOffset),
    355        mEndOffset(aEndOffset),
    356        mCache(aCache) {
    357    MOZ_ASSERT(aStartOffset <= aEndOffset);
    358  }
    359 
    360  int operator()(const AbstractRange* const aRange) const {
    361    auto ComparePoints = [](const nsINode* aNode1, const uint32_t aOffset1,
    362                            const nsINode* aNode2, const uint32_t aOffset2,
    363                            nsContentUtils::NodeIndexCache* aCache) {
    364      if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
    365        return nsContentUtils::ComparePointsWithIndices<TreeKind::Flat>(
    366            aNode1, aOffset1, aNode2, aOffset2, aCache);
    367      }
    368      return nsContentUtils::ComparePointsWithIndices<
    369          TreeKind::ShadowIncludingDOM>(aNode1, aOffset1, aNode2, aOffset2,
    370                                        aCache);
    371    };
    372 
    373    Maybe<int32_t> cmp = ComparePoints(
    374        &mNode, mEndOffset, aRange->GetMayCrossShadowBoundaryStartContainer(),
    375        aRange->MayCrossShadowBoundaryStartOffset(), mCache);
    376    // nsContentUtils::ComparePoints would return Nothing when nodes
    377    // are disconnected, ComparePoints_Deprecated used to return 1
    378    // for that case. Hence valueOr(1) to keep the legacy result.
    379    if (cmp.valueOr(1) == 1) {
    380      cmp = ComparePoints(&mNode, mStartOffset,
    381                          aRange->GetMayCrossShadowBoundaryEndContainer(),
    382                          aRange->MayCrossShadowBoundaryEndOffset(), mCache);
    383      // Same reason as above.
    384      if (cmp.valueOr(1) == -1) {
    385        return 0;
    386      }
    387      return 1;
    388    }
    389    return -1;
    390  }
    391 
    392 private:
    393  const nsINode& mNode;
    394  const uint32_t mStartOffset;
    395  const uint32_t mEndOffset;
    396  nsContentUtils::NodeIndexCache* mCache;
    397 };
    398 
    399 bool nsINode::IsSelected(const uint32_t aStartOffset, const uint32_t aEndOffset,
    400                         SelectionNodeCache* aCache) const {
    401  MOZ_ASSERT(aStartOffset <= aEndOffset);
    402  const nsINode* n = GetClosestCommonInclusiveAncestorForRangeInSelection(this);
    403  NS_ASSERTION(n || !IsMaybeSelected(),
    404               "A node without a common inclusive ancestor for a range in "
    405               "Selection is for sure not selected.");
    406 
    407  // Collect the selection objects for potential ranges.
    408  AutoTArray<Selection*, 1> ancestorSelections;
    409  for (; n; n = GetClosestCommonInclusiveAncestorForRangeInSelection(
    410                n->GetParentNode())) {
    411    const LinkedList<AbstractRange>* ranges =
    412        n->GetExistingClosestCommonInclusiveAncestorRanges();
    413    if (!ranges) {
    414      continue;
    415    }
    416    for (const AbstractRange* range : *ranges) {
    417      MOZ_ASSERT(range->IsInAnySelection(),
    418                 "Why is this range registered with a node?");
    419      // Looks like that IsInSelection() assert fails sometimes...
    420      if (range->IsInAnySelection()) {
    421        for (const WeakPtr<Selection>& selection : range->GetSelections()) {
    422          if (selection && !ancestorSelections.Contains(selection)) {
    423            ancestorSelections.AppendElement(selection);
    424          }
    425        }
    426      }
    427    }
    428  }
    429  if (aCache && aCache->MaybeCollectNodesAndCheckIfFullySelectedInAnyOf(
    430                    this, ancestorSelections)) {
    431    return true;
    432  }
    433 
    434  nsContentUtils::NodeIndexCache cache;
    435  IsItemInRangeComparator comparator{*this, aStartOffset, aEndOffset, &cache};
    436  for (Selection* selection : ancestorSelections) {
    437    // Binary search the sorted ranges in this selection.
    438    // (Selection::GetRangeAt returns its ranges ordered).
    439    size_t low = 0;
    440    size_t high = selection->RangeCount();
    441 
    442    while (high != low) {
    443      size_t middle = low + (high - low) / 2;
    444 
    445      const AbstractRange* const range = selection->GetAbstractRangeAt(middle);
    446      int result = comparator(range);
    447      if (result == 0) {
    448        if (!range->Collapsed()) {
    449          return true;
    450        }
    451 
    452        if (range->MayCrossShadowBoundary()) {
    453          MOZ_ASSERT(range->IsDynamicRange(),
    454                     "range->MayCrossShadowBoundary() can only return true for "
    455                     "dynamic range");
    456          StaticRange* crossBoundaryRange =
    457              range->AsDynamicRange()->GetCrossShadowBoundaryRange();
    458          MOZ_ASSERT(crossBoundaryRange);
    459          if (!crossBoundaryRange->Collapsed()) {
    460            return true;
    461          }
    462        }
    463 
    464        auto ComparePoints = [](const ConstRawRangeBoundary& aBoundary1,
    465                                const RangeBoundary& aBoundary2,
    466                                nsContentUtils::NodeIndexCache* aCache) {
    467          if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
    468            return nsContentUtils::ComparePoints<TreeKind::Flat>(
    469                aBoundary1, aBoundary2, aCache);
    470          }
    471          return nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>(
    472              aBoundary1, aBoundary2, aCache);
    473        };
    474 
    475        const AbstractRange* middlePlus1;
    476        const AbstractRange* middleMinus1;
    477        // if node end > start of middle+1, result = 1
    478        if (middle + 1 < high &&
    479            (middlePlus1 = selection->GetAbstractRangeAt(middle + 1)) &&
    480            ComparePoints(
    481                ConstRawRangeBoundary(this, aEndOffset,
    482                                      RangeBoundaryIsMutationObserved::No),
    483                middlePlus1->StartRef(), &cache)
    484                    .valueOr(1) > 0) {
    485          result = 1;
    486          // if node start < end of middle - 1, result = -1
    487        } else if (middle >= 1 &&
    488                   (middleMinus1 = selection->GetAbstractRangeAt(middle - 1)) &&
    489                   ComparePoints(ConstRawRangeBoundary(
    490                                     this, aStartOffset,
    491                                     RangeBoundaryIsMutationObserved::No),
    492                                 middleMinus1->EndRef(), &cache)
    493                           .valueOr(1) < 0) {
    494          result = -1;
    495        } else {
    496          break;
    497        }
    498      }
    499 
    500      if (result < 0) {
    501        high = middle;
    502      } else {
    503        low = middle + 1;
    504      }
    505    }
    506  }
    507 
    508  return false;
    509 }
    510 
    511 Element* nsINode::GetAnonymousRootElementOfTextEditor(
    512    TextEditor** aTextEditor) {
    513  if (aTextEditor) {
    514    *aTextEditor = nullptr;
    515  }
    516  RefPtr<TextControlElement> textControlElement;
    517  if (IsInNativeAnonymousSubtree()) {
    518    textControlElement = TextControlElement::FromNodeOrNull(
    519        GetClosestNativeAnonymousSubtreeRootParentOrHost());
    520  } else {
    521    textControlElement = TextControlElement::FromNode(this);
    522  }
    523  if (!textControlElement) {
    524    return nullptr;
    525  }
    526  RefPtr<TextEditor> textEditor = textControlElement->GetTextEditor();
    527  if (!textEditor) {
    528    // The found `TextControlElement` may be an input element which is not a
    529    // text control element.  In this case, such element must not be in a
    530    // native anonymous tree of a `TextEditor` so this node is not in any
    531    // `TextEditor`.
    532    return nullptr;
    533  }
    534 
    535  Element* rootElement = textEditor->GetRoot();
    536  if (aTextEditor) {
    537    textEditor.forget(aTextEditor);
    538  }
    539  return rootElement;
    540 }
    541 
    542 void nsINode::QueueDevtoolsAnonymousEvent(bool aIsRemove) {
    543  MOZ_ASSERT(IsRootOfNativeAnonymousSubtree());
    544  MOZ_ASSERT(OwnerDoc()->DevToolsAnonymousAndShadowEventsEnabled());
    545  AsyncEventDispatcher* dispatcher = new AsyncEventDispatcher(
    546      this, aIsRemove ? u"anonymousrootremoved"_ns : u"anonymousrootcreated"_ns,
    547      CanBubble::eYes, ChromeOnlyDispatch::eYes, Composed::eYes);
    548  dispatcher->PostDOMEvent();
    549 }
    550 
    551 nsINode* nsINode::GetRootNode(const GetRootNodeOptions& aOptions) {
    552  if (aOptions.mComposed) {
    553    if (Document* doc = GetComposedDoc()) {
    554      return doc;
    555    }
    556 
    557    nsINode* node = this;
    558    while (node) {
    559      node = node->SubtreeRoot();
    560      ShadowRoot* shadow = ShadowRoot::FromNode(node);
    561      if (!shadow) {
    562        break;
    563      }
    564      node = shadow->GetHost();
    565    }
    566 
    567    return node;
    568  }
    569 
    570  return SubtreeRoot();
    571 }
    572 
    573 nsIContent* nsINode::GetFirstChildOfTemplateOrNode() {
    574  if (IsTemplateElement()) {
    575    DocumentFragment* frag = static_cast<HTMLTemplateElement*>(this)->Content();
    576    return frag->GetFirstChild();
    577  }
    578 
    579  return GetFirstChild();
    580 }
    581 
    582 nsINode* nsINode::SubtreeRoot() const {
    583  auto RootOfNode = [](const nsINode* aStart) -> nsINode* {
    584    const nsINode* node = aStart;
    585    const nsINode* iter = node;
    586    while ((iter = iter->GetParentNode())) {
    587      node = iter;
    588    }
    589    return const_cast<nsINode*>(node);
    590  };
    591 
    592  // There are four cases of interest here.  nsINodes that are really:
    593  // 1. Document nodes - Are always in the document.
    594  // 2.a nsIContent nodes not in a shadow tree - Are either in the document,
    595  //     or mSubtreeRoot is updated in BindToTree/UnbindFromTree.
    596  // 2.b nsIContent nodes in a shadow tree - Are never in the document,
    597  //     ignore mSubtreeRoot and return the containing shadow root.
    598  // 4. Attr nodes - Are never in the document, and mSubtreeRoot
    599  //    is always 'this' (as set in nsINode's ctor).
    600  nsINode* node;
    601  if (IsInUncomposedDoc()) {
    602    node = OwnerDocAsNode();
    603  } else if (IsContent()) {
    604    ShadowRoot* containingShadow = AsContent()->GetContainingShadow();
    605    node = containingShadow ? containingShadow : mSubtreeRoot;
    606    if (!node) {
    607      NS_WARNING("Using SubtreeRoot() on unlinked element?");
    608      node = RootOfNode(this);
    609    }
    610  } else {
    611    node = mSubtreeRoot;
    612  }
    613  MOZ_ASSERT(node, "Should always have a node here!");
    614 #ifdef DEBUG
    615  {
    616    const nsINode* slowNode = RootOfNode(this);
    617    MOZ_ASSERT(slowNode == node, "These should always be in sync!");
    618  }
    619 #endif
    620  return node;
    621 }
    622 
    623 static nsIContent* GetRootForContentSubtree(nsIContent* aContent) {
    624  NS_ENSURE_TRUE(aContent, nullptr);
    625 
    626  // Special case for ShadowRoot because the ShadowRoot itself is
    627  // the root. This is necessary to prevent selection from crossing
    628  // the ShadowRoot boundary.
    629  //
    630  // FIXME(emilio): The NAC check should probably be done before this? We can
    631  // have NAC inside shadow DOM.
    632  if (ShadowRoot* containingShadow = aContent->GetContainingShadow()) {
    633    return containingShadow;
    634  }
    635  if (nsIContent* nativeAnonRoot =
    636          aContent->GetClosestNativeAnonymousSubtreeRoot()) {
    637    return nativeAnonRoot;
    638  }
    639  if (Document* doc = aContent->GetUncomposedDoc()) {
    640    return doc->GetRootElement();
    641  }
    642  return nsIContent::FromNode(aContent->SubtreeRoot());
    643 }
    644 
    645 nsIContent* nsINode::GetSelectionRootContent(
    646    PresShell* aPresShell,
    647    IgnoreOwnIndependentSelection aIgnoreOwnIndependentSelection,
    648    AllowCrossShadowBoundary aAllowCrossShadowBoundary) {
    649  NS_ENSURE_TRUE(aPresShell, nullptr);
    650 
    651  const bool isContent = IsContent();
    652 
    653  if (!isContent && !IsDocument()) {
    654    return nullptr;
    655  }
    656 
    657  if (isContent) {
    658    if (GetComposedDoc() != aPresShell->GetDocument()) {
    659      return nullptr;
    660    }
    661 
    662    const bool computeTextEditorRoot =
    663        IsInNativeAnonymousSubtree() ||
    664        (aIgnoreOwnIndependentSelection == IgnoreOwnIndependentSelection::No &&
    665         AsContent()->HasIndependentSelection());
    666    if (computeTextEditorRoot) {
    667      // This node should be an inclusive descendant of input/textarea editor.
    668      // In that case, the anonymous <div> for TextEditor should be always the
    669      // selection root.
    670      if (Element* anonymousDivElement =
    671              GetAnonymousRootElementOfTextEditor()) {
    672        return anonymousDivElement;
    673      }
    674    }
    675  }
    676 
    677  if (nsPresContext* presContext = aPresShell->GetPresContext()) {
    678    if (nsContentUtils::GetHTMLEditor(presContext)) {
    679      // When there is an HTMLEditor, selection root should be one of focused
    680      // editing host, <body> or root of the (sub)tree which this node belong.
    681 
    682      // If this node is in design mode or this node is not editable, selection
    683      // root should be the <body> if this node is not in any subtrees and there
    684      // is a <body> or the root of the shadow DOM if this node is in a shadow
    685      // or the document element.
    686      // XXX If this node is not connected, it seems that this should return
    687      // nullptr because this node is not selectable.
    688      if (!IsInComposedDoc() || IsInDesignMode() ||
    689          !HasFlag(NODE_IS_EDITABLE)) {
    690        Element* const bodyOrDocumentElement = [&]() -> Element* {
    691          if (Element* const bodyElement = OwnerDoc()->GetBodyElement()) {
    692            return bodyElement;
    693          }
    694          return OwnerDoc()->GetDocumentElement();
    695        }();
    696        NS_ENSURE_TRUE(bodyOrDocumentElement, nullptr);
    697        return nsContentUtils::IsInSameAnonymousTree(this,
    698                                                     bodyOrDocumentElement)
    699                   ? bodyOrDocumentElement
    700                   : GetRootForContentSubtree(AsContent());
    701      }
    702      // If this node is editable but not in the design mode, this is always an
    703      // editable node in an editing host of contenteditable.  In this case,
    704      // let's use the editing host element as selection root.
    705      MOZ_ASSERT(IsEditable());
    706      MOZ_ASSERT(!IsInDesignMode());
    707      MOZ_ASSERT(IsContent());
    708      return AsContent()->GetEditingHost();
    709    }
    710  }
    711 
    712  if (!isContent) {
    713    return nullptr;
    714  }
    715 
    716  RefPtr<nsFrameSelection> fs = aPresShell->FrameSelection();
    717  nsCOMPtr<nsIContent> content = fs->GetIndependentSelectionRootElement();
    718  if (!content) {
    719    content = fs->GetAncestorLimiter();
    720    if (!content) {
    721      Document* doc = aPresShell->GetDocument();
    722      NS_ENSURE_TRUE(doc, nullptr);
    723      content = doc->GetRootElement();
    724      if (!content) {
    725        return nullptr;
    726      }
    727    }
    728  }
    729 
    730  // This node might be in another subtree, if so, we should find this subtree's
    731  // root.  Otherwise, we can return the content simply.
    732  NS_ENSURE_TRUE(content, nullptr);
    733  if (!nsContentUtils::IsInSameAnonymousTree(this, content)) {
    734    content = GetRootForContentSubtree(AsContent());
    735    // Fixup for ShadowRoot because the ShadowRoot itself does not have a frame.
    736    // Use the host as the root.
    737    if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(content)) {
    738      content = shadowRoot->GetHost();
    739      if (content && bool(aAllowCrossShadowBoundary)) {
    740        content = content->GetSelectionRootContent(
    741            aPresShell, aIgnoreOwnIndependentSelection,
    742            aAllowCrossShadowBoundary);
    743      }
    744    }
    745  }
    746 
    747  return content;
    748 }
    749 
    750 nsFrameSelection* nsINode::GetFrameSelection() const {
    751  if (!IsInComposedDoc()) {
    752    return nullptr;
    753  }
    754  if (IsInNativeAnonymousSubtree()) {
    755    auto* const textControlElement = TextControlElement::FromNodeOrNull(
    756        GetClosestNativeAnonymousSubtreeRootParentOrHost());
    757    if (textControlElement &&
    758        textControlElement->IsSingleLineTextControlOrTextArea()) {
    759      nsFrameSelection* const independentFrameSelection =
    760          textControlElement->GetIndependentFrameSelection();
    761      if (!independentFrameSelection) {
    762        return nullptr;  // not yet initialized or being destroyed?
    763      }
    764      const Element* const anonymousDiv =
    765          independentFrameSelection->GetIndependentSelectionRootElement();
    766      if (!anonymousDiv || !IsInclusiveDescendantOf(anonymousDiv)) {
    767        return nullptr;  // not in the editor root, shouldn't be selectable
    768      }
    769      return independentFrameSelection;
    770    }
    771    // Otherwise, even if we're in a native anonymous subtree, our selection
    772    // should be managed by the document selection.
    773  }
    774  PresShell* const presShell = OwnerDoc()->GetPresShell();
    775  if (!presShell) {
    776    return nullptr;
    777  }
    778  // FIXME: PresShell::FrameSelection() returns
    779  // already_AddRefed<nsFrameSelection> for making the users work safer.
    780  // However, in these days, it should be managed with MOZ_CAN_RUN_SCRIPT.
    781  // Therefore, for now, we should use ConstFrameSelection() and cost_cast
    782  // here to avoid to AddRef/Release in unnecessary cases.
    783  return const_cast<nsFrameSelection*>(presShell->ConstFrameSelection());
    784 }
    785 
    786 nsINodeList* nsINode::ChildNodes() {
    787  nsSlots* slots = Slots();
    788  if (!slots->mChildNodes) {
    789    slots->mChildNodes = IsAttr() ? new nsAttrChildContentList(this)
    790                                  : new nsParentNodeChildContentList(this);
    791  }
    792 
    793  return slots->mChildNodes;
    794 }
    795 
    796 nsIContent* nsINode::GetLastChild() const {
    797  return mFirstChild ? mFirstChild->mPreviousOrLastSibling : nullptr;
    798 }
    799 
    800 void nsINode::InvalidateChildNodes() {
    801  MOZ_ASSERT(!IsAttr());
    802 
    803  nsSlots* slots = GetExistingSlots();
    804  if (!slots || !slots->mChildNodes) {
    805    return;
    806  }
    807 
    808  auto childNodes =
    809      static_cast<nsParentNodeChildContentList*>(slots->mChildNodes.get());
    810  childNodes->InvalidateCache();
    811 }
    812 
    813 void nsINode::GetTextContentInternal(nsAString& aTextContent,
    814                                     OOMReporter& aError) {
    815  SetDOMStringToNull(aTextContent);
    816 }
    817 
    818 DocumentOrShadowRoot* nsINode::GetContainingDocumentOrShadowRoot() const {
    819  if (IsInUncomposedDoc()) {
    820    return OwnerDoc();
    821  }
    822 
    823  if (IsInShadowTree()) {
    824    return AsContent()->GetContainingShadow();
    825  }
    826 
    827  return nullptr;
    828 }
    829 
    830 DocumentOrShadowRoot* nsINode::GetUncomposedDocOrConnectedShadowRoot() const {
    831  if (IsInUncomposedDoc()) {
    832    return OwnerDoc();
    833  }
    834 
    835  if (IsInComposedDoc() && IsInShadowTree()) {
    836    return AsContent()->GetContainingShadow();
    837  }
    838 
    839  return nullptr;
    840 }
    841 
    842 SafeDoublyLinkedList<nsIMutationObserver>* nsINode::GetMutationObservers() {
    843  if (auto* slots = GetExistingSlots()) {
    844    if (!slots->mMutationObservers.isEmpty()) {
    845      return &slots->mMutationObservers;
    846    }
    847  }
    848  return nullptr;
    849 }
    850 
    851 void nsINode::LastRelease() {
    852  if (nsSlots* slots = GetExistingSlots()) {
    853    if (!slots->mMutationObservers.isEmpty()) {
    854      for (auto iter = slots->mMutationObservers.begin();
    855           iter != slots->mMutationObservers.end(); ++iter) {
    856        iter->NodeWillBeDestroyed(this);
    857      }
    858    }
    859    ClearBoundObjects(*slots, *this);
    860    if (IsContent()) {
    861      nsIContent* content = AsContent();
    862      if (HTMLSlotElement* slot = content->GetManualSlotAssignment()) {
    863        content->SetManualSlotAssignment(nullptr);
    864        slot->RemoveManuallyAssignedNode(*content);
    865      }
    866    }
    867 
    868    if (Element* element = Element::FromNode(this)) {
    869      if (CustomElementData* data = element->GetCustomElementData()) {
    870        data->Unlink();
    871      }
    872    }
    873 
    874    delete slots;
    875    mSlots = nullptr;
    876  }
    877 
    878  // Kill properties first since that may run external code, so we want to
    879  // be in as complete state as possible at that time.
    880  if (IsDocument()) {
    881    // Delete all properties before tearing down the document. Some of the
    882    // properties are bound to nsINode objects and the destructor functions of
    883    // the properties may want to use the owner document of the nsINode.
    884    AsDocument()->RemoveAllProperties();
    885    AsDocument()->DropStyleSet();
    886  } else {
    887    if (HasProperties()) {
    888      // Strong reference to the document so that deleting properties can't
    889      // delete the document.
    890      nsCOMPtr<Document> document = OwnerDoc();
    891      document->RemoveAllPropertiesFor(this);
    892    }
    893 
    894    if (HasFlag(ADDED_TO_FORM)) {
    895      if (auto* formControl = nsGenericHTMLFormControlElement::FromNode(this)) {
    896        // Tell the form (if any) this node is going away.  Don't
    897        // notify, since we're being destroyed in any case.
    898        formControl->ClearForm(true, true);
    899      } else if (auto* imageElem = HTMLImageElement::FromNode(this)) {
    900        imageElem->ClearForm(true);
    901      }
    902    }
    903    if (HasFlag(NODE_HAS_LISTENERMANAGER)) {
    904 #ifdef DEBUG
    905      if (nsContentUtils::IsInitialized()) {
    906        EventListenerManager* manager =
    907            nsContentUtils::GetExistingListenerManagerForNode(this);
    908        if (!manager) {
    909          NS_ERROR(
    910              "Huh, our bit says we have a listener manager list, "
    911              "but there's nothing in the hash!?!!");
    912        }
    913      }
    914 #endif
    915 
    916      nsContentUtils::RemoveListenerManager(this);
    917      UnsetFlags(NODE_HAS_LISTENERMANAGER);
    918    }
    919 
    920    if (Element* element = Element::FromNode(this)) {
    921      element->ClearAttributes();
    922    }
    923  }
    924 
    925  UnsetFlags(NODE_HAS_PROPERTIES);
    926  ReleaseWrapper(this);
    927 
    928  FragmentOrElement::RemoveBlackMarkedNode(this);
    929 }
    930 
    931 std::ostream& operator<<(std::ostream& aStream, const nsINode& aNode) {
    932  nsAutoString elemDesc;
    933  const nsINode* curr = &aNode;
    934  while (curr) {
    935    nsString id, cls;
    936    if (curr->IsElement()) {
    937      curr->AsElement()->GetId(id);
    938      if (const nsAttrValue* attrValue = curr->AsElement()->GetClasses()) {
    939        attrValue->ToString(cls);
    940      }
    941    }
    942 
    943    if (!elemDesc.IsEmpty()) {
    944      elemDesc = elemDesc + u"."_ns;
    945    }
    946 
    947    if (!curr->LocalName().IsEmpty()) {
    948      elemDesc.Append(curr->LocalName());
    949    } else {
    950      elemDesc.Append(curr->NodeName());
    951    }
    952 
    953    if (!id.IsEmpty()) {
    954      elemDesc = elemDesc + u"['"_ns + id + u"']"_ns;
    955    } else if (!cls.IsEmpty()) {
    956      elemDesc = elemDesc + u"[class=\""_ns + cls + u"\"]"_ns;
    957    }
    958 
    959    if (curr->IsElement() &&
    960        curr->AsElement()->HasAttr(nsGkAtoms::contenteditable)) {
    961      nsAutoString val;
    962      curr->AsElement()->GetAttr(nsGkAtoms::contenteditable, val);
    963      elemDesc = elemDesc + u"[contenteditable=\""_ns + val + u"\"]"_ns;
    964    }
    965    if (curr->IsDocument() && curr->IsInDesignMode()) {
    966      elemDesc.Append(u"[designMode=\"on\"]"_ns);
    967    }
    968 
    969    curr = curr->GetParentNode();
    970  }
    971 
    972  NS_ConvertUTF16toUTF8 str(elemDesc);
    973  return aStream << str.get();
    974 }
    975 
    976 nsIContent* nsINode::DoGetShadowHost() const {
    977  MOZ_ASSERT(IsShadowRoot());
    978  return static_cast<const ShadowRoot*>(this)->GetHost();
    979 }
    980 
    981 ShadowRoot* nsINode::GetContainingShadow() const {
    982  if (!IsInShadowTree()) {
    983    return nullptr;
    984  }
    985  return AsContent()->GetContainingShadow();
    986 }
    987 
    988 Element* nsINode::GetContainingShadowHost() const {
    989  if (ShadowRoot* shadow = GetContainingShadow()) {
    990    return shadow->GetHost();
    991  }
    992  return nullptr;
    993 }
    994 
    995 SVGUseElement* nsINode::DoGetContainingSVGUseShadowHost() const {
    996  MOZ_ASSERT(IsInShadowTree());
    997  return SVGUseElement::FromNodeOrNull(GetContainingShadowHost());
    998 }
    999 
   1000 void nsINode::GetNodeValueInternal(nsAString& aNodeValue) {
   1001  SetDOMStringToNull(aNodeValue);
   1002 }
   1003 
   1004 static const char* NodeTypeAsString(nsINode* aNode) {
   1005  static const char* NodeTypeStrings[] = {
   1006      "",  // No nodes of type 0
   1007      "an Element",
   1008      "an Attribute",
   1009      "a Text",
   1010      "a CDATASection",
   1011      "an EntityReference",
   1012      "an Entity",
   1013      "a ProcessingInstruction",
   1014      "a Comment",
   1015      "a Document",
   1016      "a DocumentType",
   1017      "a DocumentFragment",
   1018      "a Notation",
   1019  };
   1020  static_assert(std::size(NodeTypeStrings) == nsINode::MAX_NODE_TYPE + 1,
   1021                "Max node type out of range for our array");
   1022 
   1023  uint16_t nodeType = aNode->NodeType();
   1024  MOZ_RELEASE_ASSERT(nodeType < std::size(NodeTypeStrings),
   1025                     "Uknown out-of-range node type");
   1026  return NodeTypeStrings[nodeType];
   1027 }
   1028 
   1029 nsINode* nsINode::RemoveChildInternal(
   1030    nsINode& aOldChild, MutationEffectOnScript aMutationEffectOnScript,
   1031    ErrorResult& aError) {
   1032  if (!aOldChild.IsContent()) {
   1033    // aOldChild can't be one of our children.
   1034    aError.ThrowNotFoundError(
   1035        "The node to be removed is not a child of this node");
   1036    return nullptr;
   1037  }
   1038 
   1039  if (aOldChild.GetParentNode() == this) {
   1040    nsContentUtils::NotifyDevToolsOfNodeRemoval(aOldChild);
   1041  }
   1042 
   1043  // Check again, we may not be the child's parent anymore.
   1044  // Can be triggered by dom/base/crashtests/293388-1.html
   1045  if (aOldChild.IsRootOfNativeAnonymousSubtree() ||
   1046      aOldChild.GetParentNode() != this) {
   1047    // aOldChild isn't one of our children.
   1048    aError.ThrowNotFoundError(
   1049        "The node to be removed is not a child of this node");
   1050    return nullptr;
   1051  }
   1052 
   1053  RemoveChildNode(aOldChild.AsContent(), true, nullptr, nullptr,
   1054                  aMutationEffectOnScript);
   1055  return &aOldChild;
   1056 }
   1057 
   1058 void nsINode::Normalize() {
   1059  // First collect list of nodes to be removed
   1060  AutoTArray<nsCOMPtr<nsIContent>, 50> nodes;
   1061 
   1062  bool canMerge = false;
   1063  for (nsIContent* node = this->GetFirstChild(); node;
   1064       node = node->GetNextNode(this)) {
   1065    if (node->NodeType() != TEXT_NODE) {
   1066      canMerge = false;
   1067      continue;
   1068    }
   1069 
   1070    if (canMerge || node->TextLength() == 0) {
   1071      // No need to touch canMerge. That way we can merge across empty
   1072      // textnodes if and only if the node before is a textnode
   1073      nodes.AppendElement(node);
   1074    } else {
   1075      canMerge = true;
   1076    }
   1077 
   1078    // If there's no following sibling, then we need to ensure that we don't
   1079    // collect following siblings of our (grand)parent as to-be-removed
   1080    canMerge = canMerge && !!node->GetNextSibling();
   1081  }
   1082 
   1083  if (nodes.IsEmpty()) {
   1084    return;
   1085  }
   1086 
   1087  const RefPtr<Document> doc = OwnerDoc();
   1088 
   1089  // Let DevTools know the node removals if and only if DevTools is observing
   1090  // the mutations.
   1091  const bool notifyDevToolsOfNodeRemovals =
   1092      MaybeNeedsToNotifyDevToolsOfNodeRemovalsInOwnerDoc();
   1093  if (MOZ_UNLIKELY(notifyDevToolsOfNodeRemovals)) {
   1094    for (const nsCOMPtr<nsIContent>& node : nodes) {
   1095      // Node may have already been removed.
   1096      if (node->GetParentNode()) {
   1097        // TODO: MOZ_KnownLive because of Bug 1620312
   1098        nsContentUtils::NotifyDevToolsOfNodeRemoval(MOZ_KnownLive(*node));
   1099      }
   1100    }
   1101  }
   1102 
   1103  mozAutoDocUpdate batch(doc, true);
   1104 
   1105  // Merge and remove all nodes
   1106  nsAutoString tmpStr;
   1107  for (uint32_t i = 0; i < nodes.Length(); ++i) {
   1108    nsIContent* node = nodes[i];
   1109    // Merge with previous node unless empty
   1110    const CharacterDataBuffer* characterDataBuffer =
   1111        node->GetCharacterDataBuffer();
   1112    if (characterDataBuffer->GetLength()) {
   1113      nsIContent* target = node->GetPreviousSibling();
   1114      NS_ASSERTION((target && target->NodeType() == TEXT_NODE) ||
   1115                       notifyDevToolsOfNodeRemovals,
   1116                   "Should always have a previous text sibling unless "
   1117                   "mutation events messed us up");
   1118      if (MOZ_LIKELY(!notifyDevToolsOfNodeRemovals) ||
   1119          (target && target->NodeType() == TEXT_NODE)) {
   1120        nsTextNode* t = static_cast<nsTextNode*>(target);
   1121        if (characterDataBuffer->Is2b()) {
   1122          t->AppendTextForNormalize(characterDataBuffer->Get2b(),
   1123                                    characterDataBuffer->GetLength(), true,
   1124                                    node);
   1125        } else {
   1126          tmpStr.Truncate();
   1127          characterDataBuffer->AppendTo(tmpStr);
   1128          t->AppendTextForNormalize(tmpStr.get(), tmpStr.Length(), true, node);
   1129        }
   1130      }
   1131    }
   1132 
   1133    // Remove node
   1134    nsCOMPtr<nsINode> parent = node->GetParentNode();
   1135    NS_ASSERTION(parent || notifyDevToolsOfNodeRemovals,
   1136                 "Should always have a parent unless "
   1137                 "mutation events messed us up");
   1138    if (parent) {
   1139      parent->RemoveChildNode(node, true, nullptr, nullptr,
   1140                              MutationEffectOnScript::KeepTrustWorthiness);
   1141    }
   1142  }
   1143 }
   1144 
   1145 nsresult nsINode::GetBaseURI(nsAString& aURI) const {
   1146  nsIURI* baseURI = GetBaseURI();
   1147 
   1148  nsAutoCString spec;
   1149  if (baseURI) {
   1150    nsresult rv = baseURI->GetSpec(spec);
   1151    NS_ENSURE_SUCCESS(rv, rv);
   1152  }
   1153 
   1154  CopyUTF8toUTF16(spec, aURI);
   1155  return NS_OK;
   1156 }
   1157 
   1158 void nsINode::GetBaseURIFromJS(nsAString& aURI, CallerType aCallerType,
   1159                               ErrorResult& aRv) const {
   1160  nsIURI* baseURI = GetBaseURI(aCallerType == CallerType::System);
   1161  nsAutoCString spec;
   1162  if (baseURI) {
   1163    nsresult res = baseURI->GetSpec(spec);
   1164    if (NS_FAILED(res)) {
   1165      aRv.Throw(res);
   1166      return;
   1167    }
   1168  }
   1169  CopyUTF8toUTF16(spec, aURI);
   1170 }
   1171 
   1172 nsIURI* nsINode::GetBaseURIObject() const { return GetBaseURI(true); }
   1173 
   1174 void nsINode::LookupPrefix(const nsAString& aNamespaceURI, nsAString& aPrefix) {
   1175  if (Element* nsElement = GetNameSpaceElement()) {
   1176    // XXX Waiting for DOM spec to list error codes.
   1177 
   1178    // Trace up the content parent chain looking for the namespace
   1179    // declaration that defines the aNamespaceURI namespace. Once found,
   1180    // return the prefix (i.e. the attribute localName).
   1181    for (Element* element : nsElement->InclusiveAncestorsOfType<Element>()) {
   1182      uint32_t attrCount = element->GetAttrCount();
   1183 
   1184      for (uint32_t i = 0; i < attrCount; ++i) {
   1185        const nsAttrName* name = element->GetAttrNameAt(i);
   1186 
   1187        if (name->NamespaceEquals(kNameSpaceID_XMLNS) &&
   1188            element->AttrValueIs(kNameSpaceID_XMLNS, name->LocalName(),
   1189                                 aNamespaceURI, eCaseMatters)) {
   1190          // If the localName is "xmlns", the prefix we output should be
   1191          // null.
   1192          nsAtom* localName = name->LocalName();
   1193 
   1194          if (localName != nsGkAtoms::xmlns) {
   1195            localName->ToString(aPrefix);
   1196          } else {
   1197            SetDOMStringToNull(aPrefix);
   1198          }
   1199          return;
   1200        }
   1201      }
   1202    }
   1203  }
   1204 
   1205  SetDOMStringToNull(aPrefix);
   1206 }
   1207 
   1208 uint16_t nsINode::CompareDocumentPosition(const nsINode& aOtherNode) const {
   1209  if (this == &aOtherNode) {
   1210    return 0;
   1211  }
   1212  if (GetPreviousSibling() == &aOtherNode) {
   1213    MOZ_ASSERT(GetParentNode() == aOtherNode.GetParentNode());
   1214    return Node_Binding::DOCUMENT_POSITION_PRECEDING;
   1215  }
   1216  if (GetNextSibling() == &aOtherNode) {
   1217    MOZ_ASSERT(GetParentNode() == aOtherNode.GetParentNode());
   1218    return Node_Binding::DOCUMENT_POSITION_FOLLOWING;
   1219  }
   1220 
   1221  AutoTArray<const nsINode*, 32> parents1, parents2;
   1222 
   1223  const nsINode* node1 = &aOtherNode;
   1224  const nsINode* node2 = this;
   1225 
   1226  // Check if either node is an attribute
   1227  const Attr* attr1 = Attr::FromNode(node1);
   1228  if (attr1) {
   1229    const Element* elem = attr1->GetElement();
   1230    // If there is an owner element add the attribute
   1231    // to the chain and walk up to the element
   1232    if (elem) {
   1233      node1 = elem;
   1234      parents1.AppendElement(attr1);
   1235    }
   1236  }
   1237  if (auto* attr2 = Attr::FromNode(node2)) {
   1238    const Element* elem = attr2->GetElement();
   1239    if (elem == node1 && attr1) {
   1240      // Both nodes are attributes on the same element.
   1241      // Compare position between the attributes.
   1242 
   1243      uint32_t i;
   1244      const nsAttrName* attrName;
   1245      for (i = 0; elem->GetAttrNameAt(i, &attrName); ++i) {
   1246        if (attrName->Equals(attr1->NodeInfo())) {
   1247          NS_ASSERTION(!attrName->Equals(attr2->NodeInfo()),
   1248                       "Different attrs at same position");
   1249          return Node_Binding::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC |
   1250                 Node_Binding::DOCUMENT_POSITION_PRECEDING;
   1251        }
   1252        if (attrName->Equals(attr2->NodeInfo())) {
   1253          return Node_Binding::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC |
   1254                 Node_Binding::DOCUMENT_POSITION_FOLLOWING;
   1255        }
   1256      }
   1257      MOZ_ASSERT_UNREACHABLE("neither attribute in the element");
   1258      return Node_Binding::DOCUMENT_POSITION_DISCONNECTED;
   1259    }
   1260 
   1261    if (elem) {
   1262      node2 = elem;
   1263      parents2.AppendElement(attr2);
   1264    }
   1265  }
   1266 
   1267  // We now know that both nodes are either nsIContents or Documents.
   1268  // If either node started out as an attribute, that attribute will have
   1269  // the same relative position as its ownerElement, except if the
   1270  // ownerElement ends up being the container for the other node
   1271 
   1272  // Build the chain of parents
   1273  do {
   1274    parents1.AppendElement(node1);
   1275    node1 = node1->GetParentNode();
   1276  } while (node1);
   1277  do {
   1278    parents2.AppendElement(node2);
   1279    node2 = node2->GetParentNode();
   1280  } while (node2);
   1281 
   1282  // Check if the nodes are disconnected.
   1283  uint32_t pos1 = parents1.Length();
   1284  uint32_t pos2 = parents2.Length();
   1285  const nsINode* top1 = parents1.ElementAt(--pos1);
   1286  const nsINode* top2 = parents2.ElementAt(--pos2);
   1287  if (top1 != top2) {
   1288    return top1 < top2
   1289               ? (Node_Binding::DOCUMENT_POSITION_PRECEDING |
   1290                  Node_Binding::DOCUMENT_POSITION_DISCONNECTED |
   1291                  Node_Binding::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC)
   1292               : (Node_Binding::DOCUMENT_POSITION_FOLLOWING |
   1293                  Node_Binding::DOCUMENT_POSITION_DISCONNECTED |
   1294                  Node_Binding::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC);
   1295  }
   1296 
   1297  // Find where the parent chain differs and check indices in the parent.
   1298  const nsINode* parent = top1;
   1299  uint32_t len;
   1300  for (len = std::min(pos1, pos2); len > 0; --len) {
   1301    const nsINode* child1 = parents1.ElementAt(--pos1);
   1302    const nsINode* child2 = parents2.ElementAt(--pos2);
   1303    if (child1 != child2) {
   1304      // child1 or child2 can be an attribute here. This will work fine since
   1305      // ComputeIndexOf will return Nothing for the attribute making the
   1306      // attribute be considered before any child.
   1307      Maybe<uint32_t> child1Index = parent->ComputeIndexOf(child1);
   1308      Maybe<uint32_t> child2Index = parent->ComputeIndexOf(child2);
   1309      return child1Index < child2Index
   1310                 ? Node_Binding::DOCUMENT_POSITION_PRECEDING
   1311                 : Node_Binding::DOCUMENT_POSITION_FOLLOWING;
   1312    }
   1313    parent = child1;
   1314  }
   1315 
   1316  // We hit the end of one of the parent chains without finding a difference
   1317  // between the chains. That must mean that one node is an ancestor of the
   1318  // other. The one with the shortest chain must be the ancestor.
   1319  return pos1 < pos2 ? (Node_Binding::DOCUMENT_POSITION_PRECEDING |
   1320                        Node_Binding::DOCUMENT_POSITION_CONTAINS)
   1321                     : (Node_Binding::DOCUMENT_POSITION_FOLLOWING |
   1322                        Node_Binding::DOCUMENT_POSITION_CONTAINED_BY);
   1323 }
   1324 
   1325 bool nsINode::IsSameNode(nsINode* other) { return other == this; }
   1326 
   1327 bool nsINode::IsEqualNode(nsINode* aOther) {
   1328  if (!aOther) {
   1329    return false;
   1330  }
   1331 
   1332  // Might as well do a quick check to avoid walking our kids if we're
   1333  // obviously the same.
   1334  if (aOther == this) {
   1335    return true;
   1336  }
   1337 
   1338  nsAutoString string1, string2;
   1339 
   1340  nsINode* node1 = this;
   1341  nsINode* node2 = aOther;
   1342  do {
   1343    uint16_t nodeType = node1->NodeType();
   1344    if (nodeType != node2->NodeType()) {
   1345      return false;
   1346    }
   1347 
   1348    mozilla::dom::NodeInfo* nodeInfo1 = node1->mNodeInfo;
   1349    mozilla::dom::NodeInfo* nodeInfo2 = node2->mNodeInfo;
   1350    if (!nodeInfo1->Equals(nodeInfo2) ||
   1351        nodeInfo1->GetExtraName() != nodeInfo2->GetExtraName()) {
   1352      return false;
   1353    }
   1354 
   1355    switch (nodeType) {
   1356      case ELEMENT_NODE: {
   1357        // Both are elements (we checked that their nodeinfos are equal). Do the
   1358        // check on attributes.
   1359        Element* element1 = node1->AsElement();
   1360        Element* element2 = node2->AsElement();
   1361        uint32_t attrCount = element1->GetAttrCount();
   1362        if (attrCount != element2->GetAttrCount()) {
   1363          return false;
   1364        }
   1365 
   1366        // Iterate over attributes.
   1367        for (uint32_t i = 0; i < attrCount; ++i) {
   1368          const nsAttrName* attrName = element1->GetAttrNameAt(i);
   1369 #ifdef DEBUG
   1370          bool hasAttr =
   1371 #endif
   1372              element1->GetAttr(attrName->NamespaceID(), attrName->LocalName(),
   1373                                string1);
   1374          NS_ASSERTION(hasAttr, "Why don't we have an attr?");
   1375 
   1376          if (!element2->AttrValueIs(attrName->NamespaceID(),
   1377                                     attrName->LocalName(), string1,
   1378                                     eCaseMatters)) {
   1379            return false;
   1380          }
   1381        }
   1382        break;
   1383      }
   1384      case TEXT_NODE:
   1385      case COMMENT_NODE:
   1386      case CDATA_SECTION_NODE:
   1387      case PROCESSING_INSTRUCTION_NODE: {
   1388        MOZ_ASSERT(node1->IsCharacterData());
   1389        MOZ_ASSERT(node2->IsCharacterData());
   1390        auto* data1 = static_cast<CharacterData*>(node1);
   1391        auto* data2 = static_cast<CharacterData*>(node2);
   1392 
   1393        if (!data1->TextEquals(data2)) {
   1394          return false;
   1395        }
   1396 
   1397        break;
   1398      }
   1399      case DOCUMENT_NODE:
   1400      case DOCUMENT_FRAGMENT_NODE:
   1401        break;
   1402      case ATTRIBUTE_NODE: {
   1403        NS_ASSERTION(node1 == this && node2 == aOther,
   1404                     "Did we come upon an attribute node while walking a "
   1405                     "subtree?");
   1406        node1->GetNodeValue(string1);
   1407        node2->GetNodeValue(string2);
   1408 
   1409        // Returning here as to not bother walking subtree. And there is no
   1410        // risk that we're half way through walking some other subtree since
   1411        // attribute nodes doesn't appear in subtrees.
   1412        return string1.Equals(string2);
   1413      }
   1414      case DOCUMENT_TYPE_NODE: {
   1415        DocumentType* docType1 = static_cast<DocumentType*>(node1);
   1416        DocumentType* docType2 = static_cast<DocumentType*>(node2);
   1417 
   1418        // Public ID
   1419        docType1->GetPublicId(string1);
   1420        docType2->GetPublicId(string2);
   1421        if (!string1.Equals(string2)) {
   1422          return false;
   1423        }
   1424 
   1425        // System ID
   1426        docType1->GetSystemId(string1);
   1427        docType2->GetSystemId(string2);
   1428        if (!string1.Equals(string2)) {
   1429          return false;
   1430        }
   1431 
   1432        break;
   1433      }
   1434      default:
   1435        MOZ_ASSERT(false, "Unknown node type");
   1436    }
   1437 
   1438    nsINode* nextNode = node1->GetFirstChild();
   1439    if (nextNode) {
   1440      node1 = nextNode;
   1441      node2 = node2->GetFirstChild();
   1442    } else {
   1443      if (node2->GetFirstChild()) {
   1444        // node2 has a firstChild, but node1 doesn't
   1445        return false;
   1446      }
   1447 
   1448      // Find next sibling, possibly walking parent chain.
   1449      while (1) {
   1450        if (node1 == this) {
   1451          NS_ASSERTION(node2 == aOther,
   1452                       "Should have reached the start node "
   1453                       "for both trees at the same time");
   1454          return true;
   1455        }
   1456 
   1457        nextNode = node1->GetNextSibling();
   1458        if (nextNode) {
   1459          node1 = nextNode;
   1460          node2 = node2->GetNextSibling();
   1461          break;
   1462        }
   1463 
   1464        if (node2->GetNextSibling()) {
   1465          // node2 has a nextSibling, but node1 doesn't
   1466          return false;
   1467        }
   1468 
   1469        node1 = node1->GetParentNode();
   1470        node2 = node2->GetParentNode();
   1471        NS_ASSERTION(node1 && node2, "no parent while walking subtree");
   1472      }
   1473    }
   1474  } while (node2);
   1475 
   1476  return false;
   1477 }
   1478 
   1479 void nsINode::LookupNamespaceURI(const nsAString& aNamespacePrefix,
   1480                                 nsAString& aNamespaceURI) {
   1481  Element* element = GetNameSpaceElement();
   1482  if (!element || NS_FAILED(element->LookupNamespaceURIInternal(
   1483                      aNamespacePrefix, aNamespaceURI))) {
   1484    SetDOMStringToNull(aNamespaceURI);
   1485  }
   1486 }
   1487 
   1488 mozilla::Maybe<mozilla::dom::EventCallbackDebuggerNotificationType>
   1489 nsINode::GetDebuggerNotificationType() const {
   1490  return mozilla::Some(
   1491      mozilla::dom::EventCallbackDebuggerNotificationType::Node);
   1492 }
   1493 
   1494 bool nsINode::ComputeDefaultWantsUntrusted(ErrorResult& aRv) {
   1495  return !nsContentUtils::IsChromeDoc(OwnerDoc());
   1496 }
   1497 
   1498 void nsINode::GetBoxQuads(const BoxQuadOptions& aOptions,
   1499                          nsTArray<RefPtr<DOMQuad>>& aResult,
   1500                          CallerType aCallerType, mozilla::ErrorResult& aRv) {
   1501  mozilla::GetBoxQuads(this, aOptions, aResult, aCallerType, aRv);
   1502 }
   1503 
   1504 void nsINode::GetBoxQuadsFromWindowOrigin(const BoxQuadOptions& aOptions,
   1505                                          nsTArray<RefPtr<DOMQuad>>& aResult,
   1506                                          mozilla::ErrorResult& aRv) {
   1507  mozilla::GetBoxQuadsFromWindowOrigin(this, aOptions, aResult, aRv);
   1508 }
   1509 
   1510 already_AddRefed<DOMQuad> nsINode::ConvertQuadFromNode(
   1511    DOMQuad& aQuad, const GeometryNode& aFrom,
   1512    const ConvertCoordinateOptions& aOptions, CallerType aCallerType,
   1513    ErrorResult& aRv) {
   1514  return mozilla::ConvertQuadFromNode(this, aQuad, aFrom, aOptions, aCallerType,
   1515                                      aRv);
   1516 }
   1517 
   1518 already_AddRefed<DOMQuad> nsINode::ConvertRectFromNode(
   1519    DOMRectReadOnly& aRect, const GeometryNode& aFrom,
   1520    const ConvertCoordinateOptions& aOptions, CallerType aCallerType,
   1521    ErrorResult& aRv) {
   1522  return mozilla::ConvertRectFromNode(this, aRect, aFrom, aOptions, aCallerType,
   1523                                      aRv);
   1524 }
   1525 
   1526 already_AddRefed<DOMPoint> nsINode::ConvertPointFromNode(
   1527    const DOMPointInit& aPoint, const GeometryNode& aFrom,
   1528    const ConvertCoordinateOptions& aOptions, CallerType aCallerType,
   1529    ErrorResult& aRv) {
   1530  return mozilla::ConvertPointFromNode(this, aPoint, aFrom, aOptions,
   1531                                       aCallerType, aRv);
   1532 }
   1533 
   1534 bool nsINode::DispatchEvent(Event& aEvent, CallerType aCallerType,
   1535                            ErrorResult& aRv) {
   1536  // XXX sXBL/XBL2 issue -- do we really want the owner here?  What
   1537  // if that's the XBL document?  Would we want its presshell?  Or what?
   1538  nsCOMPtr<Document> document = OwnerDoc();
   1539 
   1540  // Do nothing if the element does not belong to a document
   1541  if (!document) {
   1542    return true;
   1543  }
   1544 
   1545  // Obtain a presentation shell
   1546  RefPtr<nsPresContext> context = document->GetPresContext();
   1547 
   1548  nsEventStatus status = nsEventStatus_eIgnore;
   1549  nsresult rv = EventDispatcher::DispatchDOMEvent(this, nullptr, &aEvent,
   1550                                                  context, &status);
   1551  bool retval = !aEvent.DefaultPrevented(aCallerType);
   1552  if (NS_FAILED(rv)) {
   1553    aRv.Throw(rv);
   1554  }
   1555  return retval;
   1556 }
   1557 
   1558 nsresult nsINode::PostHandleEvent(EventChainPostVisitor& /*aVisitor*/) {
   1559  return NS_OK;
   1560 }
   1561 
   1562 EventListenerManager* nsINode::GetOrCreateListenerManager() {
   1563  return nsContentUtils::GetListenerManagerForNode(this);
   1564 }
   1565 
   1566 EventListenerManager* nsINode::GetExistingListenerManager() const {
   1567  return nsContentUtils::GetExistingListenerManagerForNode(this);
   1568 }
   1569 
   1570 nsPIDOMWindowOuter* nsINode::GetOwnerGlobalForBindingsInternal() {
   1571  bool dummy;
   1572  // FIXME(bz): This cast is a bit bogus.  See
   1573  // https://bugzilla.mozilla.org/show_bug.cgi?id=1515709
   1574  auto* window = static_cast<nsGlobalWindowInner*>(
   1575      OwnerDoc()->GetScriptHandlingObject(dummy));
   1576  return window ? nsPIDOMWindowOuter::GetFromCurrentInner(window) : nullptr;
   1577 }
   1578 
   1579 nsIGlobalObject* nsINode::GetOwnerGlobal() const {
   1580  bool dummy;
   1581  return OwnerDoc()->GetScriptHandlingObject(dummy);
   1582 }
   1583 
   1584 bool nsINode::UnoptimizableCCNode() const {
   1585  return IsInNativeAnonymousSubtree() || IsAttr();
   1586 }
   1587 
   1588 /* static */
   1589 bool nsINode::Traverse(nsINode* tmp, nsCycleCollectionTraversalCallback& cb) {
   1590  if (MOZ_LIKELY(!cb.WantAllTraces())) {
   1591    Document* currentDoc = tmp->GetComposedDoc();
   1592    if (currentDoc && nsCCUncollectableMarker::InGeneration(
   1593                          currentDoc->GetMarkedCCGeneration())) {
   1594      return false;
   1595    }
   1596 
   1597    if (nsCCUncollectableMarker::sGeneration) {
   1598      // If we're black no need to traverse.
   1599      if (tmp->HasKnownLiveWrapper() || tmp->InCCBlackTree()) {
   1600        return false;
   1601      }
   1602 
   1603      if (!tmp->UnoptimizableCCNode()) {
   1604        // If we're in a black document, return early.
   1605        if ((currentDoc && currentDoc->HasKnownLiveWrapper())) {
   1606          return false;
   1607        }
   1608        // If we're not in anonymous content and we have a black parent,
   1609        // return early.
   1610        nsIContent* parent = tmp->GetParent();
   1611        if (parent && !parent->UnoptimizableCCNode() &&
   1612            parent->HasKnownLiveWrapper()) {
   1613          MOZ_ASSERT(parent->ComputeIndexOf(tmp).isSome(),
   1614                     "Parent doesn't own us?");
   1615          return false;
   1616        }
   1617      }
   1618    }
   1619  }
   1620 
   1621  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfo)
   1622  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstChild)
   1623  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNextSibling)
   1624  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(GetParent())
   1625 
   1626  if (nsSlots* slots = tmp->GetExistingSlots()) {
   1627    slots->Traverse(cb);
   1628  }
   1629 
   1630  if (tmp->HasProperties()) {
   1631 #ifdef ACCESSIBILITY
   1632    auto* anode = static_cast<AccessibleNode*>(
   1633        tmp->GetProperty(nsGkAtoms::accessiblenode));
   1634    if (anode) {
   1635      cb.NoteXPCOMChild(anode);
   1636    }
   1637 #endif
   1638  }
   1639 
   1640  if (tmp->NodeType() != DOCUMENT_NODE &&
   1641      tmp->HasFlag(NODE_HAS_LISTENERMANAGER)) {
   1642    nsContentUtils::TraverseListenerManager(tmp, cb);
   1643  }
   1644 
   1645  return true;
   1646 }
   1647 
   1648 /* static */
   1649 void nsINode::Unlink(nsINode* tmp) {
   1650  tmp->ReleaseWrapper(tmp);
   1651 
   1652  if (nsSlots* slots = tmp->GetExistingSlots()) {
   1653    slots->Unlink(*tmp);
   1654  }
   1655 
   1656  if (tmp->NodeType() != DOCUMENT_NODE &&
   1657      tmp->HasFlag(NODE_HAS_LISTENERMANAGER)) {
   1658    nsContentUtils::RemoveListenerManager(tmp);
   1659    tmp->UnsetFlags(NODE_HAS_LISTENERMANAGER);
   1660  }
   1661 
   1662  if (tmp->HasProperties()) {
   1663    tmp->RemoveProperty(nsGkAtoms::accessiblenode);
   1664  }
   1665 }
   1666 
   1667 static void AdoptNodeIntoOwnerDoc(nsINode* aParent, nsINode* aNode,
   1668                                  ErrorResult& aError) {
   1669  NS_ASSERTION(!aNode->GetParentNode(),
   1670               "Should have removed from parent already");
   1671 
   1672  Document* doc = aParent->OwnerDoc();
   1673 
   1674  DebugOnly<nsINode*> adoptedNode = doc->AdoptNode(*aNode, aError, true);
   1675 
   1676 #ifdef DEBUG
   1677  if (!aError.Failed()) {
   1678    MOZ_ASSERT(aParent->OwnerDoc() == doc, "ownerDoc chainged while adopting");
   1679    MOZ_ASSERT(adoptedNode == aNode, "Uh, adopt node changed nodes?");
   1680    MOZ_ASSERT(aParent->OwnerDoc() == aNode->OwnerDoc(),
   1681               "ownerDocument changed again after adopting!");
   1682  }
   1683 #endif  // DEBUG
   1684 }
   1685 
   1686 static nsresult UpdateGlobalsInSubtree(nsIContent* aRoot) {
   1687  MOZ_ASSERT(ShouldUseNACScope(aRoot));
   1688  // Start off with no global so we don't fire any error events on failure.
   1689  AutoJSAPI jsapi;
   1690  jsapi.Init();
   1691 
   1692  JSContext* cx = jsapi.cx();
   1693 
   1694  ErrorResult rv;
   1695  JS::Rooted<JSObject*> reflector(cx);
   1696  for (nsIContent* cur = aRoot; cur; cur = cur->GetNextNode(aRoot)) {
   1697    if ((reflector = cur->GetWrapper())) {
   1698      JSAutoRealm ar(cx, reflector);
   1699      UpdateReflectorGlobal(cx, reflector, rv);
   1700      rv.WouldReportJSException();
   1701      if (rv.Failed()) {
   1702        // We _could_ consider BlastSubtreeToPieces here, but it's not really
   1703        // needed.  Having some nodes in here accessible to content while others
   1704        // are not is probably OK.  We just need to fail out of the actual
   1705        // insertion, so they're not in the DOM.  Returning a failure here will
   1706        // do that.
   1707        return rv.StealNSResult();
   1708      }
   1709    }
   1710  }
   1711 
   1712  return NS_OK;
   1713 }
   1714 
   1715 void nsINode::InsertChildBefore(
   1716    nsIContent* aKid, nsIContent* aBeforeThis, bool aNotify, ErrorResult& aRv,
   1717    nsINode* aOldParent, MutationEffectOnScript aMutationEffectOnScript) {
   1718  if (!IsContainerNode()) {
   1719    aRv.ThrowHierarchyRequestError(
   1720        "Parent is not a Document, DocumentFragment, or Element node.");
   1721    return;
   1722  }
   1723 
   1724  MOZ_ASSERT(!aKid->GetParentNode(), "Inserting node that already has parent");
   1725  MOZ_ASSERT(!IsAttr());
   1726 
   1727  // The id-handling code, and in the future possibly other code, need to
   1728  // react to unexpected attribute changes.
   1729  nsMutationGuard::DidMutate();
   1730 
   1731  // Do this before checking the child-count since this could cause mutations
   1732  mozAutoDocUpdate updateBatch(GetComposedDoc(), aNotify);
   1733 
   1734  if (OwnerDoc() != aKid->OwnerDoc()) {
   1735    AdoptNodeIntoOwnerDoc(this, aKid, aRv);
   1736    if (NS_WARN_IF(aRv.Failed())) {
   1737      return;
   1738    }
   1739  }
   1740 
   1741  if (!aBeforeThis) {
   1742    AppendChildToChildList(aKid);
   1743  } else {
   1744    InsertChildToChildList(aKid, aBeforeThis);
   1745  }
   1746 
   1747  nsIContent* parent = IsContent() ? AsContent() : nullptr;
   1748 
   1749  // XXXbz Do we even need this code anymore?
   1750  bool wasInNACScope = ShouldUseNACScope(aKid);
   1751  BindContext context(*this);
   1752  context.SetIsMove(aOldParent != nullptr);
   1753  aRv = aKid->BindToTree(context, *this);
   1754  if (!aRv.Failed() && !wasInNACScope && ShouldUseNACScope(aKid)) {
   1755    MOZ_ASSERT(ShouldUseNACScope(this),
   1756               "Why does the kid need to use an the anonymous content scope?");
   1757    aRv = UpdateGlobalsInSubtree(aKid);
   1758  }
   1759  if (aRv.Failed()) {
   1760    DisconnectChild(aKid);
   1761    aKid->UnbindFromTree();
   1762    return;
   1763  }
   1764 
   1765  // Invalidate cached array of child nodes
   1766  InvalidateChildNodes();
   1767 
   1768  NS_ASSERTION(aKid->GetParentNode() == this,
   1769               "Did we run script inappropriately?");
   1770 
   1771  if (aNotify) {
   1772    // Note that we always want to call ContentInserted when things are added
   1773    // as kids to documents
   1774    if (parent && !aBeforeThis) {
   1775      ContentAppendInfo info;
   1776      info.mOldParent = aOldParent;
   1777      info.mMutationEffectOnScript = aMutationEffectOnScript;
   1778      MutationObservers::NotifyContentAppended(parent, aKid, info);
   1779    } else {
   1780      ContentInsertInfo info;
   1781      info.mOldParent = aOldParent;
   1782      info.mMutationEffectOnScript = aMutationEffectOnScript;
   1783      MutationObservers::NotifyContentInserted(this, aKid, info);
   1784    }
   1785  }
   1786 }
   1787 
   1788 nsIContent* nsINode::GetPreviousSibling() const {
   1789  // Do not expose circular linked list
   1790  if (mPreviousOrLastSibling && !mPreviousOrLastSibling->mNextSibling) {
   1791    return nullptr;
   1792  }
   1793  return mPreviousOrLastSibling;
   1794 }
   1795 
   1796 // CACHE_POINTER_SHIFT indicates how many steps to downshift the |this| pointer.
   1797 // It should be small enough to not cause collisions between adjecent objects,
   1798 // and large enough to make sure that all indexes are used.
   1799 #define CACHE_POINTER_SHIFT 6
   1800 #define CACHE_NUM_SLOTS 128
   1801 #define CACHE_CHILD_LIMIT 10
   1802 
   1803 #define CACHE_GET_INDEX(_parent) \
   1804  ((NS_PTR_TO_INT32(_parent) >> CACHE_POINTER_SHIFT) & (CACHE_NUM_SLOTS - 1))
   1805 
   1806 struct IndexCacheSlot {
   1807  const nsINode* mParent;
   1808  const nsINode* mChild;
   1809  uint32_t mChildIndex;
   1810 };
   1811 
   1812 static IndexCacheSlot sIndexCache[CACHE_NUM_SLOTS];
   1813 
   1814 static inline void AddChildAndIndexToCache(const nsINode* aParent,
   1815                                           const nsINode* aChild,
   1816                                           uint32_t aChildIndex) {
   1817  uint32_t index = CACHE_GET_INDEX(aParent);
   1818  sIndexCache[index].mParent = aParent;
   1819  sIndexCache[index].mChild = aChild;
   1820  sIndexCache[index].mChildIndex = aChildIndex;
   1821 }
   1822 
   1823 static inline void GetChildAndIndexFromCache(const nsINode* aParent,
   1824                                             const nsINode** aChild,
   1825                                             Maybe<uint32_t>* aChildIndex) {
   1826  uint32_t index = CACHE_GET_INDEX(aParent);
   1827  if (sIndexCache[index].mParent == aParent) {
   1828    *aChild = sIndexCache[index].mChild;
   1829    *aChildIndex = Some(sIndexCache[index].mChildIndex);
   1830  } else {
   1831    *aChild = nullptr;
   1832    *aChildIndex = Nothing();
   1833  }
   1834 }
   1835 
   1836 static inline void RemoveFromCache(const nsINode* aParent) {
   1837  uint32_t index = CACHE_GET_INDEX(aParent);
   1838  if (sIndexCache[index].mParent == aParent) {
   1839    sIndexCache[index] = {nullptr, nullptr, UINT32_MAX};
   1840  }
   1841 }
   1842 
   1843 void nsINode::AppendChildToChildList(nsIContent* aKid) {
   1844  MOZ_ASSERT(aKid);
   1845  MOZ_ASSERT(!aKid->mNextSibling);
   1846 
   1847  RemoveFromCache(this);
   1848 
   1849  if (mFirstChild) {
   1850    nsIContent* lastChild = GetLastChild();
   1851    lastChild->mNextSibling = aKid;
   1852    aKid->mPreviousOrLastSibling = lastChild;
   1853  } else {
   1854    mFirstChild = aKid;
   1855  }
   1856 
   1857  // Maintain link to the last child
   1858  mFirstChild->mPreviousOrLastSibling = aKid;
   1859  ++mChildCount;
   1860 }
   1861 
   1862 void nsINode::InsertChildToChildList(nsIContent* aKid,
   1863                                     nsIContent* aNextSibling) {
   1864  MOZ_ASSERT(aKid);
   1865  MOZ_ASSERT(aNextSibling);
   1866 
   1867  RemoveFromCache(this);
   1868 
   1869  nsIContent* previousSibling = aNextSibling->mPreviousOrLastSibling;
   1870  aNextSibling->mPreviousOrLastSibling = aKid;
   1871  aKid->mPreviousOrLastSibling = previousSibling;
   1872  aKid->mNextSibling = aNextSibling;
   1873 
   1874  if (aNextSibling == mFirstChild) {
   1875    MOZ_ASSERT(!previousSibling->mNextSibling);
   1876    mFirstChild = aKid;
   1877  } else {
   1878    previousSibling->mNextSibling = aKid;
   1879  }
   1880 
   1881  ++mChildCount;
   1882 }
   1883 
   1884 void nsINode::DisconnectChild(nsIContent* aKid) {
   1885  MOZ_ASSERT(aKid);
   1886  MOZ_ASSERT(GetChildCount() > 0);
   1887 
   1888  RemoveFromCache(this);
   1889 
   1890  nsIContent* previousSibling = aKid->GetPreviousSibling();
   1891  nsCOMPtr<nsIContent> ref = aKid;
   1892 
   1893  if (aKid->mNextSibling) {
   1894    aKid->mNextSibling->mPreviousOrLastSibling = aKid->mPreviousOrLastSibling;
   1895  } else {
   1896    // aKid is the last child in the list
   1897    mFirstChild->mPreviousOrLastSibling = aKid->mPreviousOrLastSibling;
   1898  }
   1899  aKid->mPreviousOrLastSibling = nullptr;
   1900 
   1901  if (previousSibling) {
   1902    previousSibling->mNextSibling = std::move(aKid->mNextSibling);
   1903  } else {
   1904    // aKid is the first child in the list
   1905    mFirstChild = std::move(aKid->mNextSibling);
   1906  }
   1907 
   1908  --mChildCount;
   1909 }
   1910 
   1911 nsIContent* nsINode::GetChildAt_Deprecated(uint32_t aIndex) const {
   1912  if (aIndex >= GetChildCount()) {
   1913    return nullptr;
   1914  }
   1915 
   1916  nsIContent* child = mFirstChild;
   1917  while (aIndex--) {
   1918    child = child->GetNextSibling();
   1919  }
   1920 
   1921  return child;
   1922 }
   1923 
   1924 nsINode* nsINode::GetChildAtInFlatTree(uint32_t aIndex) const {
   1925  if (const auto* slot = HTMLSlotElement::FromNode(this)) {
   1926    const auto& assignedNodes = slot->AssignedNodes();
   1927    if (!assignedNodes.IsEmpty()) {
   1928      if (aIndex >= assignedNodes.Length()) {
   1929        return nullptr;
   1930      }
   1931      return assignedNodes[aIndex];
   1932    }
   1933  } else if (auto* shadowRoot = GetShadowRoot()) {
   1934    return shadowRoot->GetChildAtInFlatTree(aIndex);
   1935  }
   1936  return GetChildAt_Deprecated(aIndex);
   1937 }
   1938 
   1939 int32_t nsINode::ComputeIndexOf_Deprecated(
   1940    const nsINode* aPossibleChild) const {
   1941  Maybe<uint32_t> maybeIndex = ComputeIndexOf(aPossibleChild);
   1942  if (!maybeIndex) {
   1943    return -1;
   1944  }
   1945  MOZ_ASSERT(*maybeIndex <= INT32_MAX,
   1946             "ComputeIndexOf_Deprecated() returns unsupported index value, use "
   1947             "ComputeIndex() instead");
   1948  return static_cast<int32_t>(*maybeIndex);
   1949 }
   1950 
   1951 Maybe<uint32_t> nsINode::ComputeIndexOf(const nsINode* aPossibleChild) const {
   1952  if (!aPossibleChild) {
   1953    return Nothing();
   1954  }
   1955 
   1956  if (aPossibleChild->GetParentNode() != this) {
   1957    return Nothing();
   1958  }
   1959 
   1960  if (aPossibleChild == GetFirstChild()) {
   1961    return Some(0);
   1962  }
   1963 
   1964  if (aPossibleChild == GetLastChild()) {
   1965    MOZ_ASSERT(GetChildCount());
   1966    return Some(GetChildCount() - 1);
   1967  }
   1968 
   1969  if (MaybeCachesComputedIndex()) {
   1970    const nsINode* child;
   1971    Maybe<uint32_t> maybeChildIndex;
   1972    GetChildAndIndexFromCache(this, &child, &maybeChildIndex);
   1973    if (child) {
   1974      if (child == aPossibleChild) {
   1975        return maybeChildIndex;
   1976      }
   1977 
   1978      uint32_t nextIndex = *maybeChildIndex;
   1979      uint32_t prevIndex = *maybeChildIndex;
   1980      nsINode* prev = child->GetPreviousSibling();
   1981      nsINode* next = child->GetNextSibling();
   1982      do {
   1983        if (next) {
   1984          MOZ_ASSERT(nextIndex < UINT32_MAX);
   1985          ++nextIndex;
   1986          if (next == aPossibleChild) {
   1987            AddChildAndIndexToCache(this, aPossibleChild, nextIndex);
   1988            return Some(nextIndex);
   1989          }
   1990          next = next->GetNextSibling();
   1991        }
   1992        if (prev) {
   1993          MOZ_ASSERT(prevIndex > 0);
   1994          --prevIndex;
   1995          if (prev == aPossibleChild) {
   1996            AddChildAndIndexToCache(this, aPossibleChild, prevIndex);
   1997            return Some(prevIndex);
   1998          }
   1999          prev = prev->GetPreviousSibling();
   2000        }
   2001      } while (prev || next);
   2002    }
   2003  }
   2004 
   2005  uint32_t index = 0u;
   2006  nsINode* current = mFirstChild;
   2007  while (current) {
   2008    MOZ_ASSERT(current->GetParentNode() == this);
   2009    if (current == aPossibleChild) {
   2010      if (MaybeCachesComputedIndex()) {
   2011        AddChildAndIndexToCache(this, current, index);
   2012      }
   2013      return Some(index);
   2014    }
   2015    current = current->GetNextSibling();
   2016    MOZ_ASSERT(index < UINT32_MAX);
   2017    ++index;
   2018  }
   2019 
   2020  return Nothing();
   2021 }
   2022 
   2023 bool nsINode::MaybeCachesComputedIndex() const {
   2024  return mChildCount >= CACHE_CHILD_LIMIT;
   2025 }
   2026 
   2027 Maybe<uint32_t> nsINode::ComputeIndexInParentNode() const {
   2028  nsINode* parent = GetParentNode();
   2029  if (MOZ_UNLIKELY(!parent)) {
   2030    return Nothing();
   2031  }
   2032  return parent->ComputeIndexOf(this);
   2033 }
   2034 
   2035 Maybe<uint32_t> nsINode::ComputeIndexInParentContent() const {
   2036  nsIContent* parent = GetParent();
   2037  if (MOZ_UNLIKELY(!parent)) {
   2038    return Nothing();
   2039  }
   2040  return parent->ComputeIndexOf(this);
   2041 }
   2042 
   2043 bool nsINode::MaybeParentCachesComputedIndex() const {
   2044  nsINode* parent = GetParentNode();
   2045  return parent && parent->MaybeCachesComputedIndex();
   2046 }
   2047 
   2048 uint32_t nsINode::GetFlatTreeChildCount() const {
   2049  return FlattenedChildIterator::GetLength(this);
   2050 }
   2051 
   2052 Maybe<uint32_t> nsINode::ComputeFlatTreeIndexOf(
   2053    const nsINode* aPossibleChild) const {
   2054  return FlattenedChildIterator::GetIndexOf(this, aPossibleChild);
   2055 }
   2056 
   2057 static already_AddRefed<nsINode> GetNodeFromNodeOrString(
   2058    const OwningNodeOrString& aNode, Document* aDocument) {
   2059  if (aNode.IsNode()) {
   2060    nsCOMPtr<nsINode> node = aNode.GetAsNode();
   2061    return node.forget();
   2062  }
   2063 
   2064  if (aNode.IsString()) {
   2065    RefPtr<nsTextNode> textNode =
   2066        aDocument->CreateTextNode(aNode.GetAsString());
   2067    return textNode.forget();
   2068  }
   2069 
   2070  MOZ_CRASH("Impossible type");
   2071 }
   2072 
   2073 /**
   2074 * Implement the algorithm specified at
   2075 * https://dom.spec.whatwg.org/#converting-nodes-into-a-node for |prepend()|,
   2076 * |append()|, |before()|, |after()|, and |replaceWith()| APIs.
   2077 */
   2078 MOZ_CAN_RUN_SCRIPT static already_AddRefed<nsINode>
   2079 ConvertNodesOrStringsIntoNode(const Sequence<OwningNodeOrString>& aNodes,
   2080                              Document* aDocument, ErrorResult& aRv) {
   2081  if (aNodes.Length() == 1) {
   2082    return GetNodeFromNodeOrString(aNodes[0], aDocument);
   2083  }
   2084 
   2085  nsCOMPtr<nsINode> fragment = aDocument->CreateDocumentFragment();
   2086 
   2087  for (const auto& node : aNodes) {
   2088    nsCOMPtr<nsINode> childNode = GetNodeFromNodeOrString(node, aDocument);
   2089    fragment->AppendChild(*childNode, aRv);
   2090    if (aRv.Failed()) {
   2091      return nullptr;
   2092    }
   2093  }
   2094 
   2095  return fragment.forget();
   2096 }
   2097 
   2098 static void InsertNodesIntoHashset(const Sequence<OwningNodeOrString>& aNodes,
   2099                                   nsTHashSet<nsINode*>& aHashset) {
   2100  for (const auto& node : aNodes) {
   2101    if (node.IsNode()) {
   2102      aHashset.Insert(node.GetAsNode());
   2103    }
   2104  }
   2105 }
   2106 
   2107 static nsINode* FindViablePreviousSibling(
   2108    const nsINode& aNode, const Sequence<OwningNodeOrString>& aNodes) {
   2109  nsTHashSet<nsINode*> nodeSet(16);
   2110  InsertNodesIntoHashset(aNodes, nodeSet);
   2111 
   2112  nsINode* viablePreviousSibling = nullptr;
   2113  for (nsINode* sibling = aNode.GetPreviousSibling(); sibling;
   2114       sibling = sibling->GetPreviousSibling()) {
   2115    if (!nodeSet.Contains(sibling)) {
   2116      viablePreviousSibling = sibling;
   2117      break;
   2118    }
   2119  }
   2120 
   2121  return viablePreviousSibling;
   2122 }
   2123 
   2124 static nsINode* FindViableNextSibling(
   2125    const nsINode& aNode, const Sequence<OwningNodeOrString>& aNodes) {
   2126  nsTHashSet<nsINode*> nodeSet(16);
   2127  InsertNodesIntoHashset(aNodes, nodeSet);
   2128 
   2129  nsINode* viableNextSibling = nullptr;
   2130  for (nsINode* sibling = aNode.GetNextSibling(); sibling;
   2131       sibling = sibling->GetNextSibling()) {
   2132    if (!nodeSet.Contains(sibling)) {
   2133      viableNextSibling = sibling;
   2134      break;
   2135    }
   2136  }
   2137 
   2138  return viableNextSibling;
   2139 }
   2140 
   2141 void nsINode::Before(const Sequence<OwningNodeOrString>& aNodes,
   2142                     ErrorResult& aRv) {
   2143  nsCOMPtr<nsINode> parent = GetParentNode();
   2144  if (!parent) {
   2145    return;
   2146  }
   2147 
   2148  nsCOMPtr<nsINode> viablePreviousSibling =
   2149      FindViablePreviousSibling(*this, aNodes);
   2150 
   2151  nsCOMPtr<Document> doc = OwnerDoc();
   2152  nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv);
   2153  if (aRv.Failed()) {
   2154    return;
   2155  }
   2156 
   2157  viablePreviousSibling = viablePreviousSibling
   2158                              ? viablePreviousSibling->GetNextSibling()
   2159                              : parent->GetFirstChild();
   2160 
   2161  parent->InsertBefore(*node, viablePreviousSibling, aRv);
   2162 }
   2163 
   2164 void nsINode::After(const Sequence<OwningNodeOrString>& aNodes,
   2165                    ErrorResult& aRv) {
   2166  nsCOMPtr<nsINode> parent = GetParentNode();
   2167  if (!parent) {
   2168    return;
   2169  }
   2170 
   2171  nsCOMPtr<nsINode> viableNextSibling = FindViableNextSibling(*this, aNodes);
   2172 
   2173  nsCOMPtr<Document> doc = OwnerDoc();
   2174  nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv);
   2175  if (aRv.Failed()) {
   2176    return;
   2177  }
   2178 
   2179  parent->InsertBefore(*node, viableNextSibling, aRv);
   2180 }
   2181 
   2182 void nsINode::ReplaceWith(const Sequence<OwningNodeOrString>& aNodes,
   2183                          ErrorResult& aRv) {
   2184  nsCOMPtr<nsINode> parent = GetParentNode();
   2185  if (!parent) {
   2186    return;
   2187  }
   2188 
   2189  nsCOMPtr<nsINode> viableNextSibling = FindViableNextSibling(*this, aNodes);
   2190 
   2191  nsCOMPtr<Document> doc = OwnerDoc();
   2192  nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv);
   2193  if (aRv.Failed()) {
   2194    return;
   2195  }
   2196 
   2197  if (parent == GetParentNode()) {
   2198    parent->ReplaceChild(*node, *this, aRv);
   2199  } else {
   2200    parent->InsertBefore(*node, viableNextSibling, aRv);
   2201  }
   2202 }
   2203 
   2204 void nsINode::Remove() {
   2205  nsCOMPtr<nsINode> parent = GetParentNode();
   2206  if (!parent) {
   2207    return;
   2208  }
   2209 
   2210  parent->RemoveChild(*this, IgnoreErrors());
   2211 }
   2212 
   2213 Element* nsINode::GetFirstElementChild() const {
   2214  for (nsIContent* child = GetFirstChild(); child;
   2215       child = child->GetNextSibling()) {
   2216    if (child->IsElement()) {
   2217      return child->AsElement();
   2218    }
   2219  }
   2220 
   2221  return nullptr;
   2222 }
   2223 
   2224 Element* nsINode::GetLastElementChild() const {
   2225  for (nsIContent* child = GetLastChild(); child;
   2226       child = child->GetPreviousSibling()) {
   2227    if (child->IsElement()) {
   2228      return child->AsElement();
   2229    }
   2230  }
   2231 
   2232  return nullptr;
   2233 }
   2234 
   2235 static bool MatchAttribute(Element* aElement, int32_t aNamespaceID,
   2236                           nsAtom* aAttrName, void* aData) {
   2237  MOZ_ASSERT(aElement, "Must have content node to work with!");
   2238  nsString* attrValue = static_cast<nsString*>(aData);
   2239  if (aNamespaceID != kNameSpaceID_Unknown &&
   2240      aNamespaceID != kNameSpaceID_Wildcard) {
   2241    return attrValue->EqualsLiteral("*")
   2242               ? aElement->HasAttr(aNamespaceID, aAttrName)
   2243               : aElement->AttrValueIs(aNamespaceID, aAttrName, *attrValue,
   2244                                       eCaseMatters);
   2245  }
   2246 
   2247  // Qualified name match. This takes more work.
   2248  uint32_t count = aElement->GetAttrCount();
   2249  for (uint32_t i = 0; i < count; ++i) {
   2250    const nsAttrName* name = aElement->GetAttrNameAt(i);
   2251    bool nameMatch;
   2252    if (name->IsAtom()) {
   2253      nameMatch = name->Atom() == aAttrName;
   2254    } else if (aNamespaceID == kNameSpaceID_Wildcard) {
   2255      nameMatch = name->NodeInfo()->Equals(aAttrName);
   2256    } else {
   2257      nameMatch = name->NodeInfo()->QualifiedNameEquals(aAttrName);
   2258    }
   2259 
   2260    if (nameMatch) {
   2261      return attrValue->EqualsLiteral("*") ||
   2262             aElement->AttrValueIs(name->NamespaceID(), name->LocalName(),
   2263                                   *attrValue, eCaseMatters);
   2264    }
   2265  }
   2266 
   2267  return false;
   2268 }
   2269 
   2270 already_AddRefed<nsIHTMLCollection> nsINode::GetElementsByAttribute(
   2271    const nsAString& aAttribute, const nsAString& aValue) {
   2272  RefPtr<nsAtom> attrAtom(NS_Atomize(aAttribute));
   2273  RefPtr<nsContentList> list = new nsContentList(
   2274      this, MatchAttribute, nsContentUtils::DestroyMatchString,
   2275      new nsString(aValue), true, attrAtom, kNameSpaceID_Unknown);
   2276 
   2277  return list.forget();
   2278 }
   2279 
   2280 already_AddRefed<nsIHTMLCollection> nsINode::GetElementsByAttributeNS(
   2281    const nsAString& aNamespaceURI, const nsAString& aAttribute,
   2282    const nsAString& aValue, ErrorResult& aRv) {
   2283  RefPtr<nsAtom> attrAtom(NS_Atomize(aAttribute));
   2284 
   2285  int32_t nameSpaceId = kNameSpaceID_Wildcard;
   2286  if (!aNamespaceURI.EqualsLiteral("*")) {
   2287    nsresult rv = nsNameSpaceManager::GetInstance()->RegisterNameSpace(
   2288        aNamespaceURI, nameSpaceId);
   2289    if (NS_FAILED(rv)) {
   2290      aRv.Throw(rv);
   2291      return nullptr;
   2292    }
   2293  }
   2294 
   2295  RefPtr<nsContentList> list = new nsContentList(
   2296      this, MatchAttribute, nsContentUtils::DestroyMatchString,
   2297      new nsString(aValue), true, attrAtom, nameSpaceId);
   2298  return list.forget();
   2299 }
   2300 
   2301 void nsINode::Prepend(const Sequence<OwningNodeOrString>& aNodes,
   2302                      ErrorResult& aRv) {
   2303  nsCOMPtr<Document> doc = OwnerDoc();
   2304  nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv);
   2305  if (aRv.Failed()) {
   2306    return;
   2307  }
   2308 
   2309  nsCOMPtr<nsIContent> refNode = mFirstChild;
   2310  InsertBefore(*node, refNode, aRv);
   2311 }
   2312 
   2313 void nsINode::Append(const Sequence<OwningNodeOrString>& aNodes,
   2314                     ErrorResult& aRv) {
   2315  nsCOMPtr<Document> doc = OwnerDoc();
   2316  nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv);
   2317  if (aRv.Failed()) {
   2318    return;
   2319  }
   2320 
   2321  AppendChild(*node, aRv);
   2322 }
   2323 
   2324 // https://dom.spec.whatwg.org/#dom-parentnode-replacechildren
   2325 void nsINode::ReplaceChildren(const Sequence<OwningNodeOrString>& aNodes,
   2326                              ErrorResult& aRv) {
   2327  nsCOMPtr<Document> doc = OwnerDoc();
   2328  nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv);
   2329  if (aRv.Failed()) {
   2330    return;
   2331  }
   2332  MOZ_ASSERT(node);
   2333  return ReplaceChildren(node, aRv);
   2334 }
   2335 
   2336 void nsINode::ReplaceChildren(nsINode* aNode, ErrorResult& aRv,
   2337                              MutationEffectOnScript aMutationEffectOnScript) {
   2338  if (aNode) {
   2339    EnsurePreInsertionValidity(*aNode, nullptr, aRv);
   2340    if (aRv.Failed()) {
   2341      return;
   2342    }
   2343  }
   2344  nsCOMPtr<nsINode> node = aNode;
   2345  const RefPtr<Document> doc = OwnerDoc();
   2346 
   2347  if (MOZ_UNLIKELY(MaybeNeedsToNotifyDevToolsOfNodeRemovalsInOwnerDoc())) {
   2348    NotifyDevToolsOfRemovalsOfChildren();
   2349    // FIXME: There is no guarantee that node->OwnerDoc() == OwnerDoc().
   2350    // Thus, we may not require to notify DevTools of any node removals
   2351    // in the same document, but the node or its children may be in different
   2352    // document and its removal may be observed by the DevTools.
   2353    if (node) {
   2354      if (node->NodeType() == DOCUMENT_FRAGMENT_NODE) {
   2355        node->NotifyDevToolsOfRemovalsOfChildren();
   2356      } else if (node->GetParentNode()) {
   2357        nsContentUtils::NotifyDevToolsOfNodeRemoval(*node);
   2358      }
   2359    }
   2360  }
   2361 
   2362  // Needed when used in combination with contenteditable (maybe)
   2363  mozAutoDocUpdate updateBatch(doc, true);
   2364 
   2365  nsAutoMutationBatch mb(this, true, true);
   2366 
   2367  // The code above explicitly dispatched DOMNodeRemoved events if needed.
   2368  nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
   2369 
   2370  // Replace all with node within this.
   2371  RemoveAllChildren(true);
   2372  mb.RemovalDone();
   2373 
   2374  if (aNode) {
   2375    AppendChildInternal(*aNode, aMutationEffectOnScript, aRv);
   2376    mb.NodesAdded();
   2377  }
   2378 }
   2379 
   2380 static bool IsDoctypeOrHasFollowingDoctype(nsINode* aNode) {
   2381  for (; aNode; aNode = aNode->GetNextSibling()) {
   2382    if (aNode->NodeType() == nsINode::DOCUMENT_TYPE_NODE) {
   2383      return true;
   2384    }
   2385  }
   2386 
   2387  return false;
   2388 }
   2389 
   2390 // https://dom.spec.whatwg.org/#dom-parentnode-movebefore
   2391 void nsINode::MoveBefore(nsINode& aNode, nsINode* aChild, ErrorResult& aRv) {
   2392  const auto ComputeReferenceChild = [&]() -> nsINode* {
   2393    return &aNode == aChild ? aNode.GetNextSibling() : aChild;
   2394  };
   2395  nsINode* referenceChild = ComputeReferenceChild();
   2396 
   2397  // Move algorithm
   2398  // https://dom.spec.whatwg.org/#move
   2399  nsINode& newParent = *this;
   2400  const auto EnsureValidMoveRequest = [&newParent](nsINode& aNode,
   2401                                                   nsINode* aReferenceChild,
   2402                                                   ErrorResult& aRv) -> void {
   2403    // Step 1.
   2404    GetRootNodeOptions options;
   2405    options.mComposed = true;
   2406    if (newParent.GetRootNode(options) != aNode.GetRootNode(options)) {
   2407      aRv.ThrowHierarchyRequestError("Different root node.");
   2408      return;
   2409    }
   2410 
   2411    // Step 2.
   2412    if (nsContentUtils::ContentIsHostIncludingDescendantOf(&newParent,
   2413                                                           &aNode)) {
   2414      aRv.ThrowHierarchyRequestError("Node is an ancestor of the new parent.");
   2415      return;
   2416    }
   2417 
   2418    // Step 3.
   2419    if (aReferenceChild && aReferenceChild->GetParentNode() != &newParent) {
   2420      aRv.ThrowNotFoundError("Wrong reference child.");
   2421      return;
   2422    }
   2423 
   2424    // Step 4.
   2425    if (!aNode.IsElement() && !aNode.IsCharacterData()) {
   2426      aRv.ThrowHierarchyRequestError("Wrong type of node.");
   2427      return;
   2428    }
   2429 
   2430    // Step 5.
   2431    if (aNode.IsText() && newParent.IsDocument()) {
   2432      aRv.ThrowHierarchyRequestError(
   2433          "Can't move a text node to be a child of a document.");
   2434      return;
   2435    }
   2436 
   2437    // Step 6.
   2438    if (newParent.IsDocument() && aNode.IsElement() &&
   2439        (newParent.AsDocument()->GetRootElement() ||
   2440         IsDoctypeOrHasFollowingDoctype(aReferenceChild))) {
   2441      aRv.ThrowHierarchyRequestError(
   2442          "Can't move an element to be a child of the document.");
   2443      return;
   2444    }
   2445  };
   2446  EnsureValidMoveRequest(aNode, referenceChild, aRv);
   2447  if (MOZ_UNLIKELY(aRv.Failed())) {
   2448    return;
   2449  }
   2450 
   2451  // Step 7.
   2452  nsINode* oldParent = aNode.GetParentNode();
   2453 
   2454  // Step 8.
   2455  MOZ_ASSERT(oldParent);
   2456 
   2457  // For consistency with ReplaceOrInsertBefore(), we should allow DevTools to
   2458  // break on the removal of aNode.
   2459  if (MOZ_UNLIKELY(
   2460          aNode.MaybeNeedsToNotifyDevToolsOfNodeRemovalsInOwnerDoc())) {
   2461    nsMutationGuard guard;
   2462    nsContentUtils::NotifyDevToolsOfNodeRemoval(aNode);
   2463    // If the user modifies the DOM tree, let's check same things again.
   2464    if (MOZ_UNLIKELY(guard.Mutated(0))) {
   2465      referenceChild = ComputeReferenceChild();
   2466      // Step 1-6.
   2467      EnsureValidMoveRequest(aNode, referenceChild, aRv);
   2468      if (aRv.Failed()) {
   2469        return;
   2470      }
   2471      // Step 7.
   2472      oldParent = aNode.GetParentNode();
   2473      // Step 8.
   2474      MOZ_ASSERT(oldParent);
   2475    }
   2476  }
   2477 
   2478  // Steps 9-12 happen implicitly in when triggering
   2479  // nsIMutationObserver notifications.
   2480  // Step 13, and UnbindFromTree runs step 14 and step 15 and step 16,
   2481  // and also Step 25..
   2482  mozAutoDocUpdate updateBatch(GetComposedDoc(), true);
   2483  {  // Scope for AutoSuppressNotifyingDevToolsOfNodeRemovals
   2484    // XXX Do we really need to suppress notifying DevTools of this node
   2485    // removal? If we stop suppressing that, we need to check whether
   2486    // `referenceChild` is still in `oldParent` after that because the user
   2487    // can change the DOM with Inspector or Console.
   2488    AutoSuppressNotifyingDevToolsOfNodeRemovals suppressNotifyingDevTools(
   2489        *OwnerDoc());
   2490    oldParent->RemoveChildNode(aNode.AsContent(), true, nullptr, &newParent);
   2491 
   2492    // Steps 17-24 and Step 26.
   2493    // FIXME: I think this InsertChildBefore() call can be moved outside the
   2494    // scope of AutoSuppressNotifyingDevToolsOfNodeRemovals.
   2495    InsertChildBefore(aNode.AsContent(),
   2496                      referenceChild ? referenceChild->AsContent() : nullptr,
   2497                      true, aRv, oldParent);
   2498  }
   2499 }
   2500 
   2501 void nsINode::RemoveChildNode(nsIContent* aKid, bool aNotify,
   2502                              const BatchRemovalState* aState,
   2503                              nsINode* aNewParent,
   2504                              MutationEffectOnScript aMutationEffectOnScript) {
   2505  // NOTE: This function must not trigger any calls to
   2506  // Document::GetRootElement() calls until *after* it has removed aKid from
   2507  // aChildArray. Any calls before then could potentially restore a stale
   2508  // value for our cached root element, per note in
   2509  // Document::RemoveChildNode().
   2510  MOZ_ASSERT(aKid && aKid->GetParentNode() == this, "Bogus aKid");
   2511  MOZ_ASSERT(!IsAttr());
   2512 
   2513  nsMutationGuard::DidMutate();
   2514  mozAutoDocUpdate updateBatch(GetComposedDoc(), aNotify);
   2515 
   2516  if (aNotify) {
   2517    ContentRemoveInfo info;
   2518    info.mBatchRemovalState = aState;
   2519    info.mNewParent = aNewParent;
   2520    info.mMutationEffectOnScript = aMutationEffectOnScript;
   2521    MutationObservers::NotifyContentWillBeRemoved(this, aKid, info);
   2522  }
   2523 
   2524  // Since aKid is use also after DisconnectChild, ensure it stays alive.
   2525  nsCOMPtr<nsIContent> kungfuDeathGrip = aKid;
   2526  DisconnectChild(aKid);
   2527 
   2528  // Invalidate cached array of child nodes
   2529  InvalidateChildNodes();
   2530  aKid->UnbindFromTree(aNewParent, aState);
   2531 }
   2532 
   2533 // When replacing, aRefChild is the content being replaced; when
   2534 // inserting it's the content before which we're inserting.  In the
   2535 // latter case it may be null.
   2536 //
   2537 // If aRv is a failure after this call, the insertion should not happen.
   2538 //
   2539 // This implements the parts of
   2540 // https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity and
   2541 // the checks in https://dom.spec.whatwg.org/#concept-node-replace that
   2542 // depend on the child nodes or come after steps that depend on the child nodes
   2543 // (steps 2-6 in both cases).
   2544 static void EnsureAllowedAsChild(nsINode* aNewChild, nsINode* aParent,
   2545                                 bool aIsReplace, nsINode* aRefChild,
   2546                                 ErrorResult& aRv) {
   2547  MOZ_ASSERT(aNewChild, "Must have new child");
   2548  MOZ_ASSERT_IF(aIsReplace, aRefChild);
   2549  MOZ_ASSERT(aParent);
   2550  MOZ_ASSERT(aParent->IsDocument() || aParent->IsDocumentFragment() ||
   2551                 aParent->IsElement(),
   2552             "Nodes that are not documents, document fragments or elements "
   2553             "can't be parents!");
   2554 
   2555  // Step 2.
   2556  // A common case is that aNewChild has no element kids, in which case
   2557  // aParent can't be a descendant of aNewChild unless they're
   2558  // actually equal to each other.  Fast-path that case, since aParent
   2559  // could be pretty deep in the DOM tree.
   2560  if (aNewChild == aParent ||
   2561      (((aNewChild->HasFlag(NODE_MAY_HAVE_ELEMENT_CHILDREN) &&
   2562         aNewChild->GetFirstChild()) ||
   2563        // HTML template elements and ShadowRoot hosts need
   2564        // to be checked to ensure that they are not inserted into
   2565        // the hosted content.
   2566        aNewChild->NodeInfo()->NameAtom() == nsGkAtoms::_template ||
   2567        (aNewChild->IsElement() && aNewChild->AsElement()->GetShadowRoot())) &&
   2568       nsContentUtils::ContentIsHostIncludingDescendantOf(aParent,
   2569                                                          aNewChild))) {
   2570    aRv.ThrowHierarchyRequestError(
   2571        "The new child is an ancestor of the parent");
   2572    return;
   2573  }
   2574 
   2575  // Step 3.
   2576  if (aRefChild && aRefChild->GetParentNode() != aParent) {
   2577    if (aIsReplace) {
   2578      if (aNewChild->GetParentNode() == aParent) {
   2579        aRv.ThrowNotFoundError(
   2580            "New child already has this parent and old child does not. Please "
   2581            "check the order of replaceChild's arguments.");
   2582      } else {
   2583        aRv.ThrowNotFoundError(
   2584            "Child to be replaced is not a child of this node");
   2585      }
   2586    } else {
   2587      aRv.ThrowNotFoundError(
   2588          "Child to insert before is not a child of this node");
   2589    }
   2590    return;
   2591  }
   2592 
   2593  // Step 4.
   2594  if (!aNewChild->IsContent()) {
   2595    aRv.ThrowHierarchyRequestError(nsPrintfCString(
   2596        "May not add %s as a child", NodeTypeAsString(aNewChild)));
   2597    return;
   2598  }
   2599 
   2600  // Steps 5 and 6 combined.
   2601  // The allowed child nodes differ for documents and elements
   2602  switch (aNewChild->NodeType()) {
   2603    case nsINode::COMMENT_NODE:
   2604    case nsINode::PROCESSING_INSTRUCTION_NODE:
   2605      // OK in both cases
   2606      return;
   2607    case nsINode::TEXT_NODE:
   2608    case nsINode::CDATA_SECTION_NODE:
   2609    case nsINode::ENTITY_REFERENCE_NODE:
   2610      // Allowed under Elements and DocumentFragments
   2611      if (aParent->NodeType() == nsINode::DOCUMENT_NODE) {
   2612        aRv.ThrowHierarchyRequestError(
   2613            nsPrintfCString("Cannot insert %s as a child of a Document",
   2614                            NodeTypeAsString(aNewChild)));
   2615      }
   2616      return;
   2617    case nsINode::ELEMENT_NODE: {
   2618      if (!aParent->IsDocument()) {
   2619        // Always ok to have elements under other elements or document fragments
   2620        return;
   2621      }
   2622 
   2623      Document* parentDocument = aParent->AsDocument();
   2624      Element* rootElement = parentDocument->GetRootElement();
   2625      if (rootElement) {
   2626        // Already have a documentElement, so this is only OK if we're
   2627        // replacing it.
   2628        if (!aIsReplace || rootElement != aRefChild) {
   2629          aRv.ThrowHierarchyRequestError(
   2630              "Cannot have more than one Element child of a Document");
   2631        }
   2632        return;
   2633      }
   2634 
   2635      // We don't have a documentElement yet.  Our one remaining constraint is
   2636      // that the documentElement must come after the doctype.
   2637      if (!aRefChild) {
   2638        // Appending is just fine.
   2639        return;
   2640      }
   2641 
   2642      nsIContent* docTypeContent = parentDocument->GetDoctype();
   2643      if (!docTypeContent) {
   2644        // It's all good.
   2645        return;
   2646      }
   2647 
   2648      // The docTypeContent is retrived from the child list of the Document
   2649      // node so that doctypeIndex is never Nothing.
   2650      const Maybe<uint32_t> doctypeIndex =
   2651          aParent->ComputeIndexOf(docTypeContent);
   2652      MOZ_ASSERT(doctypeIndex.isSome());
   2653      // If aRefChild is an NAC, its index can be Nothing.
   2654      const Maybe<uint32_t> insertIndex = aParent->ComputeIndexOf(aRefChild);
   2655 
   2656      // Now we're OK in the following two cases only:
   2657      // 1) We're replacing something that's not before the doctype
   2658      // 2) We're inserting before something that comes after the doctype
   2659      const bool ok = MOZ_LIKELY(insertIndex.isSome()) &&
   2660                      (aIsReplace ? *insertIndex >= *doctypeIndex
   2661                                  : *insertIndex > *doctypeIndex);
   2662      if (!ok) {
   2663        aRv.ThrowHierarchyRequestError(
   2664            "Cannot insert a root element before the doctype");
   2665      }
   2666      return;
   2667    }
   2668    case nsINode::DOCUMENT_TYPE_NODE: {
   2669      if (!aParent->IsDocument()) {
   2670        // doctypes only allowed under documents
   2671        aRv.ThrowHierarchyRequestError(
   2672            nsPrintfCString("Cannot insert a DocumentType as a child of %s",
   2673                            NodeTypeAsString(aParent)));
   2674        return;
   2675      }
   2676 
   2677      Document* parentDocument = aParent->AsDocument();
   2678      nsIContent* docTypeContent = parentDocument->GetDoctype();
   2679      if (docTypeContent) {
   2680        // Already have a doctype, so this is only OK if we're replacing it
   2681        if (!aIsReplace || docTypeContent != aRefChild) {
   2682          aRv.ThrowHierarchyRequestError(
   2683              "Cannot have more than one DocumentType child of a Document");
   2684        }
   2685        return;
   2686      }
   2687 
   2688      // We don't have a doctype yet.  Our one remaining constraint is
   2689      // that the doctype must come before the documentElement.
   2690      Element* rootElement = parentDocument->GetRootElement();
   2691      if (!rootElement) {
   2692        // It's all good
   2693        return;
   2694      }
   2695 
   2696      if (!aRefChild) {
   2697        // Trying to append a doctype, but have a documentElement
   2698        aRv.ThrowHierarchyRequestError(
   2699            "Cannot have a DocumentType node after the root element");
   2700        return;
   2701      }
   2702 
   2703      // rootElement is now in the child list of the Document node so that
   2704      // ComputeIndexOf must success to find it.
   2705      const Maybe<uint32_t> rootIndex = aParent->ComputeIndexOf(rootElement);
   2706      MOZ_ASSERT(rootIndex.isSome());
   2707      const Maybe<uint32_t> insertIndex = aParent->ComputeIndexOf(aRefChild);
   2708 
   2709      // Now we're OK if and only if insertIndex <= rootIndex.  Indeed, either
   2710      // we end up replacing aRefChild or we end up before it.  Either one is
   2711      // ok as long as aRefChild is not after rootElement.
   2712      if (MOZ_LIKELY(insertIndex.isSome()) && *insertIndex > *rootIndex) {
   2713        aRv.ThrowHierarchyRequestError(
   2714            "Cannot have a DocumentType node after the root element");
   2715      }
   2716      return;
   2717    }
   2718    case nsINode::DOCUMENT_FRAGMENT_NODE: {
   2719      // Note that for now we only allow nodes inside document fragments if
   2720      // they're allowed inside elements.  If we ever change this to allow
   2721      // doctype nodes in document fragments, we'll need to update this code.
   2722      // Also, there's a version of this code in ReplaceOrInsertBefore.  If you
   2723      // change this code, change that too.
   2724      if (!aParent->IsDocument()) {
   2725        // All good here
   2726        return;
   2727      }
   2728 
   2729      bool sawElement = false;
   2730      for (nsIContent* child = aNewChild->GetFirstChild(); child;
   2731           child = child->GetNextSibling()) {
   2732        if (child->IsElement()) {
   2733          if (sawElement) {
   2734            // Can't put two elements into a document
   2735            aRv.ThrowHierarchyRequestError(
   2736                "Cannot have more than one Element child of a Document");
   2737            return;
   2738          }
   2739          sawElement = true;
   2740        }
   2741        // If we can put this content at the right place, we might be ok;
   2742        // if not, we bail out.
   2743        EnsureAllowedAsChild(child, aParent, aIsReplace, aRefChild, aRv);
   2744        if (aRv.Failed()) {
   2745          return;
   2746        }
   2747      }
   2748 
   2749      // Everything in the fragment checked out ok, so we can stick it in here
   2750      return;
   2751    }
   2752    default:
   2753      /*
   2754       * aNewChild is of invalid type.
   2755       */
   2756      break;
   2757  }
   2758 
   2759  // XXXbz when can we reach this?
   2760  aRv.ThrowHierarchyRequestError(nsPrintfCString("Cannot insert %s inside %s",
   2761                                                 NodeTypeAsString(aNewChild),
   2762                                                 NodeTypeAsString(aParent)));
   2763 }
   2764 
   2765 // Implements
   2766 // https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity
   2767 void nsINode::EnsurePreInsertionValidity(nsINode& aNewChild, nsINode* aRefChild,
   2768                                         ErrorResult& aError) {
   2769  EnsurePreInsertionValidity1(aError);
   2770  if (aError.Failed()) {
   2771    return;
   2772  }
   2773  EnsurePreInsertionValidity2(false, aNewChild, aRefChild, aError);
   2774 }
   2775 
   2776 // Implements the parts of
   2777 // https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity and
   2778 // the checks in https://dom.spec.whatwg.org/#concept-node-replace that can be
   2779 // evaluated before ever looking at the child nodes (step 1 in both cases).
   2780 void nsINode::EnsurePreInsertionValidity1(ErrorResult& aError) {
   2781  if (!IsDocument() && !IsDocumentFragment() && !IsElement()) {
   2782    aError.ThrowHierarchyRequestError(
   2783        nsPrintfCString("Cannot add children to %s", NodeTypeAsString(this)));
   2784    return;
   2785  }
   2786 }
   2787 
   2788 void nsINode::EnsurePreInsertionValidity2(bool aReplace, nsINode& aNewChild,
   2789                                          nsINode* aRefChild,
   2790                                          ErrorResult& aError) {
   2791  if (aNewChild.IsRootOfNativeAnonymousSubtree()) {
   2792    // This is anonymous content.  Don't allow its insertion
   2793    // anywhere, since it might have UnbindFromTree calls coming
   2794    // its way.
   2795    aError.ThrowNotSupportedError(
   2796        "Inserting anonymous content manually is not supported");
   2797    return;
   2798  }
   2799 
   2800  // Make sure that the inserted node is allowed as a child of its new parent.
   2801  EnsureAllowedAsChild(&aNewChild, this, aReplace, aRefChild, aError);
   2802 }
   2803 
   2804 nsINode* nsINode::ReplaceOrInsertBefore(
   2805    bool aReplace, nsINode* aNewChild, nsINode* aRefChild,
   2806    MutationEffectOnScript aMutationEffectOnScript, ErrorResult& aError) {
   2807  // XXXbz I wish I could assert that nsContentUtils::IsSafeToRunScript() so we
   2808  // could rely on scriptblockers going out of scope to actually run XBL
   2809  // teardown, but various crud adds nodes under scriptblockers (e.g. native
   2810  // anonymous content).  The only good news is those insertions can't trigger
   2811  // the bad XBL cases.
   2812  MOZ_ASSERT_IF(aReplace, aRefChild);
   2813 
   2814  // Before firing DOMNodeRemoved events, make sure this is actually an insert
   2815  // we plan to do.
   2816  EnsurePreInsertionValidity1(aError);
   2817  if (aError.Failed()) {
   2818    return nullptr;
   2819  }
   2820 
   2821  EnsurePreInsertionValidity2(aReplace, *aNewChild, aRefChild, aError);
   2822  if (aError.Failed()) {
   2823    return nullptr;
   2824  }
   2825 
   2826  uint16_t nodeType = aNewChild->NodeType();
   2827 
   2828  // Before we do anything else, fire all DOMNodeRemoved mutation events
   2829  // We do this up front as to avoid having to deal with script running
   2830  // at random places further down.
   2831  // Scope firing mutation events so that we don't carry any state that
   2832  // might be stale
   2833  {
   2834    nsMutationGuard guard;
   2835 
   2836    // If we're replacing, fire for node-to-be-replaced.
   2837    // If aRefChild == aNewChild then we'll fire for it in check below
   2838    if (aReplace && aRefChild != aNewChild) {
   2839      nsContentUtils::NotifyDevToolsOfNodeRemoval(*aRefChild);
   2840    }
   2841 
   2842    // If the new node already has a parent, fire for removing from old
   2843    // parent
   2844    if (aNewChild->GetParentNode()) {
   2845      nsContentUtils::NotifyDevToolsOfNodeRemoval(*aNewChild);
   2846    }
   2847 
   2848    // If we're inserting a fragment, fire for all the children of the
   2849    // fragment
   2850    if (nodeType == DOCUMENT_FRAGMENT_NODE) {
   2851      static_cast<FragmentOrElement*>(aNewChild)
   2852          ->NotifyDevToolsOfRemovalsOfChildren();
   2853    }
   2854 
   2855    if (guard.Mutated(0)) {
   2856      // Re-check the parts of our pre-insertion validity that might depend on
   2857      // the tree shape.
   2858      EnsurePreInsertionValidity2(aReplace, *aNewChild, aRefChild, aError);
   2859      if (aError.Failed()) {
   2860        return nullptr;
   2861      }
   2862    }
   2863  }
   2864 
   2865  // Record the node to insert before, if any
   2866  nsIContent* nodeToInsertBefore;
   2867  if (aReplace) {
   2868    nodeToInsertBefore = aRefChild->GetNextSibling();
   2869  } else {
   2870    // Since aRefChild is our child, it must be an nsIContent object.
   2871    nodeToInsertBefore = aRefChild ? aRefChild->AsContent() : nullptr;
   2872  }
   2873  if (nodeToInsertBefore == aNewChild) {
   2874    // We're going to remove aNewChild from its parent, so use its next sibling
   2875    // as the node to insert before.
   2876    nodeToInsertBefore = nodeToInsertBefore->GetNextSibling();
   2877  }
   2878 
   2879  Maybe<AutoTArray<nsCOMPtr<nsIContent>, 50>> fragChildren;
   2880 
   2881  // Remove the new child from the old parent if one exists
   2882  nsIContent* newContent = aNewChild->AsContent();
   2883  nsCOMPtr<nsINode> oldParent = newContent->GetParentNode();
   2884  if (oldParent) {
   2885    // Hold a strong ref to nodeToInsertBefore across the removal of newContent
   2886    nsCOMPtr<nsINode> kungFuDeathGrip = nodeToInsertBefore;
   2887 
   2888    // Removing a child can run script, via XBL destructors.
   2889    nsMutationGuard guard;
   2890 
   2891    // Scope for the mutation batch and scriptblocker, so they go away
   2892    // while kungFuDeathGrip is still alive.
   2893    {
   2894      mozAutoDocUpdate batch(newContent->GetComposedDoc(), true);
   2895      nsAutoMutationBatch mb(oldParent, true, true);
   2896      // ScriptBlocker ensures previous and next stay alive.
   2897      nsIContent* previous = aNewChild->GetPreviousSibling();
   2898      nsIContent* next = aNewChild->GetNextSibling();
   2899      oldParent->RemoveChildNode(aNewChild->AsContent(), true, nullptr, nullptr,
   2900                                 aMutationEffectOnScript);
   2901      if (nsAutoMutationBatch::GetCurrentBatch() == &mb) {
   2902        mb.RemovalDone();
   2903        mb.SetPrevSibling(previous);
   2904        mb.SetNextSibling(next);
   2905      }
   2906    }
   2907 
   2908    // We expect one mutation (the removal) to have happened.
   2909    if (guard.Mutated(1)) {
   2910      // XBL destructors, yuck.
   2911 
   2912      // Verify that newContent has no parent.
   2913      if (newContent->GetParentNode()) {
   2914        aError.ThrowHierarchyRequestError(
   2915            "New child was inserted somewhere else");
   2916        return nullptr;
   2917      }
   2918 
   2919      // And verify that newContent is still allowed as our child.
   2920      if (aNewChild == aRefChild) {
   2921        // We've already removed aRefChild.  So even if we were doing a replace,
   2922        // now we're doing a simple insert before nodeToInsertBefore.
   2923        EnsureAllowedAsChild(newContent, this, false, nodeToInsertBefore,
   2924                             aError);
   2925        if (aError.Failed()) {
   2926          return nullptr;
   2927        }
   2928      } else {
   2929        EnsureAllowedAsChild(newContent, this, aReplace, aRefChild, aError);
   2930        if (aError.Failed()) {
   2931          return nullptr;
   2932        }
   2933 
   2934        // And recompute nodeToInsertBefore, just in case.
   2935        if (aReplace) {
   2936          nodeToInsertBefore = aRefChild->GetNextSibling();
   2937        } else {
   2938          nodeToInsertBefore = aRefChild ? aRefChild->AsContent() : nullptr;
   2939        }
   2940      }
   2941    }
   2942  } else if (nodeType == DOCUMENT_FRAGMENT_NODE) {
   2943    // Make sure to remove all the fragment's kids.  We need to do this before
   2944    // we start inserting anything, so we will run out XBL destructors and
   2945    // binding teardown (GOD, I HATE THESE THINGS) before we insert anything
   2946    // into the DOM.
   2947    uint32_t count = newContent->GetChildCount();
   2948 
   2949    fragChildren.emplace();
   2950 
   2951    // Copy the children into a separate array to avoid having to deal with
   2952    // mutations to the fragment later on here.
   2953    fragChildren->SetCapacity(count);
   2954    for (nsIContent* child = newContent->GetFirstChild(); child;
   2955         child = child->GetNextSibling()) {
   2956      NS_ASSERTION(!child->GetUncomposedDoc(),
   2957                   "How did we get a child with a current doc?");
   2958      fragChildren->AppendElement(child);
   2959    }
   2960 
   2961    // Hold a strong ref to nodeToInsertBefore across the removals
   2962    nsCOMPtr<nsINode> kungFuDeathGrip = nodeToInsertBefore;
   2963 
   2964    nsMutationGuard guard;
   2965 
   2966    // Scope for the mutation batch and scriptblocker, so they go away
   2967    // while kungFuDeathGrip is still alive.
   2968    {
   2969      mozAutoDocUpdate batch(newContent->GetComposedDoc(), true);
   2970      nsAutoMutationBatch mb(newContent, false, true);
   2971 
   2972      newContent->RemoveAllChildren<BatchRemovalOrder::BackToFront>(true);
   2973    }
   2974 
   2975    // We expect |count| removals
   2976    if (guard.Mutated(count)) {
   2977      // XBL destructors, yuck.
   2978 
   2979      // Verify that nodeToInsertBefore, if non-null, is still our child.  If
   2980      // it's not, there's no way we can do this insert sanely; just bail out.
   2981      if (nodeToInsertBefore && nodeToInsertBefore->GetParent() != this) {
   2982        aError.ThrowHierarchyRequestError("Don't know where to insert child");
   2983        return nullptr;
   2984      }
   2985 
   2986      // Verify that all the things in fragChildren have no parent.
   2987      for (uint32_t i = 0; i < count; ++i) {
   2988        if (fragChildren->ElementAt(i)->GetParentNode()) {
   2989          aError.ThrowHierarchyRequestError(
   2990              "New child was inserted somewhere else");
   2991          return nullptr;
   2992        }
   2993      }
   2994 
   2995      // Note that unlike the single-element case above, none of our kids can
   2996      // be aRefChild, so we can always pass through aReplace in the
   2997      // EnsureAllowedAsChild checks below and don't have to worry about whether
   2998      // recomputing nodeToInsertBefore is OK.
   2999 
   3000      // Verify that our aRefChild is still sensible
   3001      if (aRefChild && aRefChild->GetParent() != this) {
   3002        aError.ThrowHierarchyRequestError("Don't know where to insert child");
   3003        return nullptr;
   3004      }
   3005 
   3006      // Recompute nodeToInsertBefore, just in case.
   3007      if (aReplace) {
   3008        nodeToInsertBefore = aRefChild->GetNextSibling();
   3009      } else {
   3010        // If aRefChild has 'this' as a parent, it must be an nsIContent.
   3011        nodeToInsertBefore = aRefChild ? aRefChild->AsContent() : nullptr;
   3012      }
   3013 
   3014      // And verify that newContent is still allowed as our child.  Sadly, we
   3015      // need to reimplement the relevant part of EnsureAllowedAsChild() because
   3016      // now our nodes are in an array and all.  If you change this code,
   3017      // change the code there.
   3018      if (IsDocument()) {
   3019        bool sawElement = false;
   3020        for (uint32_t i = 0; i < count; ++i) {
   3021          nsIContent* child = fragChildren->ElementAt(i);
   3022          if (child->IsElement()) {
   3023            if (sawElement) {
   3024              // No good
   3025              aError.ThrowHierarchyRequestError(
   3026                  "Cannot have more than one Element child of a Document");
   3027              return nullptr;
   3028            }
   3029            sawElement = true;
   3030          }
   3031          EnsureAllowedAsChild(child, this, aReplace, aRefChild, aError);
   3032          if (aError.Failed()) {
   3033            return nullptr;
   3034          }
   3035        }
   3036      }
   3037    }
   3038  }
   3039 
   3040  mozAutoDocUpdate batch(GetComposedDoc(), true);
   3041  nsAutoMutationBatch mb;
   3042 
   3043  // If we're replacing and we haven't removed aRefChild yet, do so now
   3044  if (aReplace && aRefChild != aNewChild) {
   3045    mb.Init(this, true, true);
   3046 
   3047    // Since aRefChild is never null in the aReplace case, we know that at
   3048    // this point nodeToInsertBefore is the next sibling of aRefChild.
   3049    NS_ASSERTION(aRefChild->GetNextSibling() == nodeToInsertBefore,
   3050                 "Unexpected nodeToInsertBefore");
   3051 
   3052    nsIContent* toBeRemoved = nodeToInsertBefore
   3053                                  ? nodeToInsertBefore->GetPreviousSibling()
   3054                                  : GetLastChild();
   3055    MOZ_ASSERT(toBeRemoved);
   3056 
   3057    RemoveChildNode(toBeRemoved, true, nullptr, nullptr,
   3058                    aMutationEffectOnScript);
   3059  }
   3060 
   3061  // Move new child over to our document if needed. Do this after removing
   3062  // it from its parent so that AdoptNode doesn't fire DOMNodeRemoved
   3063  // DocumentType nodes are the only nodes that can have a null
   3064  // ownerDocument according to the DOM spec, and we need to allow
   3065  // inserting them w/o calling AdoptNode().
   3066  Document* doc = OwnerDoc();
   3067  if (doc != newContent->OwnerDoc() && nodeType != DOCUMENT_FRAGMENT_NODE) {
   3068    AdoptNodeIntoOwnerDoc(this, aNewChild, aError);
   3069    if (aError.Failed()) {
   3070      return nullptr;
   3071    }
   3072  }
   3073 
   3074  /*
   3075   * Check if we're inserting a document fragment. If we are, we need
   3076   * to actually add its children individually (i.e. we don't add the
   3077   * actual document fragment).
   3078   */
   3079  nsINode* result = aReplace ? aRefChild : aNewChild;
   3080  if (nodeType == DOCUMENT_FRAGMENT_NODE) {
   3081    nsAutoMutationBatch* mutationBatch = nsAutoMutationBatch::GetCurrentBatch();
   3082    if (mutationBatch && mutationBatch != &mb) {
   3083      mutationBatch = nullptr;
   3084    } else if (!aReplace) {
   3085      mb.Init(this, true, true);
   3086      mutationBatch = nsAutoMutationBatch::GetCurrentBatch();
   3087    }
   3088 
   3089    if (mutationBatch) {
   3090      mutationBatch->RemovalDone();
   3091      mutationBatch->SetPrevSibling(
   3092          nodeToInsertBefore ? nodeToInsertBefore->GetPreviousSibling()
   3093                             : GetLastChild());
   3094      mutationBatch->SetNextSibling(nodeToInsertBefore);
   3095    }
   3096 
   3097    uint32_t count = fragChildren->Length();
   3098    if (!count) {
   3099      return result;
   3100    }
   3101 
   3102    bool appending = !IsDocument() && !nodeToInsertBefore;
   3103    nsIContent* firstInsertedContent = fragChildren->ElementAt(0);
   3104 
   3105    // Iterate through the fragment's children, and insert them in the new
   3106    // parent
   3107    for (uint32_t i = 0; i < count; ++i) {
   3108      // XXXbz how come no reparenting here?  That seems odd...
   3109      // Insert the child.
   3110      InsertChildBefore(fragChildren->ElementAt(i), nodeToInsertBefore,
   3111                        !appending, aError);
   3112      if (aError.Failed()) {
   3113        // Make sure to notify on any children that we did succeed to insert
   3114        if (appending && i != 0) {
   3115          ContentAppendInfo info;
   3116          info.mMutationEffectOnScript = aMutationEffectOnScript;
   3117          MutationObservers::NotifyContentAppended(
   3118              static_cast<nsIContent*>(this), firstInsertedContent, info);
   3119        }
   3120        return nullptr;
   3121      }
   3122    }
   3123 
   3124    if (mutationBatch && !appending) {
   3125      mutationBatch->NodesAdded();
   3126    }
   3127 
   3128    // Notify and fire mutation events when appending
   3129    if (appending) {
   3130      ContentAppendInfo info;
   3131      info.mMutationEffectOnScript = aMutationEffectOnScript;
   3132      MutationObservers::NotifyContentAppended(static_cast<nsIContent*>(this),
   3133                                               firstInsertedContent, info);
   3134      if (mutationBatch) {
   3135        mutationBatch->NodesAdded();
   3136      }
   3137    }
   3138  } else {
   3139    // Not inserting a fragment but rather a single node.
   3140 
   3141    // FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=544654
   3142    //       We need to reparent here for nodes for which the parent of their
   3143    //       wrapper is not the wrapper for their ownerDocument (XUL elements,
   3144    //       form controls, ...). Also applies in the fragment code above.
   3145    if (nsAutoMutationBatch::GetCurrentBatch() == &mb) {
   3146      mb.RemovalDone();
   3147      mb.SetPrevSibling(nodeToInsertBefore
   3148                            ? nodeToInsertBefore->GetPreviousSibling()
   3149                            : GetLastChild());
   3150      mb.SetNextSibling(nodeToInsertBefore);
   3151    }
   3152    InsertChildBefore(newContent, nodeToInsertBefore, true, aError, nullptr,
   3153                      aMutationEffectOnScript);
   3154    if (aError.Failed()) {
   3155      return nullptr;
   3156    }
   3157  }
   3158 
   3159  return result;
   3160 }
   3161 
   3162 void nsINode::BindObject(nsISupports* aObject, UnbindCallback aDtor) {
   3163  Slots()->mBoundObjects.EmplaceBack(aObject, aDtor);
   3164 }
   3165 
   3166 void nsINode::UnbindObject(nsISupports* aObject) {
   3167  if (auto* slots = GetExistingSlots()) {
   3168    slots->mBoundObjects.RemoveElement(aObject);
   3169  }
   3170 }
   3171 
   3172 already_AddRefed<AccessibleNode> nsINode::GetAccessibleNode() {
   3173 #ifdef ACCESSIBILITY
   3174  nsresult rv = NS_OK;
   3175 
   3176  RefPtr<AccessibleNode> anode =
   3177      static_cast<AccessibleNode*>(GetProperty(nsGkAtoms::accessiblenode, &rv));
   3178  if (NS_FAILED(rv)) {
   3179    anode = new AccessibleNode(this);
   3180    RefPtr<AccessibleNode> temp = anode;
   3181    rv = SetProperty(nsGkAtoms::accessiblenode, temp.forget().take(),
   3182                     nsPropertyTable::SupportsDtorFunc, true);
   3183    if (NS_FAILED(rv)) {
   3184      NS_WARNING("SetProperty failed");
   3185      return nullptr;
   3186    }
   3187  }
   3188  return anode.forget();
   3189 #else
   3190  return nullptr;
   3191 #endif
   3192 }
   3193 
   3194 void nsINode::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
   3195                                     size_t* aNodeSize) const {
   3196  EventListenerManager* elm = GetExistingListenerManager();
   3197  if (elm) {
   3198    *aNodeSize += elm->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf);
   3199  }
   3200 
   3201  // Measurement of the following members may be added later if DMD finds it is
   3202  // worthwhile:
   3203  // - mNodeInfo
   3204  // - mSlots
   3205  //
   3206  // The following members are not measured:
   3207  // - mParent, mNextSibling, mPreviousOrLastSibling, mFirstChild: because
   3208  //   they're non-owning, from "exclusive ownership" point of view.
   3209 }
   3210 
   3211 void nsINode::AddSizeOfIncludingThis(nsWindowSizes& aSizes,
   3212                                     size_t* aNodeSize) const {
   3213  *aNodeSize += aSizes.mState.mMallocSizeOf(this);
   3214  AddSizeOfExcludingThis(aSizes, aNodeSize);
   3215 }
   3216 
   3217 bool nsINode::Contains(const nsINode* aOther) const {
   3218  if (aOther == this) {
   3219    return true;
   3220  }
   3221 
   3222  if (!aOther || OwnerDoc() != aOther->OwnerDoc() ||
   3223      IsInUncomposedDoc() != aOther->IsInUncomposedDoc() ||
   3224      !aOther->IsContent() || !HasChildren()) {
   3225    return false;
   3226  }
   3227 
   3228  if (IsDocument()) {
   3229    // document.contains(aOther) returns true if aOther is in the document,
   3230    // but is not in any anonymous subtree.
   3231    // IsInUncomposedDoc() check is done already before this.
   3232    return !aOther->IsInNativeAnonymousSubtree();
   3233  }
   3234 
   3235  if (!IsElement() && !IsDocumentFragment()) {
   3236    return false;
   3237  }
   3238 
   3239  if (IsInShadowTree() != aOther->IsInShadowTree() ||
   3240      IsInNativeAnonymousSubtree() != aOther->IsInNativeAnonymousSubtree()) {
   3241    return false;
   3242  }
   3243 
   3244  if (IsInNativeAnonymousSubtree()) {
   3245    if (GetClosestNativeAnonymousSubtreeRoot() !=
   3246        aOther->GetClosestNativeAnonymousSubtreeRoot()) {
   3247      return false;
   3248    }
   3249  }
   3250 
   3251  if (IsInShadowTree()) {
   3252    ShadowRoot* otherRoot = aOther->GetContainingShadow();
   3253    if (IsShadowRoot()) {
   3254      return otherRoot == this;
   3255    }
   3256    if (otherRoot != GetContainingShadow()) {
   3257      return false;
   3258    }
   3259  }
   3260 
   3261  return aOther->IsInclusiveDescendantOf(this);
   3262 }
   3263 
   3264 uint32_t nsINode::Length() const {
   3265  switch (NodeType()) {
   3266    case DOCUMENT_TYPE_NODE:
   3267      return 0;
   3268 
   3269    case TEXT_NODE:
   3270    case CDATA_SECTION_NODE:
   3271    case PROCESSING_INSTRUCTION_NODE:
   3272    case COMMENT_NODE:
   3273      MOZ_ASSERT(IsContent());
   3274      return AsContent()->TextLength();
   3275 
   3276    default:
   3277      return GetChildCount();
   3278  }
   3279 }
   3280 
   3281 namespace {
   3282 class SelectorCacheKey {
   3283 public:
   3284  explicit SelectorCacheKey(const nsACString& aString) : mKey(aString) {
   3285    MOZ_COUNT_CTOR(SelectorCacheKey);
   3286  }
   3287 
   3288  nsCString mKey;
   3289  nsExpirationState mState;
   3290 
   3291  nsExpirationState* GetExpirationState() { return &mState; }
   3292 
   3293  MOZ_COUNTED_DTOR(SelectorCacheKey)
   3294 };
   3295 
   3296 class SelectorCache final : public nsExpirationTracker<SelectorCacheKey, 4> {
   3297 public:
   3298  using SelectorList = UniquePtr<StyleSelectorList>;
   3299  using Table = nsTHashMap<nsCStringHashKey, SelectorList>;
   3300 
   3301  SelectorCache()
   3302      : nsExpirationTracker<SelectorCacheKey, 4>(
   3303            1000, "SelectorCache"_ns, GetMainThreadSerialEventTarget()) {}
   3304 
   3305  void NotifyExpired(SelectorCacheKey* aSelector) final {
   3306    MOZ_ASSERT(NS_IsMainThread());
   3307    MOZ_ASSERT(aSelector);
   3308 
   3309    // There is no guarantee that this method won't be re-entered when selector
   3310    // matching is ongoing because "memory-pressure" could be notified
   3311    // immediately when OOM happens according to the design of
   3312    // nsExpirationTracker. The perfect solution is to delete the |aSelector|
   3313    // and its StyleSelectorList in mTable asynchronously. We remove these
   3314    // objects synchronously for now because NotifyExpired() will never be
   3315    // triggered by "memory-pressure" which is not implemented yet in the stage
   3316    // 2 of mozalloc_handle_oom(). Once these objects are removed
   3317    // asynchronously, we should update the warning added in
   3318    // mozalloc_handle_oom() as well.
   3319    RemoveObject(aSelector);
   3320    mTable.Remove(aSelector->mKey);
   3321    delete aSelector;
   3322  }
   3323 
   3324  // We do not call MarkUsed because it would just slow down lookups and
   3325  // because we're OK expiring things after a few seconds even if they're
   3326  // being used.  Returns whether we actually had an entry for aSelector.
   3327  //
   3328  // If we have an entry and the selector list returned has a null
   3329  // StyleSelectorList*, that indicates that aSelector has already been
   3330  // parsed and is not a syntactically valid selector.
   3331  template <typename F>
   3332  StyleSelectorList* GetListOrInsertFrom(const nsACString& aSelector,
   3333                                         F&& aFrom) {
   3334    MOZ_ASSERT(NS_IsMainThread());
   3335    return mTable.LookupOrInsertWith(aSelector, std::forward<F>(aFrom)).get();
   3336  }
   3337 
   3338  ~SelectorCache() { AgeAllGenerations(); }
   3339 
   3340 private:
   3341  Table mTable;
   3342 };
   3343 
   3344 SelectorCache& GetSelectorCache(bool aChromeRulesEnabled) {
   3345  static StaticAutoPtr<SelectorCache> sSelectorCache;
   3346  static StaticAutoPtr<SelectorCache> sChromeSelectorCache;
   3347  auto& cache = aChromeRulesEnabled ? sChromeSelectorCache : sSelectorCache;
   3348  if (!cache) {
   3349    cache = new SelectorCache();
   3350    ClearOnShutdown(&cache);
   3351  }
   3352  return *cache;
   3353 }
   3354 }  // namespace
   3355 
   3356 const StyleSelectorList* nsINode::ParseSelectorList(
   3357    const nsACString& aSelectorString, ErrorResult& aRv) {
   3358  Document* doc = OwnerDoc();
   3359  const bool chromeRulesEnabled = doc->ChromeRulesEnabled();
   3360 
   3361  SelectorCache& cache = GetSelectorCache(chromeRulesEnabled);
   3362  StyleSelectorList* list = cache.GetListOrInsertFrom(aSelectorString, [&] {
   3363    // Note that we want to cache even if null was returned, because we
   3364    // want to cache the "This is not a valid selector" result.
   3365    return WrapUnique(
   3366        Servo_SelectorList_Parse(&aSelectorString, chromeRulesEnabled));
   3367  });
   3368 
   3369  if (!list) {
   3370    // Invalid selector.
   3371    aRv.ThrowSyntaxError("'"_ns + aSelectorString +
   3372                         "' is not a valid selector"_ns);
   3373  }
   3374 
   3375  return list;
   3376 }
   3377 
   3378 // Given an id, find first element with that id under aRoot.
   3379 // If none found, return nullptr. aRoot must be in the document.
   3380 inline static Element* FindMatchingElementWithId(
   3381    const nsAString& aId, const Element& aRoot,
   3382    const DocumentOrShadowRoot& aContainingDocOrShadowRoot) {
   3383  MOZ_ASSERT(aRoot.SubtreeRoot() == &aContainingDocOrShadowRoot.AsNode());
   3384  MOZ_ASSERT(
   3385      aRoot.IsInUncomposedDoc() || aRoot.IsInShadowTree(),
   3386      "Don't call me if the root is not in the document or in a shadow tree");
   3387 
   3388  Span elements = aContainingDocOrShadowRoot.GetAllElementsForId(aId);
   3389 
   3390  // XXXbz: Should we fall back to the tree walk if |elements| is long,
   3391  // for some value of "long"?
   3392  for (Element* element : elements) {
   3393    if (MOZ_UNLIKELY(element == &aRoot)) {
   3394      continue;
   3395    }
   3396 
   3397    if (!element->IsInclusiveDescendantOf(&aRoot)) {
   3398      continue;
   3399    }
   3400 
   3401    // We have an element with the right id and it's a strict descendant
   3402    // of aRoot.
   3403    return element;
   3404  }
   3405 
   3406  return nullptr;
   3407 }
   3408 
   3409 Element* nsINode::QuerySelector(const nsACString& aSelector,
   3410                                ErrorResult& aResult) {
   3411  AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING_RELEVANT_FOR_JS(
   3412      "querySelector", LAYOUT_SelectorQuery, aSelector);
   3413 
   3414  const StyleSelectorList* list = ParseSelectorList(aSelector, aResult);
   3415  if (!list) {
   3416    return nullptr;
   3417  }
   3418  const bool useInvalidation = false;
   3419  return const_cast<Element*>(
   3420      Servo_SelectorList_QueryFirst(this, list, useInvalidation));
   3421 }
   3422 
   3423 already_AddRefed<nsINodeList> nsINode::QuerySelectorAll(
   3424    const nsACString& aSelector, ErrorResult& aResult) {
   3425  AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING_RELEVANT_FOR_JS(
   3426      "querySelectorAll", LAYOUT_SelectorQuery, aSelector);
   3427 
   3428  RefPtr<nsSimpleContentList> contentList = new nsSimpleContentList(this);
   3429  const StyleSelectorList* list = ParseSelectorList(aSelector, aResult);
   3430  if (!list) {
   3431    return contentList.forget();
   3432  }
   3433 
   3434  const bool useInvalidation = false;
   3435  Servo_SelectorList_QueryAll(this, list, contentList.get(), useInvalidation);
   3436  return contentList.forget();
   3437 }
   3438 
   3439 Element* nsINode::GetElementById(const nsAString& aId) {
   3440  MOZ_ASSERT(!IsShadowRoot(), "Should use the faster version");
   3441  MOZ_ASSERT(IsElement() || IsDocumentFragment(),
   3442             "Bogus this object for GetElementById call");
   3443  if (IsInUncomposedDoc()) {
   3444    MOZ_ASSERT(IsElement(), "Huh? A fragment in a document?");
   3445    return FindMatchingElementWithId(aId, *AsElement(), *OwnerDoc());
   3446  }
   3447 
   3448  if (ShadowRoot* containingShadow = AsContent()->GetContainingShadow()) {
   3449    MOZ_ASSERT(IsElement(), "Huh? A fragment in a ShadowRoot?");
   3450    return FindMatchingElementWithId(aId, *AsElement(), *containingShadow);
   3451  }
   3452 
   3453  for (nsIContent* kid = GetFirstChild(); kid; kid = kid->GetNextNode(this)) {
   3454    if (!kid->IsElement()) {
   3455      continue;
   3456    }
   3457    nsAtom* id = kid->AsElement()->GetID();
   3458    if (id && id->Equals(aId)) {
   3459      return kid->AsElement();
   3460    }
   3461  }
   3462  return nullptr;
   3463 }
   3464 
   3465 JSObject* nsINode::WrapObject(JSContext* aCx,
   3466                              JS::Handle<JSObject*> aGivenProto) {
   3467  // Make sure one of these is true
   3468  // (1) our owner document has a script handling object,
   3469  // (2) Our owner document has had a script handling object, or has been marked
   3470  //     to have had one,
   3471  // (3) we are running a privileged script.
   3472  // Event handling is possible only if (1). If (2) event handling is
   3473  // prevented.
   3474  // If the document has never had a script handling object, untrusted
   3475  // scripts (3) shouldn't touch it!
   3476  bool hasHadScriptHandlingObject = false;
   3477  if (!OwnerDoc()->GetScriptHandlingObject(hasHadScriptHandlingObject) &&
   3478      !hasHadScriptHandlingObject && !nsContentUtils::IsSystemCaller(aCx)) {
   3479    Throw(aCx, NS_ERROR_UNEXPECTED);
   3480    return nullptr;
   3481  }
   3482 
   3483  JS::Rooted<JSObject*> obj(aCx, WrapNode(aCx, aGivenProto));
   3484  if (obj && ChromeOnlyAccess()) {
   3485    MOZ_RELEASE_ASSERT(
   3486        xpc::IsUnprivilegedJunkScope(JS::GetNonCCWObjectGlobal(obj)) ||
   3487        xpc::IsInUAWidgetScope(obj) || xpc::AccessCheck::isChrome(obj));
   3488  }
   3489  return obj;
   3490 }
   3491 
   3492 already_AddRefed<nsINode> nsINode::CloneNode(bool aDeep, ErrorResult& aError) {
   3493  return Clone(aDeep, nullptr, aError);
   3494 }
   3495 
   3496 nsDOMAttributeMap* nsINode::GetAttributes() {
   3497  if (!IsElement()) {
   3498    return nullptr;
   3499  }
   3500  return AsElement()->Attributes();
   3501 }
   3502 
   3503 Element* nsINode::GetParentElementCrossingShadowRoot() const {
   3504  if (!mParent) {
   3505    return nullptr;
   3506  }
   3507 
   3508  if (mParent->IsElement()) {
   3509    return mParent->AsElement();
   3510  }
   3511 
   3512  if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(mParent)) {
   3513    MOZ_ASSERT(shadowRoot->GetHost(), "ShowRoots should always have a host");
   3514    return shadowRoot->GetHost();
   3515  }
   3516 
   3517  return nullptr;
   3518 }
   3519 
   3520 bool nsINode::HasBoxQuadsSupport(JSContext* aCx, JSObject* /* unused */) {
   3521  return xpc::AccessCheck::isChrome(js::GetContextCompartment(aCx)) ||
   3522         StaticPrefs::layout_css_getBoxQuads_enabled();
   3523 }
   3524 
   3525 nsINode* nsINode::GetScopeChainParent() const { return nullptr; }
   3526 
   3527 Element* nsINode::GetParentFlexElement() {
   3528  if (!IsContent()) {
   3529    return nullptr;
   3530  }
   3531 
   3532  nsIFrame* primaryFrame = AsContent()->GetPrimaryFrame(FlushType::Frames);
   3533 
   3534  // Walk up the parent chain and pierce through any anonymous boxes
   3535  // that might be between this frame and a possible flex parent.
   3536  for (nsIFrame* f = primaryFrame; f; f = f->GetParent()) {
   3537    if (f != primaryFrame && !f->Style()->IsAnonBox()) {
   3538      // We hit a non-anonymous ancestor before finding a flex item.
   3539      // Bail out.
   3540      break;
   3541    }
   3542    if (f->IsFlexItem()) {
   3543      return f->GetParent()->GetContent()->AsElement();
   3544    }
   3545  }
   3546 
   3547  return nullptr;
   3548 }
   3549 
   3550 Element* nsINode::GetNearestInclusiveOpenPopover() const {
   3551  for (auto* el : InclusiveFlatTreeAncestorsOfType<Element>()) {
   3552    if (el->IsPopoverOpenedInMode(PopoverAttributeState::Auto)) {
   3553      return el;
   3554    }
   3555  }
   3556  return nullptr;
   3557 }
   3558 
   3559 Element* nsINode::GetNearestInclusiveTargetPopoverForInvoker() const {
   3560  for (auto* el : InclusiveFlatTreeAncestorsOfType<Element>()) {
   3561    if (auto* popover = el->GetEffectiveCommandForElement()) {
   3562      if (popover->IsPopoverOpenedInMode(PopoverAttributeState::Auto)) {
   3563        return popover;
   3564      }
   3565    }
   3566    if (auto* popover = el->GetEffectivePopoverTargetElement()) {
   3567      if (popover->IsPopoverOpenedInMode(PopoverAttributeState::Auto)) {
   3568        return popover;
   3569      }
   3570    }
   3571  }
   3572  return nullptr;
   3573 }
   3574 
   3575 nsGenericHTMLElement* nsINode::GetEffectiveCommandForElement() const {
   3576  if (!StaticPrefs::dom_element_commandfor_enabled()) {
   3577    return nullptr;
   3578  }
   3579 
   3580  const auto* formControl =
   3581      nsGenericHTMLFormControlElementWithState::FromNode(this);
   3582  if (!formControl || formControl->IsDisabled() ||
   3583      !formControl->IsButtonControl()) {
   3584    return nullptr;
   3585  }
   3586 
   3587  if (const auto* buttonControl = HTMLButtonElement::FromNodeOrNull(this)) {
   3588    if (auto* popover = nsGenericHTMLElement::FromNodeOrNull(
   3589            buttonControl->GetCommandForElement())) {
   3590      if (popover->GetPopoverAttributeState() != PopoverAttributeState::None) {
   3591        return popover;
   3592      }
   3593    }
   3594  }
   3595  return nullptr;
   3596 }
   3597 
   3598 nsGenericHTMLElement* nsINode::GetEffectivePopoverTargetElement() const {
   3599  const auto* formControl =
   3600      nsGenericHTMLFormControlElementWithState::FromNode(this);
   3601  if (!formControl || formControl->IsDisabled() ||
   3602      !formControl->IsButtonControl()) {
   3603    return nullptr;
   3604  }
   3605  if (auto* popover = nsGenericHTMLElement::FromNodeOrNull(
   3606          formControl->GetPopoverTargetElement())) {
   3607    if (popover->GetPopoverAttributeState() != PopoverAttributeState::None) {
   3608      return popover;
   3609    }
   3610  }
   3611  return nullptr;
   3612 }
   3613 
   3614 Element* nsINode::GetTopmostClickedPopover() const {
   3615  Element* clickedPopover = GetNearestInclusiveOpenPopover();
   3616  Element* invokedPopover = GetNearestInclusiveTargetPopoverForInvoker();
   3617  if (!clickedPopover) {
   3618    return invokedPopover;
   3619  }
   3620  auto autoPopoverList =
   3621      clickedPopover->OwnerDoc()->PopoverListOf(PopoverAttributeState::Auto);
   3622  for (Element* el : Reversed(autoPopoverList)) {
   3623    if (el == clickedPopover || el == invokedPopover) {
   3624      return el;
   3625    }
   3626  }
   3627  return nullptr;
   3628 }
   3629 
   3630 // https://html.spec.whatwg.org/multipage/interactive-elements.html#nearest-clicked-dialog
   3631 HTMLDialogElement* nsINode::NearestClickedDialog(mozilla::WidgetEvent* aEvent) {
   3632  // 1. Let target be event's target.
   3633  // (Skipped - `this`).
   3634 
   3635  WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
   3636  if (!pointerEvent) {
   3637    return nullptr;
   3638  }
   3639 
   3640  // 2. If target is a dialog element, target has an open attribute, target's is
   3641  // modal is true...
   3642  RefPtr dialogElement = HTMLDialogElement::FromNode(this);
   3643  if (dialogElement && dialogElement->IsInTopLayer()) {
   3644    // ... , and event's clientX and clientY are outside the bounds of target,
   3645    // then return null.
   3646    auto* frame = dialogElement->GetPrimaryFrame();
   3647    if (!frame) {
   3648      return nullptr;
   3649    }
   3650    nsPoint point = nsLayoutUtils::GetEventCoordinatesRelativeTo(
   3651        aEvent, pointerEvent->mRefPoint, RelativeTo{frame});
   3652    nsRect frameRect = frame->GetRectRelativeToSelf();
   3653    if (!frameRect.Contains(point)) {
   3654      return nullptr;
   3655    }
   3656  }
   3657 
   3658  // 3. Let currentNode be target.
   3659  // 4. While currentNode is not null:
   3660  // 4.2 Set currentNode to currentNode's parent in the flat tree.
   3661  for (auto* currentNode :
   3662       InclusiveFlatTreeAncestorsOfType<HTMLDialogElement>()) {
   3663    // 4.1 If currentNode is a dialog element and currentNode has an open
   3664    // attribute, then return currentNode.
   3665    if (currentNode->Open()) {
   3666      return currentNode;
   3667    }
   3668  }
   3669 
   3670  // 5. Return null.
   3671  return nullptr;
   3672 }
   3673 
   3674 void nsINode::AddAnimationObserver(nsIAnimationObserver* aAnimationObserver) {
   3675  AddMutationObserver(aAnimationObserver);
   3676  OwnerDoc()->SetMayHaveAnimationObservers();
   3677 }
   3678 
   3679 void nsINode::AddAnimationObserverUnlessExists(
   3680    nsIAnimationObserver* aAnimationObserver) {
   3681  AddMutationObserverUnlessExists(aAnimationObserver);
   3682  OwnerDoc()->SetMayHaveAnimationObservers();
   3683 }
   3684 
   3685 already_AddRefed<nsINode> nsINode::CloneAndAdopt(
   3686    nsINode* aNode, bool aClone, bool aDeep,
   3687    nsNodeInfoManager* aNewNodeInfoManager,
   3688    JS::Handle<JSObject*> aReparentScope, nsINode* aParent,
   3689    ErrorResult& aError) {
   3690  MOZ_ASSERT((!aClone && aNewNodeInfoManager) || !aReparentScope,
   3691             "If cloning or not getting a new nodeinfo we shouldn't rewrap");
   3692  MOZ_ASSERT(!aParent || aNode->IsContent(),
   3693             "Can't insert document or attribute nodes into a parent");
   3694 
   3695  // First deal with aNode and walk its attributes (and their children). Then,
   3696  // if aDeep is true, deal with aNode's children (and recurse into their
   3697  // attributes and children).
   3698 
   3699  nsAutoScriptBlocker scriptBlocker;
   3700 
   3701  nsNodeInfoManager* nodeInfoManager = aNewNodeInfoManager;
   3702 
   3703  // aNode.
   3704  class NodeInfo* nodeInfo = aNode->mNodeInfo;
   3705  RefPtr<class NodeInfo> newNodeInfo;
   3706  if (nodeInfoManager) {
   3707    // Don't allow importing/adopting nodes from non-privileged "scriptable"
   3708    // documents to "non-scriptable" documents.
   3709    Document* newDoc = nodeInfoManager->GetDocument();
   3710    if (NS_WARN_IF(!newDoc)) {
   3711      aError.Throw(NS_ERROR_UNEXPECTED);
   3712      return nullptr;
   3713    }
   3714    bool hasHadScriptHandlingObject = false;
   3715    if (!newDoc->GetScriptHandlingObject(hasHadScriptHandlingObject) &&
   3716        !hasHadScriptHandlingObject) {
   3717      Document* currentDoc = aNode->OwnerDoc();
   3718      if (NS_WARN_IF(!nsContentUtils::IsChromeDoc(currentDoc) &&
   3719                     (currentDoc->GetScriptHandlingObject(
   3720                          hasHadScriptHandlingObject) ||
   3721                      hasHadScriptHandlingObject))) {
   3722        aError.Throw(NS_ERROR_UNEXPECTED);
   3723        return nullptr;
   3724      }
   3725    }
   3726 
   3727    newNodeInfo = nodeInfoManager->GetNodeInfo(
   3728        nodeInfo->NameAtom(), nodeInfo->GetPrefixAtom(),
   3729        nodeInfo->NamespaceID(), nodeInfo->NodeType(),
   3730        nodeInfo->GetExtraName());
   3731 
   3732    nodeInfo = newNodeInfo;
   3733  }
   3734 
   3735  Element* elem = Element::FromNode(aNode);
   3736 
   3737  nsCOMPtr<nsINode> clone;
   3738  if (aClone) {
   3739    nsresult rv = aNode->Clone(nodeInfo, getter_AddRefs(clone));
   3740    if (NS_WARN_IF(NS_FAILED(rv))) {
   3741      aError.Throw(rv);
   3742      return nullptr;
   3743    }
   3744 
   3745    if (aParent) {
   3746      // If we're cloning we need to insert the cloned children into the cloned
   3747      // parent.
   3748      aParent->AppendChildTo(static_cast<nsIContent*>(clone.get()),
   3749                             /* aNotify = */ true, aError);
   3750      if (NS_WARN_IF(aError.Failed())) {
   3751        return nullptr;
   3752      }
   3753    } else if (aDeep && clone->IsDocument()) {
   3754      // After cloning the document itself, we want to clone the children into
   3755      // the cloned document (somewhat like cloning and importing them into the
   3756      // cloned document).
   3757      nodeInfoManager = clone->mNodeInfo->NodeInfoManager();
   3758    }
   3759  } else if (nodeInfoManager) {
   3760    Document* oldDoc = aNode->OwnerDoc();
   3761 
   3762    DOMArena* domArenaToStore =
   3763        !aNode->HasFlag(NODE_KEEPS_DOMARENA)
   3764            ? aNode->NodeInfo()->NodeInfoManager()->GetArenaAllocator()
   3765            : nullptr;
   3766 
   3767    Document* newDoc = nodeInfoManager->GetDocument();
   3768    MOZ_ASSERT(newDoc);
   3769 
   3770    bool wasRegistered = false;
   3771    if (elem) {
   3772      wasRegistered = oldDoc->UnregisterActivityObserver(elem);
   3773    }
   3774 
   3775    const bool hadProperties = aNode->HasProperties();
   3776    if (hadProperties) {
   3777      // NOTE: We want this to happen before NodeInfoChanged so that
   3778      // NodeInfoChanged can use node properties normally.
   3779      //
   3780      // When this fails, it removes all properties for the node anyway, so no
   3781      // extra error handling needed.
   3782      (void)oldDoc->PropertyTable().TransferOrRemoveAllPropertiesFor(
   3783          aNode, newDoc->PropertyTable());
   3784    }
   3785 
   3786    aNode->mNodeInfo.swap(newNodeInfo);
   3787    aNode->NodeInfoChanged(oldDoc);
   3788 
   3789    MOZ_ASSERT(newDoc != oldDoc);
   3790    if (elem) {
   3791      // Adopted callback must be enqueued whenever a node’s
   3792      // shadow-including inclusive descendants that is custom.
   3793      CustomElementData* data = elem->GetCustomElementData();
   3794      if (data && data->mState == CustomElementData::State::eCustom) {
   3795        LifecycleCallbackArgs args;
   3796        args.mOldDocument = oldDoc;
   3797        args.mNewDocument = newDoc;
   3798 
   3799        nsContentUtils::EnqueueLifecycleCallback(ElementCallbackType::eAdopted,
   3800                                                 elem, args);
   3801      }
   3802    }
   3803 
   3804    // XXX what if oldDoc is null, we don't know if this should be
   3805    // registered or not! Can that really happen?
   3806    if (wasRegistered) {
   3807      newDoc->RegisterActivityObserver(aNode->AsElement());
   3808    }
   3809 
   3810    if (nsPIDOMWindowInner* window = newDoc->GetInnerWindow()) {
   3811      EventListenerManager* elm = aNode->GetExistingListenerManager();
   3812      if (elm) {
   3813        if (elm->MayHaveDOMActivateListeners()) {
   3814          window->SetHasDOMActivateEventListeners();
   3815        }
   3816        if (elm->MayHaveTouchEventListener()) {
   3817          window->SetHasTouchEventListeners();
   3818        }
   3819        if (elm->MayHaveMouseEnterLeaveEventListener()) {
   3820          window->SetHasMouseEnterLeaveEventListeners();
   3821        }
   3822        if (elm->MayHavePointerEnterLeaveEventListener()) {
   3823          window->SetHasPointerEnterLeaveEventListeners();
   3824        }
   3825        if (elm->MayHavePointerRawUpdateEventListener()) {
   3826          window->MaybeSetHasPointerRawUpdateEventListeners();
   3827        }
   3828        if (elm->MayHaveSelectionChangeEventListener()) {
   3829          window->SetHasSelectionChangeEventListeners();
   3830        }
   3831        if (elm->MayHaveFormSelectEventListener()) {
   3832          window->SetHasFormSelectEventListeners();
   3833        }
   3834        if (elm->MayHaveTransitionEventListener()) {
   3835          window->SetHasTransitionEventListeners();
   3836        }
   3837        if (elm->MayHaveSMILTimeEventListener()) {
   3838          window->SetHasSMILTimeEventListeners();
   3839        }
   3840      }
   3841    }
   3842    if (wasRegistered) {
   3843      nsIContent* content = aNode->AsContent();
   3844      if (auto* mediaElem = HTMLMediaElement::FromNodeOrNull(content)) {
   3845        mediaElem->NotifyOwnerDocumentActivityChanged();
   3846      }
   3847      // HTMLImageElement::FromNode is insufficient since we need this for
   3848      // <svg:image> as well.
   3849      nsCOMPtr<nsIImageLoadingContent> imageLoadingContent =
   3850          do_QueryInterface(aNode);
   3851      if (imageLoadingContent) {
   3852        auto* ilc =
   3853            static_cast<nsImageLoadingContent*>(imageLoadingContent.get());
   3854        ilc->NotifyOwnerDocumentActivityChanged();
   3855      }
   3856    }
   3857 
   3858    if (oldDoc->MayHaveDOMMutationObservers()) {
   3859      newDoc->SetMayHaveDOMMutationObservers();
   3860    }
   3861 
   3862    if (oldDoc->MayHaveAnimationObservers()) {
   3863      newDoc->SetMayHaveAnimationObservers();
   3864    }
   3865 
   3866    if (elem) {
   3867      elem->RecompileScriptEventListeners();
   3868    }
   3869 
   3870    if (aReparentScope) {
   3871      AutoJSContext cx;
   3872      JS::Rooted<JSObject*> wrapper(cx);
   3873      if ((wrapper = aNode->GetWrapper())) {
   3874        MOZ_ASSERT(IsDOMObject(wrapper));
   3875        JSAutoRealm ar(cx, wrapper);
   3876        UpdateReflectorGlobal(cx, wrapper, aError);
   3877        if (aError.Failed()) {
   3878          if (wasRegistered) {
   3879            newDoc->UnregisterActivityObserver(aNode->AsElement());
   3880          }
   3881          if (hadProperties) {
   3882            // NOTE: When it fails it removes all properties for the node
   3883            // anyway, so no extra error handling needed.
   3884            (void)newDoc->PropertyTable().TransferOrRemoveAllPropertiesFor(
   3885                aNode, oldDoc->PropertyTable());
   3886          }
   3887          aNode->mNodeInfo.swap(newNodeInfo);
   3888          aNode->NodeInfoChanged(newDoc);
   3889          if (wasRegistered) {
   3890            oldDoc->RegisterActivityObserver(aNode->AsElement());
   3891          }
   3892          return nullptr;
   3893        }
   3894      }
   3895    }
   3896 
   3897    // At this point, a new node is added to the document, and this
   3898    // node isn't allocated by the NodeInfoManager of this document,
   3899    // so we need to do this SetArenaAllocator logic to bypass
   3900    // the !HasChildren() check in NodeInfoManager::Allocate.
   3901    if (mozilla::StaticPrefs::dom_arena_allocator_enabled_AtStartup()) {
   3902      if (!newDoc->NodeInfoManager()->HasAllocated()) {
   3903        if (DocGroup* docGroup = newDoc->GetDocGroup()) {
   3904          newDoc->NodeInfoManager()->SetArenaAllocator(
   3905              docGroup->ArenaAllocator());
   3906        }
   3907      }
   3908 
   3909      if (domArenaToStore && newDoc->GetDocGroup() != oldDoc->GetDocGroup()) {
   3910        nsContentUtils::AddEntryToDOMArenaTable(aNode, domArenaToStore);
   3911      }
   3912    }
   3913  }
   3914 
   3915  if (aDeep && (!aClone || !aNode->IsAttr())) {
   3916    // aNode's children.
   3917    for (nsIContent* cloneChild = aNode->GetFirstChild(); cloneChild;
   3918         cloneChild = cloneChild->GetNextSibling()) {
   3919      nsCOMPtr<nsINode> child =
   3920          CloneAndAdopt(cloneChild, aClone, true, nodeInfoManager,
   3921                        aReparentScope, clone, aError);
   3922      if (NS_WARN_IF(aError.Failed())) {
   3923        return nullptr;
   3924      }
   3925    }
   3926  }
   3927 
   3928  if (aDeep && aNode->IsElement()) {
   3929    if (aClone) {
   3930      if (nodeInfo->GetDocument()->IsStaticDocument()) {
   3931        // Clone any animations to the node in the static document, including
   3932        // the current timing. They will need to be paused later after the new
   3933        // document's pres shell gets initialized.
   3934        //
   3935        // This needs to be done here rather than in Element::CopyInnerTo
   3936        // because the animations clone code relies on the target (that is,
   3937        // `clone`) being connected already.
   3938        clone->AsElement()->CloneAnimationsFrom(*aNode->AsElement());
   3939 
   3940        // Clone the Shadow DOM
   3941        ShadowRoot* originalShadowRoot = aNode->AsElement()->GetShadowRoot();
   3942        if (originalShadowRoot) {
   3943          RefPtr<ShadowRoot> newShadowRoot =
   3944              clone->AsElement()->AttachShadowWithoutNameChecks(
   3945                  originalShadowRoot->Mode());
   3946 
   3947          newShadowRoot->CloneInternalDataFrom(originalShadowRoot);
   3948          for (nsIContent* origChild = originalShadowRoot->GetFirstChild();
   3949               origChild; origChild = origChild->GetNextSibling()) {
   3950            nsCOMPtr<nsINode> child =
   3951                CloneAndAdopt(origChild, aClone, aDeep, nodeInfoManager,
   3952                              aReparentScope, newShadowRoot, aError);
   3953            if (NS_WARN_IF(aError.Failed())) {
   3954              return nullptr;
   3955            }
   3956          }
   3957        }
   3958      }
   3959    } else {
   3960      if (ShadowRoot* shadowRoot = aNode->AsElement()->GetShadowRoot()) {
   3961        nsCOMPtr<nsINode> child =
   3962            CloneAndAdopt(shadowRoot, aClone, aDeep, nodeInfoManager,
   3963                          aReparentScope, clone, aError);
   3964        if (NS_WARN_IF(aError.Failed())) {
   3965          return nullptr;
   3966        }
   3967      }
   3968    }
   3969  }
   3970 
   3971  if (aClone && aNode->IsElement() &&
   3972      !nodeInfo->GetDocument()->IsStaticDocument()) {
   3973    // Clone the Shadow DOM
   3974    ShadowRoot* originalShadowRoot = aNode->AsElement()->GetShadowRoot();
   3975    if (originalShadowRoot && originalShadowRoot->Clonable()) {
   3976      ShadowRootInit init;
   3977      init.mMode = originalShadowRoot->Mode();
   3978      init.mDelegatesFocus = originalShadowRoot->DelegatesFocus();
   3979      init.mSlotAssignment = originalShadowRoot->SlotAssignment();
   3980      init.mClonable = true;
   3981 
   3982      RefPtr<ShadowRoot> newShadowRoot =
   3983          clone->AsElement()->AttachShadow(init, aError);
   3984      if (NS_WARN_IF(aError.Failed())) {
   3985        return nullptr;
   3986      }
   3987      newShadowRoot->SetIsDeclarative(originalShadowRoot->IsDeclarative());
   3988      nsAtom* referenceTarget = originalShadowRoot->ReferenceTarget();
   3989      newShadowRoot->SetReferenceTarget(referenceTarget);
   3990 
   3991      for (nsIContent* origChild = originalShadowRoot->GetFirstChild();
   3992           origChild; origChild = origChild->GetNextSibling()) {
   3993        nsCOMPtr<nsINode> child =
   3994            CloneAndAdopt(origChild, aClone, true, nodeInfoManager,
   3995                          aReparentScope, newShadowRoot, aError);
   3996        if (NS_WARN_IF(aError.Failed())) {
   3997          return nullptr;
   3998        }
   3999      }
   4000    }
   4001  }
   4002 
   4003  // Cloning template element.
   4004  if (aDeep && aClone && aNode->IsTemplateElement()) {
   4005    DocumentFragment* origContent =
   4006        static_cast<HTMLTemplateElement*>(aNode)->Content();
   4007    DocumentFragment* cloneContent =
   4008        static_cast<HTMLTemplateElement*>(clone.get())->Content();
   4009 
   4010    // Clone the children into the clone's template content owner
   4011    // document's nodeinfo manager.
   4012    nsNodeInfoManager* ownerNodeInfoManager =
   4013        cloneContent->mNodeInfo->NodeInfoManager();
   4014 
   4015    for (nsIContent* cloneChild = origContent->GetFirstChild(); cloneChild;
   4016         cloneChild = cloneChild->GetNextSibling()) {
   4017      nsCOMPtr<nsINode> child =
   4018          CloneAndAdopt(cloneChild, aClone, aDeep, ownerNodeInfoManager,
   4019                        aReparentScope, cloneContent, aError);
   4020      if (NS_WARN_IF(aError.Failed())) {
   4021        return nullptr;
   4022      }
   4023    }
   4024  }
   4025 
   4026  return clone.forget();
   4027 }
   4028 
   4029 void nsINode::Adopt(nsNodeInfoManager* aNewNodeInfoManager,
   4030                    JS::Handle<JSObject*> aReparentScope,
   4031                    mozilla::ErrorResult& aError) {
   4032  if (aNewNodeInfoManager) {
   4033    Document* beforeAdoptDoc = OwnerDoc();
   4034    Document* afterAdoptDoc = aNewNodeInfoManager->GetDocument();
   4035 
   4036    MOZ_ASSERT(beforeAdoptDoc);
   4037    MOZ_ASSERT(afterAdoptDoc);
   4038    MOZ_ASSERT(beforeAdoptDoc != afterAdoptDoc);
   4039 
   4040    if (afterAdoptDoc->GetDocGroup() != beforeAdoptDoc->GetDocGroup()) {
   4041      // This is a temporary solution for Bug 1590526 to only limit
   4042      // the restriction to chrome level documents because web extensions
   4043      // rely on content to content node adoption.
   4044      if (nsContentUtils::IsChromeDoc(afterAdoptDoc) ||
   4045          nsContentUtils::IsChromeDoc(beforeAdoptDoc)) {
   4046        return aError.ThrowSecurityError(
   4047            "Adopting nodes across docgroups in chrome documents "
   4048            "is unsupported");
   4049      }
   4050    }
   4051  }
   4052 
   4053  // Just need to store the return value of CloneAndAdopt in a
   4054  // temporary nsCOMPtr to make sure we release it.
   4055  nsCOMPtr<nsINode> node = CloneAndAdopt(this, false, true, aNewNodeInfoManager,
   4056                                         aReparentScope, nullptr, aError);
   4057 
   4058  nsMutationGuard::DidMutate();
   4059 }
   4060 
   4061 already_AddRefed<nsINode> nsINode::Clone(bool aDeep,
   4062                                         nsNodeInfoManager* aNewNodeInfoManager,
   4063                                         ErrorResult& aError) {
   4064  return CloneAndAdopt(this, true, aDeep, aNewNodeInfoManager, nullptr, nullptr,
   4065                       aError);
   4066 }
   4067 
   4068 void nsINode::GenerateXPath(nsAString& aResult) {
   4069  XPathGenerator::Generate(this, aResult);
   4070 }
   4071 
   4072 bool nsINode::IsApzAware() const { return IsNodeApzAware(); }
   4073 
   4074 bool nsINode::IsNodeApzAwareInternal() const {
   4075  return EventTarget::IsApzAware();
   4076 }
   4077 
   4078 DocGroup* nsINode::GetDocGroup() const { return OwnerDoc()->GetDocGroup(); }
   4079 
   4080 nsINode* nsINode::GetFlattenedTreeParentNodeNonInline() const {
   4081  return GetFlattenedTreeParentNode();
   4082 }
   4083 
   4084 ParentObject nsINode::GetParentObject() const {
   4085  ParentObject p(OwnerDoc());
   4086  // Note that mReflectionScope is a no-op for chrome, and other places where we
   4087  // don't check this value.
   4088  if (IsInNativeAnonymousSubtree()) {
   4089    if (ShouldUseUAWidgetScope(this)) {
   4090      p.mReflectionScope = ReflectionScope::UAWidget;
   4091    } else {
   4092      MOZ_ASSERT(ShouldUseNACScope(this));
   4093      p.mReflectionScope = ReflectionScope::NAC;
   4094    }
   4095  } else {
   4096    MOZ_ASSERT(!ShouldUseNACScope(this));
   4097    MOZ_ASSERT(!ShouldUseUAWidgetScope(this));
   4098  }
   4099  return p;
   4100 }
   4101 
   4102 void nsINode::AddMutationObserver(
   4103    nsMultiMutationObserver* aMultiMutationObserver) {
   4104  if (aMultiMutationObserver) {
   4105    NS_ASSERTION(!aMultiMutationObserver->ContainsNode(this),
   4106                 "Observer already in the list");
   4107    aMultiMutationObserver->AddMutationObserverToNode(this);
   4108  }
   4109 }
   4110 
   4111 void nsINode::AddMutationObserverUnlessExists(
   4112    nsMultiMutationObserver* aMultiMutationObserver) {
   4113  if (aMultiMutationObserver && !aMultiMutationObserver->ContainsNode(this)) {
   4114    aMultiMutationObserver->AddMutationObserverToNode(this);
   4115  }
   4116 }
   4117 
   4118 void nsINode::RemoveMutationObserver(
   4119    nsMultiMutationObserver* aMultiMutationObserver) {
   4120  if (aMultiMutationObserver) {
   4121    aMultiMutationObserver->RemoveMutationObserverFromNode(this);
   4122  }
   4123 }
   4124 
   4125 bool nsINode::MaybeNeedsToNotifyDevToolsOfNodeRemovalsInOwnerDoc() const {
   4126  // XXX Should we check SuppressedNotifyingDevToolsOfNodeRemovals() here too?
   4127  // Then, we could skip to handle some node removals while we're handling some
   4128  // APIs.
   4129  return OwnerDoc()->DevToolsWatchingDOMMutations();
   4130 }
   4131 
   4132 bool nsINode::DevToolsShouldBeNotifiedOfThisRemoval() const {
   4133  return MOZ_UNLIKELY(MaybeNeedsToNotifyDevToolsOfNodeRemovalsInOwnerDoc()) &&
   4134         IsInComposedDoc() &&
   4135         !OwnerDoc()->SuppressedNotifyingDevToolsOfNodeRemovals() &&
   4136         !ChromeOnlyAccess();
   4137 }
   4138 
   4139 void nsINode::NotifyDevToolsOfRemovalsOfChildren() {
   4140  // Optimize the common case
   4141  if (MOZ_LIKELY(!MaybeNeedsToNotifyDevToolsOfNodeRemovalsInOwnerDoc())) {
   4142    return;
   4143  }
   4144 
   4145  for (nsCOMPtr<nsIContent> child = GetFirstChild();
   4146       child && child->GetParentNode() == this;
   4147       child = child->GetNextSibling()) {
   4148    nsContentUtils::NotifyDevToolsOfNodeRemoval(*child);
   4149  }
   4150 }
   4151 
   4152 ShadowRoot* nsINode::GetShadowRootForSelection() const {
   4153  if (!StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
   4154    return nullptr;
   4155  }
   4156 
   4157  ShadowRoot* shadowRoot = GetShadowRoot();
   4158  if (!shadowRoot) {
   4159    return nullptr;
   4160  }
   4161 
   4162  // ie. <details> and <video>
   4163  if (shadowRoot->IsUAWidget()) {
   4164    return nullptr;
   4165  }
   4166 
   4167  // ie. <use> element
   4168  if (IsElement() && !AsElement()->CanAttachShadowDOM()) {
   4169    return nullptr;
   4170  }
   4171 
   4172  return shadowRoot;
   4173 }
   4174 
   4175 void nsINode::QueueAncestorRevealingAlgorithm() {
   4176  NS_DispatchToMainThread(NS_NewRunnableFunction(
   4177      "RevealAncestors",
   4178      [self = RefPtr{this}]() MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA {
   4179        self->AncestorRevealingAlgorithm(IgnoreErrors());
   4180      }));
   4181 }
   4182 
   4183 enum class RevealType : uint8_t {
   4184  UntilFound,
   4185  Details,
   4186 };
   4187 // https://html.spec.whatwg.org/#ancestor-revealing-algorithm
   4188 void nsINode::AncestorRevealingAlgorithm(ErrorResult& aRv) {
   4189  // 1. Let ancestorsToReveal be an empty list.
   4190  AutoTArray<std::pair<RefPtr<nsINode>, RevealType>, 16> ancestorsToReveal;
   4191  // 2. Let ancestor be target.
   4192  // 3. While ancestor has a parent node within the flat tree:
   4193  for (nsINode* ancestor : InclusiveFlatTreeAncestors(*this)) {
   4194    // 3.1 If ancestor has a hidden attribute in the hidden until found state,
   4195    //     then append (ancestor, "until-found") to ancestorsToReveal.
   4196    if (Element* currentAsElement = Element::FromNode(ancestor);
   4197        currentAsElement &&
   4198        currentAsElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden,
   4199                                      nsGkAtoms::untilFound, eIgnoreCase)) {
   4200      ancestorsToReveal.AppendElement(
   4201          std::make_pair(ancestor, RevealType::UntilFound));
   4202    }
   4203 
   4204    // 3.2 If ancestor is slotted into the second slot of a details element
   4205    //     which does not have an open attribute, then append (ancestor's
   4206    //     parent node, "details") to ancestorsToReveal.
   4207    if (HTMLSlotElement* slot = HTMLSlotElement::FromNode(ancestor)) {
   4208      // Note: There are two slots in the details element. Gecko names the
   4209      //       summary, and leaves the content slot unnamed.
   4210      if (HTMLDetailsElement* details = HTMLDetailsElement::FromNodeOrNull(
   4211              slot->GetContainingShadowHost());
   4212          details && !details->Open() && !slot->HasName()) {
   4213        ancestorsToReveal.AppendElement(
   4214            std::make_pair(details, RevealType::Details));
   4215      }
   4216    }
   4217 
   4218    // 3.3 Set ancestor to ancestor's parent node within the flat tree.
   4219  }
   4220 
   4221  // 4. For each (ancestor, type) in ancestorsToReveal:
   4222  for (const auto& [ancestor, revealType] : ancestorsToReveal) {
   4223    // 4.1 If ancestorToReveal is not connected, then return.
   4224    if (!ancestor->IsInComposedDoc()) {
   4225      return;
   4226    }
   4227 
   4228    // 4.2 If type is "until-found", then:
   4229    if (revealType == RevealType::UntilFound) {
   4230      // 4.2.1 If ancestorToReveal's hidden attribute is not in the Hidden Until
   4231      //       Found state, then return.
   4232      RefPtr ancestorAsElement = Element::FromNode(ancestor);
   4233      if (!ancestorAsElement ||
   4234          !ancestorAsElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden,
   4235                                          nsGkAtoms::untilFound, eIgnoreCase)) {
   4236        return;
   4237      }
   4238      // 4.2.2 Fire an event named beforematch at ancestorToReveal with the
   4239      //       bubbles attribute initialized to true.
   4240      ancestorAsElement->FireBeforematchEvent(aRv);
   4241      if (MOZ_UNLIKELY(aRv.Failed())) {
   4242        return;
   4243      }
   4244      // 4.2.3 If ancestorToReveal is not connected, then return.
   4245      if (!ancestor->IsInComposedDoc()) {
   4246        return;
   4247      }
   4248      // 4.2.4 Remove the hidden attribute from ancestorToReveal.
   4249      ancestorAsElement->UnsetAttr(kNameSpaceID_None, nsGkAtoms::hidden,
   4250                                   /*aNotify=*/true);
   4251    } else {  // 4.3 Otherwise
   4252      // 4.3.1 Assert: revealType is "details".
   4253      MOZ_ASSERT(revealType == RevealType::Details);
   4254      // 4.3.2 If ancestorToReveal has an open attribute, then return.
   4255      RefPtr details = HTMLDetailsElement::FromNode(ancestor);
   4256      MOZ_ASSERT(details);
   4257      if (details->Open()) {
   4258        return;
   4259      }
   4260      // 4.3.3 Set the open attribute on ancestorToReveal to the empty string.
   4261      details->SetOpen(true, aRv);
   4262      if (MOZ_UNLIKELY(aRv.Failed())) {
   4263        return;
   4264      }
   4265    }
   4266  }
   4267 }
   4268 
   4269 NS_IMPL_ISUPPORTS(nsNodeWeakReference, nsIWeakReference)
   4270 
   4271 nsNodeWeakReference::nsNodeWeakReference(nsINode* aNode)
   4272    : nsIWeakReference(aNode) {}
   4273 
   4274 nsNodeWeakReference::~nsNodeWeakReference() {
   4275  nsINode* node = static_cast<nsINode*>(mObject);
   4276 
   4277  if (node) {
   4278    NS_ASSERTION(node->Slots()->mWeakReference == this,
   4279                 "Weak reference has wrong value");
   4280    node->Slots()->mWeakReference = nullptr;
   4281  }
   4282 }
   4283 
   4284 NS_IMETHODIMP
   4285 nsNodeWeakReference::QueryReferentFromScript(const nsIID& aIID,
   4286                                             void** aInstancePtr) {
   4287  return QueryReferent(aIID, aInstancePtr);
   4288 }
   4289 
   4290 size_t nsNodeWeakReference::SizeOfOnlyThis(
   4291    mozilla::MallocSizeOf aMallocSizeOf) {
   4292  return aMallocSizeOf(this);
   4293 }