tor-browser

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

FragmentOrElement.cpp (68935B)


      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 element classes and DocumentFragment.
      9 */
     10 
     11 #include "mozilla/dom/FragmentOrElement.h"
     12 
     13 #include "DOMIntersectionObserver.h"
     14 #include "mozilla/AsyncEventDispatcher.h"
     15 #include "mozilla/DeclarationBlock.h"
     16 #include "mozilla/EffectSet.h"
     17 #include "mozilla/ElementAnimationData.h"
     18 #include "mozilla/EventDispatcher.h"
     19 #include "mozilla/EventListenerManager.h"
     20 #include "mozilla/HTMLEditor.h"
     21 #include "mozilla/Likely.h"
     22 #include "mozilla/MemoryReporting.h"
     23 #include "mozilla/MouseEvents.h"
     24 #include "mozilla/PresShell.h"
     25 #include "mozilla/RestyleManager.h"
     26 #include "mozilla/StaticPtr.h"
     27 #include "mozilla/TextEditor.h"
     28 #include "mozilla/TouchEvents.h"
     29 #include "mozilla/URLExtraData.h"
     30 #include "mozilla/dom/AncestorIterator.h"
     31 #include "mozilla/dom/Attr.h"
     32 #include "mozilla/dom/CharacterDataBuffer.h"
     33 #include "mozilla/dom/CloseWatcher.h"
     34 #include "mozilla/dom/CustomElementRegistry.h"
     35 #include "mozilla/dom/Document.h"
     36 #include "mozilla/dom/DocumentInlines.h"
     37 #include "mozilla/dom/Event.h"
     38 #include "mozilla/dom/NodeInfo.h"
     39 #include "mozilla/dom/RadioGroupContainer.h"
     40 #include "mozilla/dom/ScriptLoader.h"
     41 #include "mozilla/dom/StylePropertyMap.h"
     42 #include "mozilla/dom/StylePropertyMapReadOnly.h"
     43 #include "mozilla/dom/UnbindContext.h"
     44 #include "mozilla/mozInlineSpellChecker.h"
     45 #include "nsAtom.h"
     46 #include "nsContentList.h"
     47 #include "nsDOMAttributeMap.h"
     48 #include "nsDOMCSSAttrDeclaration.h"
     49 #include "nsDOMTokenList.h"
     50 #include "nsError.h"
     51 #include "nsFocusManager.h"
     52 #include "nsIAnonymousContentCreator.h"
     53 #include "nsIControllers.h"
     54 #include "nsIDocumentEncoder.h"
     55 #include "nsIFrame.h"
     56 #include "nsNameSpaceManager.h"
     57 #include "nsNetUtil.h"
     58 #include "nsPresContext.h"
     59 #include "nsString.h"
     60 #include "nsXULElement.h"
     61 #ifdef DEBUG
     62 #  include "nsRange.h"
     63 #endif
     64 
     65 #include "ChildIterator.h"
     66 #include "NodeUbiReporting.h"
     67 #include "mozAutoDocUpdate.h"
     68 #include "mozilla/BloomFilter.h"
     69 #include "mozilla/Sprintf.h"
     70 #include "mozilla/dom/HTMLSlotElement.h"
     71 #include "mozilla/dom/HTMLTemplateElement.h"
     72 #include "mozilla/dom/MutationObservers.h"
     73 #include "mozilla/dom/NodeListBinding.h"
     74 #include "mozilla/dom/SVGUseElement.h"
     75 #include "mozilla/dom/ShadowRoot.h"
     76 #include "mozilla/htmlaccel/htmlaccelEnabled.h"
     77 #ifdef MOZ_MAY_HAVE_HTMLACCEL
     78 #  include "mozilla/htmlaccel/htmlaccelNotInline.h"
     79 #endif
     80 #include "nsCCUncollectableMarker.h"
     81 #include "nsChildContentList.h"
     82 #include "nsContentCreatorFunctions.h"
     83 #include "nsContentUtils.h"
     84 #include "nsCycleCollector.h"
     85 #include "nsDOMMutationObserver.h"
     86 #include "nsFrameLoader.h"
     87 #include "nsGenericHTMLElement.h"
     88 #include "nsGkAtoms.h"
     89 #include "nsIWidget.h"
     90 #include "nsLayoutUtils.h"
     91 #include "nsNodeInfoManager.h"
     92 #include "nsPIDOMWindow.h"
     93 #include "nsWindowSizes.h"
     94 #include "nsWrapperCacheInlines.h"
     95 #include "xpcpublic.h"
     96 
     97 #ifdef ACCESSIBILITY
     98 #  include "nsAccessibilityService.h"
     99 #endif
    100 
    101 using namespace mozilla;
    102 using namespace mozilla::dom;
    103 
    104 uint64_t nsMutationGuard::sGeneration = 0;
    105 
    106 NS_IMPL_CYCLE_COLLECTION_CLASS(nsIContent)
    107 
    108 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsIContent)
    109  MOZ_ASSERT_UNREACHABLE("Our subclasses don't call us");
    110 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    111 
    112 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsIContent)
    113  MOZ_ASSERT_UNREACHABLE("Our subclasses don't call us");
    114 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    115 
    116 NS_INTERFACE_MAP_BEGIN(nsIContent)
    117  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    118  // Don't bother to QI to cycle collection, because our CC impl is
    119  // not doing anything anyway.
    120  NS_INTERFACE_MAP_ENTRY(nsIContent)
    121  NS_INTERFACE_MAP_ENTRY(nsINode)
    122  NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
    123  NS_INTERFACE_MAP_ENTRY_TEAROFF(nsISupportsWeakReference,
    124                                 new nsNodeSupportsWeakRefTearoff(this))
    125  // DOM bindings depend on the identity pointer being the
    126  // same as nsINode (which nsIContent inherits).
    127  NS_INTERFACE_MAP_ENTRY(nsISupports)
    128 NS_INTERFACE_MAP_END
    129 
    130 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsIContent)
    131 
    132 NS_IMPL_DOMARENA_DESTROY(nsIContent)
    133 
    134 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE_AND_DESTROY(nsIContent,
    135                                                               LastRelease(),
    136                                                               Destroy())
    137 
    138 nsIContent* nsIContent::FindFirstNonChromeOnlyAccessContent() const {
    139  // This handles also nested native anonymous content.
    140  // Oops, this function signature allows casting const to non-const.  (Then
    141  // again, so does GetFirstChild()->GetParent().)
    142  for (nsIContent* content = const_cast<nsIContent*>(this); content;
    143       content = content->GetClosestNativeAnonymousSubtreeRootParentOrHost()) {
    144    if (!content->ChromeOnlyAccess()) {
    145      return content;
    146    }
    147  }
    148  return nullptr;
    149 }
    150 
    151 void nsIContent::UnbindFromTree(nsINode* aNewParent,
    152                                const BatchRemovalState* aBatchState) {
    153  UnbindContext context(*this, aBatchState);
    154  context.SetIsMove(aNewParent != nullptr);
    155  UnbindFromTree(context);
    156 }
    157 
    158 // https://dom.spec.whatwg.org/#dom-slotable-assignedslot
    159 HTMLSlotElement* nsIContent::GetAssignedSlotByMode() const {
    160  /**
    161   * Get slotable's assigned slot for the result of
    162   * find a slot with open flag UNSET [1].
    163   *
    164   * [1] https://dom.spec.whatwg.org/#assign-a-slot
    165   */
    166  HTMLSlotElement* slot = GetAssignedSlot();
    167  if (!slot) {
    168    return nullptr;
    169  }
    170 
    171  MOZ_ASSERT(GetParent());
    172  MOZ_ASSERT(GetParent()->GetShadowRoot());
    173 
    174  /**
    175   * Additional check for open flag SET:
    176   *   If slotable’s parent’s shadow root's mode is not "open",
    177   *   then return null.
    178   */
    179  if (GetParent()->GetShadowRoot()->IsClosed()) {
    180    return nullptr;
    181  }
    182 
    183  return slot;
    184 }
    185 
    186 nsIContent::IMEState nsIContent::GetDesiredIMEState() {
    187  if (!IsEditable() || !IsInComposedDoc()) {
    188    // Check for the special case where we're dealing with elements which don't
    189    // have the editable flag set, but are readwrite (such as text controls).
    190    if (!IsElement() ||
    191        !AsElement()->State().HasState(ElementState::READWRITE)) {
    192      return IMEState(IMEEnabled::Disabled);
    193    }
    194  }
    195  // NOTE: The content for independent editors (e.g., input[type=text],
    196  // textarea) must override this method, so, we don't need to worry about
    197  // that here.
    198  nsIContent* editableAncestor = GetEditingHost();
    199 
    200  // This is in another editable content, use the result of it.
    201  if (editableAncestor && editableAncestor != this) {
    202    return editableAncestor->GetDesiredIMEState();
    203  }
    204  Document* doc = GetComposedDoc();
    205  if (!doc) {
    206    return IMEState(IMEEnabled::Disabled);
    207  }
    208  nsPresContext* pc = doc->GetPresContext();
    209  if (!pc) {
    210    return IMEState(IMEEnabled::Disabled);
    211  }
    212  HTMLEditor* htmlEditor = nsContentUtils::GetHTMLEditor(pc);
    213  if (!htmlEditor) {
    214    return IMEState(IMEEnabled::Disabled);
    215  }
    216  // FYI: HTMLEditor::GetPreferredIMEState() is infallible.
    217  return htmlEditor->GetPreferredIMEState().unwrap();
    218 }
    219 
    220 bool nsIContent::HasIndependentSelection() const {
    221  nsIFrame* frame = GetPrimaryFrame();
    222  return frame && frame->IsInsideTextControl();
    223 }
    224 
    225 dom::Element* nsIContent::GetEditingHost() const {
    226  // If this isn't editable, return nullptr.
    227  if (!IsEditable()) {
    228    return nullptr;
    229  }
    230 
    231  Document* doc = GetComposedDoc();
    232  if (!doc) {
    233    return nullptr;
    234  }
    235 
    236  // If this is in designMode, we should return <body>
    237  if (IsInDesignMode() && !IsInShadowTree()) {
    238    // FIXME: There may be no <body>.  In such case and aLimitInBodyElement is
    239    // "No", we should use root element instead.
    240    return doc->GetBodyElement();
    241  }
    242 
    243  dom::Element* editableParentElement = nullptr;
    244  for (dom::Element* parent = GetParentElement();
    245       parent && parent->HasFlag(NODE_IS_EDITABLE);
    246       parent = editableParentElement->GetParentElement()) {
    247    editableParentElement = parent;
    248  }
    249  return editableParentElement
    250             ? editableParentElement
    251             : dom::Element::FromNode(const_cast<nsIContent*>(this));
    252 }
    253 
    254 nsresult nsIContent::LookupNamespaceURIInternal(
    255    const nsAString& aNamespacePrefix, nsAString& aNamespaceURI) const {
    256  if (aNamespacePrefix.EqualsLiteral("xml")) {
    257    // Special-case for xml prefix
    258    aNamespaceURI.AssignLiteral("http://www.w3.org/XML/1998/namespace");
    259    return NS_OK;
    260  }
    261 
    262  if (aNamespacePrefix.EqualsLiteral("xmlns")) {
    263    // Special-case for xmlns prefix
    264    aNamespaceURI.AssignLiteral("http://www.w3.org/2000/xmlns/");
    265    return NS_OK;
    266  }
    267 
    268  RefPtr<nsAtom> name;
    269  if (!aNamespacePrefix.IsEmpty()) {
    270    name = NS_Atomize(aNamespacePrefix);
    271    NS_ENSURE_TRUE(name, NS_ERROR_OUT_OF_MEMORY);
    272  } else {
    273    name = nsGkAtoms::xmlns;
    274  }
    275  // Trace up the content parent chain looking for the namespace
    276  // declaration that declares aNamespacePrefix.
    277  for (Element* element = GetAsElementOrParentElement(); element;
    278       element = element->GetParentElement()) {
    279    if (element->GetAttr(kNameSpaceID_XMLNS, name, aNamespaceURI)) {
    280      return NS_OK;
    281    }
    282  }
    283  return NS_ERROR_FAILURE;
    284 }
    285 
    286 nsAtom* nsIContent::GetLang() const {
    287  for (const Element* element = GetAsElementOrParentElement(); element;
    288       element = element->GetParentElement()) {
    289    if (!element->GetAttrCount()) {
    290      continue;
    291    }
    292 
    293    // xml:lang has precedence over lang on HTML elements (see
    294    // XHTML1 section C.7).
    295    const nsAttrValue* attr =
    296        element->GetParsedAttr(nsGkAtoms::lang, kNameSpaceID_XML);
    297    if (!attr && element->SupportsLangAttr()) {
    298      attr = element->GetParsedAttr(nsGkAtoms::lang);
    299    }
    300    if (attr) {
    301      MOZ_ASSERT(attr->Type() == nsAttrValue::eAtom);
    302      MOZ_ASSERT(attr->GetAtomValue());
    303      return attr->GetAtomValue();
    304    }
    305  }
    306 
    307  return nullptr;
    308 }
    309 
    310 nsIURI* nsIContent::GetBaseURI(bool aTryUseXHRDocBaseURI) const {
    311  if (SVGUseElement* use = GetContainingSVGUseShadowHost()) {
    312    if (URLExtraData* data = use->GetContentURLData()) {
    313      return data->BaseURI();
    314    }
    315  }
    316 
    317  return OwnerDoc()->GetBaseURI(aTryUseXHRDocBaseURI);
    318 }
    319 
    320 nsIURI* nsIContent::GetBaseURIForStyleAttr() const {
    321  if (SVGUseElement* use = GetContainingSVGUseShadowHost()) {
    322    if (URLExtraData* data = use->GetContentURLData()) {
    323      return data->BaseURI();
    324    }
    325  }
    326  // This also ignores the case that SVG inside XBL binding.
    327  // But it is probably fine.
    328  return OwnerDoc()->GetDocBaseURI();
    329 }
    330 
    331 already_AddRefed<URLExtraData> nsIContent::GetURLDataForStyleAttr(
    332    nsIPrincipal* aSubjectPrincipal) const {
    333  if (SVGUseElement* use = GetContainingSVGUseShadowHost()) {
    334    if (URLExtraData* data = use->GetContentURLData()) {
    335      return do_AddRef(data);
    336    }
    337  }
    338  auto* doc = OwnerDoc();
    339  if (aSubjectPrincipal && aSubjectPrincipal != NodePrincipal()) {
    340    nsCOMPtr<nsIReferrerInfo> referrerInfo =
    341        doc->ReferrerInfoForInternalCSSAndSVGResources();
    342    // TODO: Cache this?
    343    return MakeAndAddRef<URLExtraData>(doc->GetDocBaseURI(), referrerInfo,
    344                                       aSubjectPrincipal);
    345  }
    346  return do_AddRef(doc->DefaultStyleAttrURLData());
    347 }
    348 
    349 void nsIContent::ConstructUbiNode(void* storage) {
    350  JS::ubi::Concrete<nsIContent>::construct(storage, this);
    351 }
    352 
    353 bool nsIContent::InclusiveDescendantMayNeedSpellchecking(HTMLEditor* aEditor) {
    354  // Return true if the node may have elements as children, since those or their
    355  // descendants may have spellcheck attributes.
    356  return HasFlag(NODE_MAY_HAVE_ELEMENT_CHILDREN) ||
    357         mozInlineSpellChecker::ShouldSpellCheckNode(aEditor, this);
    358 }
    359 
    360 //----------------------------------------------------------------------
    361 
    362 static inline JSObject* GetJSObjectChild(nsWrapperCache* aCache) {
    363  return aCache->PreservingWrapper() ? aCache->GetWrapperPreserveColor()
    364                                     : nullptr;
    365 }
    366 
    367 static bool NeedsScriptTraverse(nsINode* aNode) {
    368  return aNode->PreservingWrapper() && aNode->GetWrapperPreserveColor() &&
    369         !aNode->HasKnownLiveWrapperAndDoesNotNeedTracing(aNode);
    370 }
    371 
    372 //----------------------------------------------------------------------
    373 
    374 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAttrChildContentList)
    375 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAttrChildContentList)
    376 
    377 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsAttrChildContentList, mNode)
    378 
    379 // If the wrapper is known-live, the list can't be part of a garbage cycle.
    380 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsAttrChildContentList)
    381  return tmp->HasKnownLiveWrapper();
    382 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
    383 
    384 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsAttrChildContentList)
    385  return tmp->HasKnownLiveWrapperAndDoesNotNeedTracing(tmp);
    386 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
    387 
    388 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsAttrChildContentList)
    389 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
    390 
    391 NS_INTERFACE_TABLE_HEAD(nsAttrChildContentList)
    392  NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
    393  NS_INTERFACE_TABLE(nsAttrChildContentList, nsINodeList)
    394  NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsAttrChildContentList)
    395 NS_INTERFACE_MAP_END
    396 
    397 JSObject* nsAttrChildContentList::WrapObject(
    398    JSContext* cx, JS::Handle<JSObject*> aGivenProto) {
    399  return NodeList_Binding::Wrap(cx, this, aGivenProto);
    400 }
    401 
    402 uint32_t nsAttrChildContentList::Length() {
    403  return mNode ? mNode->GetChildCount() : 0;
    404 }
    405 
    406 nsIContent* nsAttrChildContentList::Item(uint32_t aIndex) {
    407  if (mNode) {
    408    return mNode->GetChildAt_Deprecated(aIndex);
    409  }
    410 
    411  return nullptr;
    412 }
    413 
    414 int32_t nsAttrChildContentList::IndexOf(nsIContent* aContent) {
    415  if (mNode) {
    416    return mNode->ComputeIndexOf_Deprecated(aContent);
    417  }
    418 
    419  return -1;
    420 }
    421 
    422 //----------------------------------------------------------------------
    423 uint32_t nsParentNodeChildContentList::Length() {
    424  return mNode ? mNode->GetChildCount() : 0;
    425 }
    426 
    427 nsIContent* nsParentNodeChildContentList::Item(uint32_t aIndex) {
    428  if (!mIsCacheValid) {
    429    if (MOZ_UNLIKELY(!mNode)) {
    430      return nullptr;
    431    }
    432    // Try to avoid the cache for some common cases, see bug 1917511.
    433    if (aIndex == 0) {
    434      return mNode->GetFirstChild();
    435    }
    436    uint32_t childCount = mNode->GetChildCount();
    437    if (aIndex >= childCount) {
    438      return nullptr;
    439    }
    440    if (aIndex + 1 == childCount) {
    441      return mNode->GetLastChild();
    442    }
    443    ValidateCache();
    444    MOZ_ASSERT(mIsCacheValid);
    445  }
    446  return mCachedChildArray.SafeElementAt(aIndex, nullptr);
    447 }
    448 
    449 int32_t nsParentNodeChildContentList::IndexOf(nsIContent* aContent) {
    450  EnsureCacheValid();
    451  return mCachedChildArray.IndexOf(aContent);
    452 }
    453 
    454 void nsParentNodeChildContentList::ValidateCache() {
    455  MOZ_ASSERT(!mIsCacheValid);
    456  MOZ_ASSERT(mCachedChildArray.IsEmpty());
    457 
    458  if (MOZ_UNLIKELY(!mNode)) {
    459    return;
    460  }
    461 
    462  for (nsIContent* node = mNode->GetFirstChild(); node;
    463       node = node->GetNextSibling()) {
    464    mCachedChildArray.AppendElement(node);
    465  }
    466  mIsCacheValid = true;
    467 }
    468 
    469 //----------------------------------------------------------------------
    470 
    471 nsIHTMLCollection* FragmentOrElement::Children() {
    472  nsDOMSlots* slots = DOMSlots();
    473 
    474  if (!slots->mChildrenList) {
    475    slots->mChildrenList =
    476        new nsContentList(this, kNameSpaceID_Wildcard, nsGkAtoms::_asterisk,
    477                          nsGkAtoms::_asterisk, false);
    478  }
    479 
    480  return slots->mChildrenList;
    481 }
    482 
    483 //----------------------------------------------------------------------
    484 
    485 NS_IMPL_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff, mNode)
    486 
    487 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff)
    488  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
    489 NS_INTERFACE_MAP_END_AGGREGATED(mNode)
    490 
    491 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsNodeSupportsWeakRefTearoff)
    492 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsNodeSupportsWeakRefTearoff)
    493 
    494 NS_IMETHODIMP
    495 nsNodeSupportsWeakRefTearoff::GetWeakReference(
    496    nsIWeakReference** aInstancePtr) {
    497  nsINode::nsSlots* slots = mNode->Slots();
    498  if (!slots->mWeakReference) {
    499    slots->mWeakReference = new nsNodeWeakReference(mNode);
    500  }
    501 
    502  NS_ADDREF(*aInstancePtr = slots->mWeakReference);
    503 
    504  return NS_OK;
    505 }
    506 
    507 //----------------------------------------------------------------------
    508 
    509 static const size_t MaxDOMSlotSizeAllowed =
    510 #ifdef HAVE_64BIT_BUILD
    511    128;
    512 #else
    513    64;
    514 #endif
    515 
    516 static_assert(sizeof(nsINode::nsSlots) <= MaxDOMSlotSizeAllowed,
    517              "DOM slots cannot be grown without consideration");
    518 static_assert(sizeof(FragmentOrElement::nsDOMSlots) <= MaxDOMSlotSizeAllowed,
    519              "DOM slots cannot be grown without consideration");
    520 
    521 void nsIContent::nsExtendedContentSlots::UnlinkExtendedSlots(nsIContent&) {
    522  mContainingShadow = nullptr;
    523  mAssignedSlot = nullptr;
    524 }
    525 
    526 void nsIContent::nsExtendedContentSlots::TraverseExtendedSlots(
    527    nsCycleCollectionTraversalCallback& aCb) {
    528  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mContainingShadow");
    529  aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mContainingShadow));
    530 
    531  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mAssignedSlot");
    532  aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mAssignedSlot.get()));
    533 }
    534 
    535 nsIContent::nsExtendedContentSlots::nsExtendedContentSlots() = default;
    536 
    537 nsIContent::nsExtendedContentSlots::~nsExtendedContentSlots() {
    538  MOZ_ASSERT(!mManualSlotAssignment);
    539 }
    540 
    541 size_t nsIContent::nsExtendedContentSlots::SizeOfExcludingThis(
    542    MallocSizeOf aMallocSizeOf) const {
    543  // For now, nothing to measure here.  We don't actually own any of our
    544  // members.
    545  return 0;
    546 }
    547 
    548 FragmentOrElement::nsDOMSlots::nsDOMSlots() { MOZ_COUNT_CTOR(nsDOMSlots); }
    549 
    550 FragmentOrElement::nsDOMSlots::~nsDOMSlots() {
    551  MOZ_COUNT_DTOR(nsDOMSlots);
    552 
    553  if (mAttributeMap) {
    554    mAttributeMap->DropReference();
    555  }
    556 }
    557 
    558 void FragmentOrElement::nsDOMSlots::Traverse(
    559    nsCycleCollectionTraversalCallback& aCb) {
    560  nsIContent::nsContentSlots::Traverse(aCb);
    561 
    562  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mStyle");
    563  aCb.NoteXPCOMChild(mStyle.get());
    564 
    565  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mAttributeMap");
    566  aCb.NoteXPCOMChild(mAttributeMap.get());
    567 
    568  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mChildrenList");
    569  aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsINodeList*, mChildrenList));
    570 
    571  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mClassList");
    572  aCb.NoteXPCOMChild(mClassList.get());
    573 
    574  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mComputedStyleMap");
    575  aCb.NoteXPCOMChild(mComputedStyleMap.get());
    576 
    577  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mAttributeStyleMap");
    578  aCb.NoteXPCOMChild(mAttributeStyleMap.get());
    579 }
    580 
    581 void FragmentOrElement::nsDOMSlots::Unlink(nsINode& aNode) {
    582  nsIContent::nsContentSlots::Unlink(aNode);
    583  mStyle = nullptr;
    584  if (mAttributeMap) {
    585    mAttributeMap->DropReference();
    586    mAttributeMap = nullptr;
    587  }
    588  mChildrenList = nullptr;
    589  mClassList = nullptr;
    590  mComputedStyleMap = nullptr;
    591  mAttributeStyleMap = nullptr;
    592 }
    593 
    594 size_t FragmentOrElement::nsDOMSlots::SizeOfIncludingThis(
    595    MallocSizeOf aMallocSizeOf) const {
    596  size_t n = aMallocSizeOf(this);
    597 
    598  nsExtendedContentSlots* extendedSlots = GetExtendedContentSlots();
    599  if (extendedSlots) {
    600    if (OwnsExtendedSlots()) {
    601      n += aMallocSizeOf(extendedSlots);
    602    }
    603 
    604    n += extendedSlots->SizeOfExcludingThis(aMallocSizeOf);
    605  }
    606 
    607  if (mAttributeMap) {
    608    n += mAttributeMap->SizeOfIncludingThis(aMallocSizeOf);
    609  }
    610 
    611  if (mChildrenList) {
    612    n += mChildrenList->SizeOfIncludingThis(aMallocSizeOf);
    613  }
    614 
    615  if (mComputedStyleMap) {
    616    n += mComputedStyleMap->SizeOfIncludingThis(aMallocSizeOf);
    617  }
    618 
    619  if (mAttributeStyleMap) {
    620    n += mAttributeStyleMap->SizeOfIncludingThis(aMallocSizeOf);
    621  }
    622 
    623  // Measurement of the following members may be added later if DMD finds it is
    624  // worthwhile:
    625  // - Superclass members (nsINode::nsSlots)
    626  // - mStyle
    627  // - mClassList
    628 
    629  // The following member are not measured:
    630  // - mControllers: because it is non-owning
    631  return n;
    632 }
    633 
    634 FragmentOrElement::nsExtendedDOMSlots::nsExtendedDOMSlots() = default;
    635 
    636 FragmentOrElement::nsExtendedDOMSlots::~nsExtendedDOMSlots() = default;
    637 
    638 void FragmentOrElement::nsExtendedDOMSlots::UnlinkExtendedSlots(
    639    nsIContent& aContent) {
    640  nsIContent::nsExtendedContentSlots::UnlinkExtendedSlots(aContent);
    641 
    642  // mShadowRoot will similarly be cleared explicitly from
    643  // FragmentOrElement::Unlink.
    644  mSMILOverrideStyle = nullptr;
    645  mControllers = nullptr;
    646  mLabelsList = nullptr;
    647  mPopoverData = nullptr;
    648  if (mCustomElementData) {
    649    mCustomElementData->Unlink();
    650    mCustomElementData = nullptr;
    651  }
    652  if (mAnimations) {
    653    mAnimations = nullptr;
    654    aContent.ClearMayHaveAnimations();
    655  }
    656  mExplicitlySetAttrElementMap.Clear();
    657  mAttrElementsMap.Clear();
    658  mRadioGroupContainer = nullptr;
    659  mPart = nullptr;
    660 }
    661 
    662 void FragmentOrElement::nsExtendedDOMSlots::TraverseExtendedSlots(
    663    nsCycleCollectionTraversalCallback& aCb) {
    664  nsIContent::nsExtendedContentSlots::TraverseExtendedSlots(aCb);
    665 
    666  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mSMILOverrideStyle");
    667  aCb.NoteXPCOMChild(mSMILOverrideStyle.get());
    668 
    669  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mControllers");
    670  aCb.NoteXPCOMChild(mControllers);
    671 
    672  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mLabelsList");
    673  aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsINodeList*, mLabelsList));
    674 
    675  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mShadowRoot");
    676  aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mShadowRoot));
    677 
    678  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mPart");
    679  aCb.NoteXPCOMChild(mPart.get());
    680 
    681  for (auto& tableEntry : mAttrElementsMap) {
    682    auto& [explicitlySetElements, cachedAttrElements] =
    683        *tableEntry.GetModifiableData();
    684    if (cachedAttrElements) {
    685      ImplCycleCollectionTraverse(aCb, *cachedAttrElements,
    686                                  "cached attribute elements entry", 0);
    687    }
    688  }
    689 
    690  if (mCustomElementData) {
    691    mCustomElementData->Traverse(aCb);
    692  }
    693  if (mAnimations) {
    694    mAnimations->Traverse(aCb);
    695  }
    696  if (mRadioGroupContainer) {
    697    RadioGroupContainer::Traverse(mRadioGroupContainer.get(), aCb);
    698  }
    699 }
    700 
    701 size_t FragmentOrElement::nsExtendedDOMSlots::SizeOfExcludingThis(
    702    MallocSizeOf aMallocSizeOf) const {
    703  size_t n =
    704      nsIContent::nsExtendedContentSlots::SizeOfExcludingThis(aMallocSizeOf);
    705 
    706  // We own mSMILOverrideStyle but there seems to be no memory reporting on CSS
    707  // declarations?  At least report the memory the declaration takes up
    708  // directly.
    709  if (mSMILOverrideStyle) {
    710    n += aMallocSizeOf(mSMILOverrideStyle);
    711  }
    712 
    713  // We don't really own mSMILOverrideStyleDeclaration.  mSMILOverrideStyle owns
    714  // it.
    715 
    716  // We don't seem to have memory reporting for nsXULControllers.  At least
    717  // report the memory it's using directly.
    718  if (mControllers) {
    719    n += aMallocSizeOf(mControllers);
    720  }
    721 
    722  if (mLabelsList) {
    723    n += mLabelsList->SizeOfIncludingThis(aMallocSizeOf);
    724  }
    725 
    726  // mShadowRoot should be handled during normal DOM tree memory reporting, just
    727  // like kids, siblings, etc.
    728 
    729  if (mCustomElementData) {
    730    n += mCustomElementData->SizeOfIncludingThis(aMallocSizeOf);
    731  }
    732 
    733  if (mRadioGroupContainer) {
    734    n += mRadioGroupContainer->SizeOfIncludingThis(aMallocSizeOf);
    735  }
    736 
    737  return n;
    738 }
    739 
    740 FragmentOrElement::FragmentOrElement(
    741    already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
    742    : nsIContent(std::move(aNodeInfo)) {}
    743 
    744 FragmentOrElement::~FragmentOrElement() {
    745  MOZ_ASSERT(!IsInUncomposedDoc(),
    746             "Please remove this from the document properly");
    747  if (GetParent()) {
    748    NS_RELEASE(mParent);
    749  }
    750 }
    751 
    752 static nsINode* FindChromeAccessOnlySubtreeOwnerForEvents(nsINode* aNode) {
    753  if (!aNode->ChromeOnlyAccessForEvents()) {
    754    return aNode;
    755  }
    756  return aNode->GetClosestNativeAnonymousSubtreeRootParentOrHost();
    757 }
    758 
    759 nsINode* FindChromeAccessOnlySubtreeOwnerForEvents(EventTarget* aTarget) {
    760  nsINode* node = nsINode::FromEventTargetOrNull(aTarget);
    761  if (!node) {
    762    return nullptr;
    763  }
    764  return FindChromeAccessOnlySubtreeOwnerForEvents(node);
    765 }
    766 
    767 void nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
    768  // FIXME! Document how this event retargeting works, Bug 329124.
    769  aVisitor.mCanHandle = true;
    770  aVisitor.mMayHaveListenerManager = HasListenerManager();
    771 
    772  if (IsInShadowTree()) {
    773    aVisitor.mItemInShadowTree = true;
    774  }
    775 
    776  // Don't propagate mouseover and mouseout events when mouse is moving
    777  // inside chrome access only content.
    778  const bool isAnonForEvents = IsRootOfChromeAccessOnlySubtree();
    779  aVisitor.mRootOfClosedTree = isAnonForEvents;
    780  if ((aVisitor.mEvent->mMessage == eMouseOver ||
    781       aVisitor.mEvent->mMessage == eMouseOut ||
    782       aVisitor.mEvent->mMessage == ePointerOver ||
    783       aVisitor.mEvent->mMessage == ePointerOut) &&
    784      // Check if we should stop event propagation when event has just been
    785      // dispatched or when we're about to propagate from
    786      // chrome access only subtree or if we are about to propagate out of
    787      // a shadow root to a shadow root host.
    788      ((this == aVisitor.mEvent->mOriginalTarget && !ChromeOnlyAccess()) ||
    789       isAnonForEvents)) {
    790    nsIContent* relatedTarget = nsIContent::FromEventTargetOrNull(
    791        aVisitor.mEvent->AsMouseEvent()->mRelatedTarget);
    792    if (relatedTarget && relatedTarget->OwnerDoc() == OwnerDoc()) {
    793      // If current target is anonymous for events or we know that related
    794      // target is descendant of an element which is anonymous for events,
    795      // we may want to stop event propagation.
    796      // If this is the original target, aVisitor.mRelatedTargetIsInAnon
    797      // must be updated.
    798      if (isAnonForEvents || aVisitor.mRelatedTargetIsInAnon ||
    799          (aVisitor.mEvent->mOriginalTarget == this &&
    800           (aVisitor.mRelatedTargetIsInAnon =
    801                relatedTarget->ChromeOnlyAccessForEvents()))) {
    802        nsINode* anonOwner = FindChromeAccessOnlySubtreeOwnerForEvents(this);
    803        if (anonOwner) {
    804          nsINode* anonOwnerRelated =
    805              FindChromeAccessOnlySubtreeOwnerForEvents(relatedTarget);
    806          if (anonOwnerRelated) {
    807            // Note, anonOwnerRelated may still be inside some other
    808            // native anonymous subtree. The case where anonOwner is still
    809            // inside native anonymous subtree will be handled when event
    810            // propagates up in the DOM tree.
    811            while (anonOwner != anonOwnerRelated &&
    812                   anonOwnerRelated->ChromeOnlyAccessForEvents()) {
    813              anonOwnerRelated =
    814                  FindChromeAccessOnlySubtreeOwnerForEvents(anonOwnerRelated);
    815            }
    816            if (anonOwner == anonOwnerRelated) {
    817 #ifdef DEBUG_smaug
    818              nsIContent* originalTarget = nsIContent::FromEventTargetOrNull(
    819                  aVisitor.mEvent->mOriginalTarget);
    820              nsAutoString ot, ct, rt;
    821              if (originalTarget) {
    822                originalTarget->NodeInfo()->NameAtom()->ToString(ot);
    823              }
    824              NodeInfo()->NameAtom()->ToString(ct);
    825              relatedTarget->NodeInfo()->NameAtom()->ToString(rt);
    826              printf(
    827                  "Stopping %s propagation:"
    828                  "\n\toriginalTarget=%s \n\tcurrentTarget=%s %s"
    829                  "\n\trelatedTarget=%s %s \n%s",
    830                  (aVisitor.mEvent->mMessage == eMouseOver) ? "mouseover"
    831                                                            : "mouseout",
    832                  NS_ConvertUTF16toUTF8(ot).get(),
    833                  NS_ConvertUTF16toUTF8(ct).get(),
    834                  isAnonForEvents
    835                      ? "(is native anonymous)"
    836                      : (ChromeOnlyAccess() ? "(is in native anonymous subtree)"
    837                                            : ""),
    838                  NS_ConvertUTF16toUTF8(rt).get(),
    839                  relatedTarget->ChromeOnlyAccess()
    840                      ? "(is in native anonymous subtree)"
    841                      : "",
    842                  (originalTarget &&
    843                   relatedTarget->FindFirstNonChromeOnlyAccessContent() ==
    844                       originalTarget->FindFirstNonChromeOnlyAccessContent())
    845                      ? ""
    846                      : "Wrong event propagation!?!\n");
    847 #endif
    848              aVisitor.SetParentTarget(nullptr, false);
    849              // Event should not propagate to non-anon content.
    850              aVisitor.mCanHandle = isAnonForEvents;
    851              return;
    852            }
    853          }
    854        }
    855      }
    856    }
    857  }
    858 
    859  // Event parent is the assigned slot, if node is assigned, or node's parent
    860  // otherwise.
    861  HTMLSlotElement* slot = GetAssignedSlot();
    862  nsIContent* parent = slot ? slot : GetParent();
    863 
    864  // Event may need to be retargeted if this is the root of a native anonymous
    865  // content subtree.
    866  if (isAnonForEvents) {
    867    aVisitor.mEventTargetAtParent = parent;
    868  } else if (parent && aVisitor.mOriginalTargetIsInAnon) {
    869    nsIContent* content =
    870        nsIContent::FromEventTargetOrNull(aVisitor.mEvent->mTarget);
    871    if (content &&
    872        content->GetClosestNativeAnonymousSubtreeRootParentOrHost() == parent) {
    873      aVisitor.mEventTargetAtParent = parent;
    874    }
    875  }
    876 
    877  if (!aVisitor.mEvent->mFlags.mComposedInNativeAnonymousContent &&
    878      isAnonForEvents && OwnerDoc()->GetWindow()) {
    879    aVisitor.SetParentTarget(OwnerDoc()->GetWindow()->GetParentTarget(), true);
    880  } else if (parent) {
    881    aVisitor.SetParentTarget(parent, false);
    882    if (slot) {
    883      ShadowRoot* root = slot->GetContainingShadow();
    884      if (root && root->IsClosed()) {
    885        aVisitor.mParentIsSlotInClosedTree = true;
    886      }
    887    }
    888  } else {
    889    aVisitor.SetParentTarget(GetComposedDoc(), false);
    890  }
    891 
    892  if (!ChromeOnlyAccessForEvents() &&
    893      !aVisitor.mRelatedTargetRetargetedInCurrentScope) {
    894    // We don't support Shadow DOM in native anonymous content yet.
    895    aVisitor.mRelatedTargetRetargetedInCurrentScope = true;
    896    if (aVisitor.mEvent->mOriginalRelatedTarget) {
    897      // https://dom.spec.whatwg.org/#concept-event-dispatch
    898      // Step 3.
    899      // "Let relatedTarget be the result of retargeting event's relatedTarget
    900      //  against target if event's relatedTarget is non-null, and null
    901      //  otherwise."
    902      //
    903      // This is a bit complicated because the event might be from native
    904      // anonymous content, but we need to deal with non-native anonymous
    905      // content there.
    906      bool initialTarget = this == aVisitor.mEvent->mOriginalTarget;
    907      nsINode* originalTargetAsNode = nullptr;
    908      // Use of mOriginalTargetIsInAnon is an optimization here.
    909      if (!initialTarget && aVisitor.mOriginalTargetIsInAnon) {
    910        originalTargetAsNode = FindChromeAccessOnlySubtreeOwnerForEvents(
    911            aVisitor.mEvent->mOriginalTarget);
    912        initialTarget = originalTargetAsNode == this;
    913      }
    914      if (initialTarget) {
    915        nsINode* relatedTargetAsNode =
    916            FindChromeAccessOnlySubtreeOwnerForEvents(
    917                aVisitor.mEvent->mOriginalRelatedTarget);
    918        if (!originalTargetAsNode) {
    919          originalTargetAsNode =
    920              nsINode::FromEventTargetOrNull(aVisitor.mEvent->mOriginalTarget);
    921        }
    922 
    923        if (relatedTargetAsNode && originalTargetAsNode) {
    924          nsINode* retargetedRelatedTarget = nsContentUtils::Retarget(
    925              relatedTargetAsNode, originalTargetAsNode);
    926          if (originalTargetAsNode == retargetedRelatedTarget &&
    927              retargetedRelatedTarget != relatedTargetAsNode) {
    928            // Step 4.
    929            // "If target is relatedTarget and target is not event's
    930            //  relatedTarget, then return true."
    931            aVisitor.IgnoreCurrentTargetBecauseOfShadowDOMRetargeting();
    932            // Old code relies on mTarget to point to the first element which
    933            // was not added to the event target chain because of mCanHandle
    934            // being false, but in Shadow DOM case mTarget really should
    935            // point to a node in Shadow DOM.
    936            aVisitor.mEvent->mTarget = aVisitor.mTargetInKnownToBeHandledScope;
    937            return;
    938          }
    939 
    940          // Part of step 5. Retargeting target has happened already higher
    941          // up in this method.
    942          // "Append to an event path with event, target, targetOverride,
    943          //  relatedTarget, and false."
    944          aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
    945        }
    946      } else if (nsINode* relatedTargetAsNode =
    947                     FindChromeAccessOnlySubtreeOwnerForEvents(
    948                         aVisitor.mEvent->mOriginalRelatedTarget)) {
    949        // Step 11.3.
    950        // "Let relatedTarget be the result of retargeting event's
    951        // relatedTarget against parent if event's relatedTarget is non-null,
    952        // and null otherwise.".
    953        nsINode* retargetedRelatedTarget =
    954            nsContentUtils::Retarget(relatedTargetAsNode, this);
    955        nsINode* targetInKnownToBeHandledScope =
    956            FindChromeAccessOnlySubtreeOwnerForEvents(
    957                aVisitor.mTargetInKnownToBeHandledScope);
    958        // If aVisitor.mTargetInKnownToBeHandledScope wasn't nsINode,
    959        // targetInKnownToBeHandledScope will be null. This may happen when
    960        // dispatching event to Window object in a content page and
    961        // propagating the event to a chrome Element.
    962        if (targetInKnownToBeHandledScope &&
    963            IsShadowIncludingInclusiveDescendantOf(
    964                targetInKnownToBeHandledScope->SubtreeRoot())) {
    965          // Part of step 11.4.
    966          // "If target's root is a shadow-including inclusive ancestor of
    967          //  parent, then"
    968          // "...Append to an event path with event, parent, null,
    969          // relatedTarget, "   and slot-in-closed-tree."
    970          aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
    971        } else if (this == retargetedRelatedTarget) {
    972          // Step 11.5
    973          // "Otherwise, if parent and relatedTarget are identical, then set
    974          //  parent to null."
    975          aVisitor.IgnoreCurrentTargetBecauseOfShadowDOMRetargeting();
    976          // Old code relies on mTarget to point to the first element which
    977          // was not added to the event target chain because of mCanHandle
    978          // being false, but in Shadow DOM case mTarget really should
    979          // point to a node in Shadow DOM.
    980          aVisitor.mEvent->mTarget = aVisitor.mTargetInKnownToBeHandledScope;
    981          return;
    982        } else if (targetInKnownToBeHandledScope) {
    983          // Note, if targetInKnownToBeHandledScope is null,
    984          // mTargetInKnownToBeHandledScope could be Window object in content
    985          // page and we're in chrome document in the same process.
    986 
    987          // Step 11.6
    988          aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
    989        }
    990      }
    991    }
    992 
    993    if (aVisitor.mEvent->mClass == eTouchEventClass) {
    994      // Retarget touch objects.
    995      MOZ_ASSERT(!aVisitor.mRetargetedTouchTargets.isSome());
    996      aVisitor.mRetargetedTouchTargets.emplace();
    997      WidgetTouchEvent* touchEvent = aVisitor.mEvent->AsTouchEvent();
    998      WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
    999      for (uint32_t i = 0; i < touches.Length(); ++i) {
   1000        Touch* touch = touches[i];
   1001        EventTarget* originalTarget = touch->mOriginalTarget;
   1002        EventTarget* touchTarget = originalTarget;
   1003        nsCOMPtr<nsINode> targetAsNode =
   1004            nsINode::FromEventTargetOrNull(originalTarget);
   1005        if (targetAsNode) {
   1006          EventTarget* retargeted =
   1007              nsContentUtils::Retarget(targetAsNode, this);
   1008          if (retargeted) {
   1009            touchTarget = retargeted;
   1010          }
   1011        }
   1012        aVisitor.mRetargetedTouchTargets->AppendElement(touchTarget);
   1013        touch->mTarget = touchTarget;
   1014      }
   1015      MOZ_ASSERT(aVisitor.mRetargetedTouchTargets->Length() ==
   1016                 touches.Length());
   1017    }
   1018  }
   1019 
   1020  if (slot) {
   1021    // Inform that we're about to exit the current scope.
   1022    aVisitor.mRelatedTargetRetargetedInCurrentScope = false;
   1023  }
   1024 }
   1025 
   1026 Element* nsIContent::GetAutofocusDelegate(IsFocusableFlags aFlags) const {
   1027  for (nsINode* node = GetFirstChild(); node; node = node->GetNextNode(this)) {
   1028    auto* descendant = Element::FromNode(*node);
   1029    if (!descendant || !descendant->GetBoolAttr(nsGkAtoms::autofocus)) {
   1030      continue;
   1031    }
   1032 
   1033    nsIFrame* frame = descendant->GetPrimaryFrame();
   1034    if (frame && frame->IsFocusable(aFlags)) {
   1035      return descendant;
   1036    }
   1037  }
   1038  return nullptr;
   1039 }
   1040 
   1041 bool nsIContent::CanStartSelectionAsWebCompatHack() const {
   1042  if (!StaticPrefs::dom_selection_mimic_chrome_tostring_enabled()) {
   1043    return true;
   1044  }
   1045 
   1046  for (const nsIContent* content = this; content;
   1047       content = content->GetFlattenedTreeParent()) {
   1048    if (content->IsEditable()) {
   1049      return true;
   1050    }
   1051    nsIFrame* frame = content->GetPrimaryFrame();
   1052    if (!frame) {
   1053      return true;
   1054    }
   1055    if (!frame->IsSelectable()) {
   1056      return false;
   1057    }
   1058  }
   1059 
   1060  return true;
   1061 }
   1062 
   1063 Element* nsIContent::GetFocusDelegate(IsFocusableFlags aFlags) const {
   1064  const nsIContent* whereToLook = this;
   1065  if (ShadowRoot* root = GetShadowRoot()) {
   1066    if (!root->DelegatesFocus()) {
   1067      // 1. If focusTarget is a shadow host and its shadow root 's delegates
   1068      // focus is false, then return null.
   1069      return nullptr;
   1070    }
   1071    whereToLook = root;
   1072  }
   1073 
   1074  auto IsFocusable = [&](Element* aElement) -> Focusable {
   1075    nsIFrame* frame = aElement->GetPrimaryFrame();
   1076 
   1077    if (!frame) {
   1078      return {};
   1079    }
   1080 
   1081    return frame->IsFocusable(aFlags);
   1082  };
   1083 
   1084  Element* potentialFocus = nullptr;
   1085  for (nsINode* node = whereToLook->GetFirstChild(); node;
   1086       node = node->GetNextNode(whereToLook)) {
   1087    auto* el = Element::FromNode(*node);
   1088    if (!el) {
   1089      continue;
   1090    }
   1091 
   1092    const bool autofocus = el->GetBoolAttr(nsGkAtoms::autofocus);
   1093 
   1094    if (autofocus) {
   1095      if (IsFocusable(el)) {
   1096        // Found an autofocus candidate.
   1097        return el;
   1098      }
   1099    } else if (!potentialFocus) {
   1100      if (Focusable focusable = IsFocusable(el)) {
   1101        if (IsHTMLElement(nsGkAtoms::dialog)) {
   1102          if (focusable.mTabIndex >= 0) {
   1103            // If focusTarget is a dialog element and descendant is sequentially
   1104            // focusable, then set focusableArea to descendant.
   1105            potentialFocus = el;
   1106          }
   1107        } else {
   1108          // This element could be the one if we can't find an
   1109          // autofocus candidate which has the precedence.
   1110          potentialFocus = el;
   1111        }
   1112      }
   1113    }
   1114 
   1115    if (!autofocus && potentialFocus) {
   1116      // Nothing else to do, we are not looking for more focusable elements
   1117      // here.
   1118      continue;
   1119    }
   1120 
   1121    if (auto* shadow = el->GetShadowRoot()) {
   1122      if (shadow->DelegatesFocus()) {
   1123        if (Element* delegatedFocus = shadow->GetFocusDelegate(aFlags)) {
   1124          if (autofocus) {
   1125            // This element has autofocus and we found an focus delegates
   1126            // in its descendants, so use the focus delegates
   1127            return delegatedFocus;
   1128          }
   1129          if (!potentialFocus) {
   1130            potentialFocus = delegatedFocus;
   1131          }
   1132        }
   1133      }
   1134    }
   1135  }
   1136 
   1137  return potentialFocus;
   1138 }
   1139 
   1140 Focusable nsIContent::IsFocusableWithoutStyle(IsFocusableFlags) {
   1141  // Default, not tabbable
   1142  return {};
   1143 }
   1144 
   1145 void nsIContent::SetAssignedSlot(HTMLSlotElement* aSlot) {
   1146  MOZ_ASSERT(aSlot || GetExistingExtendedContentSlots());
   1147  ExtendedContentSlots()->mAssignedSlot = aSlot;
   1148 }
   1149 
   1150 #ifdef MOZ_DOM_LIST
   1151 void nsIContent::Dump() { List(); }
   1152 #endif
   1153 
   1154 void FragmentOrElement::GetTextContentInternal(nsAString& aTextContent,
   1155                                               OOMReporter& aError) {
   1156  if (!nsContentUtils::GetNodeTextContent(this, true, aTextContent, fallible)) {
   1157    aError.ReportOOM();
   1158  }
   1159 }
   1160 
   1161 void FragmentOrElement::SetTextContentInternal(
   1162    const nsAString& aTextContent, nsIPrincipal* aSubjectPrincipal,
   1163    ErrorResult& aError, MutationEffectOnScript aMutationEffectOnScript) {
   1164  bool tryReuse = false;
   1165  if (!aTextContent.IsEmpty()) {
   1166    if (nsIContent* firstChild = GetFirstChild()) {
   1167      tryReuse = firstChild->NodeType() == TEXT_NODE &&
   1168                 !firstChild->GetNextSibling() &&
   1169                 firstChild->OwnedOnlyByTheDOMAndFrameTrees() &&
   1170 #ifdef ACCESSIBILITY
   1171                 !GetAccService() &&
   1172 #endif
   1173                 !OwnerDoc()->MayHaveDOMMutationObservers() &&
   1174                 !MaybeNeedsToNotifyDevToolsOfNodeRemovalsInOwnerDoc();
   1175    }
   1176  }
   1177 
   1178  aError = nsContentUtils::SetNodeTextContent(this, aTextContent, tryReuse,
   1179                                              aMutationEffectOnScript);
   1180 }
   1181 
   1182 void FragmentOrElement::DestroyContent() {
   1183  // Drop any servo data. We do this before the RemovedFromDocument call below
   1184  // so that it doesn't need to try to keep the style state sane when shuffling
   1185  // around the flattened tree.
   1186  //
   1187  // TODO(emilio): I suspect this can be asserted against instead, with a bit of
   1188  // effort to avoid calling Document::Destroy with a shell...
   1189  if (IsElement()) {
   1190    AsElement()->ClearServoData();
   1191  }
   1192 
   1193 #ifdef DEBUG
   1194  uint32_t oldChildCount = GetChildCount();
   1195 #endif
   1196 
   1197  for (nsIContent* child = GetFirstChild(); child;
   1198       child = child->GetNextSibling()) {
   1199    child->DestroyContent();
   1200    MOZ_ASSERT(child->GetParent() == this,
   1201               "Mutating the tree during XBL destructors is evil");
   1202  }
   1203 
   1204  MOZ_ASSERT(oldChildCount == GetChildCount(),
   1205             "Mutating the tree during XBL destructors is evil");
   1206 
   1207  if (ShadowRoot* shadowRoot = GetShadowRoot()) {
   1208    shadowRoot->DestroyContent();
   1209  }
   1210 }
   1211 
   1212 void FragmentOrElement::SaveSubtreeState() {
   1213  for (nsIContent* child = GetFirstChild(); child;
   1214       child = child->GetNextSibling()) {
   1215    child->SaveSubtreeState();
   1216  }
   1217 
   1218  // FIXME(bug 1469277): Pretty sure this wants to dig into shadow trees as
   1219  // well.
   1220 }
   1221 
   1222 //----------------------------------------------------------------------
   1223 
   1224 // nsISupports implementation
   1225 
   1226 #define SUBTREE_UNBINDINGS_PER_RUNNABLE 500
   1227 
   1228 class ContentUnbinder : public Runnable {
   1229 public:
   1230  ContentUnbinder() : Runnable("ContentUnbinder") { mLast = this; }
   1231 
   1232  ~ContentUnbinder() { Run(); }
   1233 
   1234  void UnbindSubtree(nsIContent* aNode) {
   1235    if (!aNode->HasChildren()) {
   1236      return;
   1237    }
   1238    if (aNode->NodeType() != nsINode::ELEMENT_NODE &&
   1239        aNode->NodeType() != nsINode::DOCUMENT_FRAGMENT_NODE) {
   1240      return;
   1241    }
   1242    auto* container = static_cast<FragmentOrElement*>(aNode);
   1243    // Invalidate cached array of child nodes
   1244    container->InvalidateChildNodes();
   1245    BatchRemovalState state{};
   1246    while (nsCOMPtr<nsIContent> child = container->GetLastChild()) {
   1247      // Hold a strong ref to the node when we remove it, because we may be
   1248      // the last reference to it.  We need to call DisconnectChild()
   1249      // before calling UnbindFromTree, since this last can notify various
   1250      // observers and they should really see consistent tree state.
   1251      // If this code changes, change the corresponding code in
   1252      // FragmentOrElement's and Document's unlink impls.
   1253      container->DisconnectChild(child);
   1254      UnbindSubtree(child);
   1255      child->UnbindFromTree(/* aNewParent = */ nullptr, &state);
   1256      state.mIsFirst = false;
   1257    }
   1258  }
   1259 
   1260  NS_IMETHOD Run() override {
   1261    nsAutoScriptBlocker scriptBlocker;
   1262    uint32_t len = mSubtreeRoots.Length();
   1263    if (len) {
   1264      for (uint32_t i = 0; i < len; ++i) {
   1265        UnbindSubtree(mSubtreeRoots[i]);
   1266      }
   1267      mSubtreeRoots.Clear();
   1268    }
   1269    nsCycleCollector_dispatchDeferredDeletion();
   1270    if (this == sContentUnbinder) {
   1271      sContentUnbinder = nullptr;
   1272      if (mNext) {
   1273        RefPtr<ContentUnbinder> next;
   1274        next.swap(mNext);
   1275        sContentUnbinder = next;
   1276        next->mLast = mLast;
   1277        mLast = nullptr;
   1278        NS_DispatchToCurrentThreadQueue(next.forget(),
   1279                                        EventQueuePriority::Idle);
   1280      }
   1281    }
   1282    return NS_OK;
   1283  }
   1284 
   1285  static void UnbindAll() {
   1286    RefPtr<ContentUnbinder> ub = sContentUnbinder;
   1287    sContentUnbinder = nullptr;
   1288    while (ub) {
   1289      ub->Run();
   1290      ub = ub->mNext;
   1291    }
   1292  }
   1293 
   1294  static void Append(nsIContent* aSubtreeRoot) {
   1295    if (!sContentUnbinder) {
   1296      sContentUnbinder = new ContentUnbinder();
   1297      nsCOMPtr<nsIRunnable> e = sContentUnbinder;
   1298      NS_DispatchToCurrentThreadQueue(e.forget(), EventQueuePriority::Idle);
   1299    }
   1300 
   1301    if (sContentUnbinder->mLast->mSubtreeRoots.Length() >=
   1302        SUBTREE_UNBINDINGS_PER_RUNNABLE) {
   1303      sContentUnbinder->mLast->mNext = new ContentUnbinder();
   1304      sContentUnbinder->mLast = sContentUnbinder->mLast->mNext;
   1305    }
   1306    sContentUnbinder->mLast->mSubtreeRoots.AppendElement(aSubtreeRoot);
   1307  }
   1308 
   1309 private:
   1310  AutoTArray<nsCOMPtr<nsIContent>, SUBTREE_UNBINDINGS_PER_RUNNABLE>
   1311      mSubtreeRoots;
   1312  RefPtr<ContentUnbinder> mNext;
   1313  ContentUnbinder* mLast;
   1314  static ContentUnbinder* sContentUnbinder;
   1315 };
   1316 
   1317 ContentUnbinder* ContentUnbinder::sContentUnbinder = nullptr;
   1318 
   1319 void FragmentOrElement::ClearContentUnbinder() { ContentUnbinder::UnbindAll(); }
   1320 
   1321 // Note, _INHERITED macro isn't used here since nsINode implementations are
   1322 // rather special.
   1323 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(FragmentOrElement)
   1324 
   1325 // We purposefully don't UNLINK_BEGIN_INHERITED here.
   1326 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement)
   1327  nsIContent::Unlink(tmp);
   1328 
   1329  // Unlink child content (and unbind our subtree).
   1330  if (tmp->UnoptimizableCCNode() || !nsCCUncollectableMarker::sGeneration) {
   1331    // Don't allow script to run while we're unbinding everything.
   1332    nsAutoScriptBlocker scriptBlocker;
   1333    BatchRemovalState state{};
   1334    while (nsCOMPtr<nsIContent> child = tmp->GetLastChild()) {
   1335      // Hold a strong ref to the node when we remove it, because we may be
   1336      // the last reference to it.
   1337      // If this code changes, change the corresponding code in Document's
   1338      // unlink impl and ContentUnbinder::UnbindSubtree.
   1339      tmp->DisconnectChild(child);
   1340      child->UnbindFromTree(/* aNewParent = */ nullptr, &state);
   1341      state.mIsFirst = false;
   1342    }
   1343  } else if (!tmp->GetParent() && tmp->HasChildren()) {
   1344    ContentUnbinder::Append(tmp);
   1345  } /* else {
   1346    The subtree root will end up to a ContentUnbinder, and that will
   1347    unbind the child nodes.
   1348  } */
   1349 
   1350  if (ShadowRoot* shadowRoot = tmp->GetShadowRoot()) {
   1351    shadowRoot->Unbind();
   1352    tmp->ExtendedDOMSlots()->mShadowRoot = nullptr;
   1353  }
   1354 
   1355 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   1356 
   1357 void FragmentOrElement::MarkNodeChildren(nsINode* aNode) {
   1358  JSObject* o = GetJSObjectChild(aNode);
   1359  if (o) {
   1360    JS::ExposeObjectToActiveJS(o);
   1361  }
   1362 
   1363  EventListenerManager* elm = aNode->GetExistingListenerManager();
   1364  if (elm) {
   1365    elm->MarkForCC();
   1366  }
   1367 }
   1368 
   1369 nsINode* FindOptimizableSubtreeRoot(nsINode* aNode) {
   1370  nsINode* p;
   1371  while ((p = aNode->GetParentNode())) {
   1372    if (aNode->UnoptimizableCCNode()) {
   1373      return nullptr;
   1374    }
   1375    aNode = p;
   1376  }
   1377 
   1378  if (aNode->UnoptimizableCCNode()) {
   1379    return nullptr;
   1380  }
   1381  return aNode;
   1382 }
   1383 
   1384 StaticAutoPtr<nsTHashSet<nsINode*>> gCCBlackMarkedNodes;
   1385 
   1386 static void ClearBlackMarkedNodes() {
   1387  if (!gCCBlackMarkedNodes) {
   1388    return;
   1389  }
   1390  for (nsINode* n : *gCCBlackMarkedNodes) {
   1391    n->SetCCMarkedRoot(false);
   1392    n->SetInCCBlackTree(false);
   1393  }
   1394  gCCBlackMarkedNodes = nullptr;
   1395 }
   1396 
   1397 // static
   1398 void FragmentOrElement::RemoveBlackMarkedNode(nsINode* aNode) {
   1399  if (!gCCBlackMarkedNodes) {
   1400    return;
   1401  }
   1402  gCCBlackMarkedNodes->Remove(aNode);
   1403 }
   1404 
   1405 static bool IsCertainlyAliveNode(nsINode* aNode, Document* aDoc) {
   1406  MOZ_ASSERT(aNode->GetComposedDoc() == aDoc);
   1407 
   1408  // Marked to be in-CC-generation or if the document is an svg image that's
   1409  // being kept alive by the image cache. (Note that an svg image's internal
   1410  // SVG document will receive an OnPageHide() call when it gets purged from
   1411  // the image cache; hence, we use IsVisible() as a hint that the document is
   1412  // actively being kept alive by the cache.)
   1413  return nsCCUncollectableMarker::InGeneration(aDoc->GetMarkedCCGeneration()) ||
   1414         (nsCCUncollectableMarker::sGeneration && aDoc->IsBeingUsedAsImage() &&
   1415          aDoc->IsVisible());
   1416 }
   1417 
   1418 // static
   1419 bool FragmentOrElement::CanSkipInCC(nsINode* aNode) {
   1420  // Don't try to optimize anything during shutdown.
   1421  if (nsCCUncollectableMarker::sGeneration == 0) {
   1422    return false;
   1423  }
   1424 
   1425  Document* currentDoc = aNode->GetComposedDoc();
   1426  if (currentDoc && IsCertainlyAliveNode(aNode, currentDoc)) {
   1427    return !NeedsScriptTraverse(aNode);
   1428  }
   1429 
   1430  // Bail out early if aNode is somewhere in anonymous content,
   1431  // or otherwise unusual.
   1432  if (aNode->UnoptimizableCCNode()) {
   1433    return false;
   1434  }
   1435 
   1436  nsINode* root = currentDoc ? static_cast<nsINode*>(currentDoc)
   1437                             : FindOptimizableSubtreeRoot(aNode);
   1438  if (!root) {
   1439    return false;
   1440  }
   1441 
   1442  // Subtree has been traversed already.
   1443  if (root->CCMarkedRoot()) {
   1444    return root->InCCBlackTree() && !NeedsScriptTraverse(aNode);
   1445  }
   1446 
   1447  if (!gCCBlackMarkedNodes) {
   1448    gCCBlackMarkedNodes = new nsTHashSet<nsINode*>(1020);
   1449  }
   1450 
   1451  // nodesToUnpurple contains nodes which will be removed
   1452  // from the purple buffer if the DOM tree is known-live.
   1453  AutoTArray<nsIContent*, 1020> nodesToUnpurple;
   1454  // grayNodes need script traverse, so they aren't removed from
   1455  // the purple buffer, but are marked to be in known-live subtree so that
   1456  // traverse is faster.
   1457  AutoTArray<nsINode*, 1020> grayNodes;
   1458 
   1459  bool foundLiveWrapper = root->HasKnownLiveWrapper();
   1460  if (root != currentDoc) {
   1461    currentDoc = nullptr;
   1462    if (NeedsScriptTraverse(root)) {
   1463      grayNodes.AppendElement(root);
   1464    } else if (static_cast<nsIContent*>(root)->IsPurple()) {
   1465      nodesToUnpurple.AppendElement(static_cast<nsIContent*>(root));
   1466    }
   1467  }
   1468 
   1469  // Traverse the subtree and check if we could know without CC
   1470  // that it is known-live.
   1471  // Note, this traverse is non-virtual and inline, so it should be a lot faster
   1472  // than CC's generic traverse.
   1473  for (nsIContent* node = root->GetFirstChild(); node;
   1474       node = node->GetNextNode(root)) {
   1475    foundLiveWrapper = foundLiveWrapper || node->HasKnownLiveWrapper();
   1476    if (foundLiveWrapper && currentDoc) {
   1477      // If we can mark the whole document known-live, no need to optimize
   1478      // so much, since when the next purple node in the document will be
   1479      // handled, it is fast to check that currentDoc is in CCGeneration.
   1480      break;
   1481    }
   1482    if (NeedsScriptTraverse(node)) {
   1483      // Gray nodes need real CC traverse.
   1484      grayNodes.AppendElement(node);
   1485    } else if (node->IsPurple()) {
   1486      nodesToUnpurple.AppendElement(node);
   1487    }
   1488  }
   1489 
   1490  root->SetCCMarkedRoot(true);
   1491  root->SetInCCBlackTree(foundLiveWrapper);
   1492  gCCBlackMarkedNodes->Insert(root);
   1493 
   1494  if (!foundLiveWrapper) {
   1495    return false;
   1496  }
   1497 
   1498  if (currentDoc) {
   1499    // Special case documents. If we know the document is known-live,
   1500    // we can mark the document to be in CCGeneration.
   1501    currentDoc->MarkUncollectableForCCGeneration(
   1502        nsCCUncollectableMarker::sGeneration);
   1503  } else {
   1504    for (uint32_t i = 0; i < grayNodes.Length(); ++i) {
   1505      nsINode* node = grayNodes[i];
   1506      node->SetInCCBlackTree(true);
   1507      gCCBlackMarkedNodes->Insert(node);
   1508    }
   1509  }
   1510 
   1511  // Subtree is known-live, we can remove non-gray purple nodes from
   1512  // purple buffer.
   1513  for (uint32_t i = 0; i < nodesToUnpurple.Length(); ++i) {
   1514    nsIContent* purple = nodesToUnpurple[i];
   1515    // Can't remove currently handled purple node.
   1516    if (purple != aNode) {
   1517      purple->RemovePurple();
   1518    }
   1519  }
   1520  return !NeedsScriptTraverse(aNode);
   1521 }
   1522 
   1523 AutoTArray<nsINode*, 1020>* gPurpleRoots = nullptr;
   1524 AutoTArray<nsIContent*, 1020>* gNodesToUnbind = nullptr;
   1525 
   1526 void ClearCycleCollectorCleanupData() {
   1527  if (gPurpleRoots) {
   1528    uint32_t len = gPurpleRoots->Length();
   1529    for (uint32_t i = 0; i < len; ++i) {
   1530      nsINode* n = gPurpleRoots->ElementAt(i);
   1531      n->SetIsPurpleRoot(false);
   1532    }
   1533    delete gPurpleRoots;
   1534    gPurpleRoots = nullptr;
   1535  }
   1536  if (gNodesToUnbind) {
   1537    uint32_t len = gNodesToUnbind->Length();
   1538    for (uint32_t i = 0; i < len; ++i) {
   1539      nsIContent* c = gNodesToUnbind->ElementAt(i);
   1540      c->SetIsPurpleRoot(false);
   1541      ContentUnbinder::Append(c);
   1542    }
   1543    delete gNodesToUnbind;
   1544    gNodesToUnbind = nullptr;
   1545  }
   1546 }
   1547 
   1548 static bool ShouldClearPurple(nsIContent* aContent) {
   1549  MOZ_ASSERT(aContent);
   1550  if (aContent->IsPurple()) {
   1551    return true;
   1552  }
   1553 
   1554  JSObject* o = GetJSObjectChild(aContent);
   1555  if (o && JS::ObjectIsMarkedGray(o)) {
   1556    return true;
   1557  }
   1558 
   1559  if (aContent->HasListenerManager()) {
   1560    return true;
   1561  }
   1562 
   1563  return aContent->HasProperties();
   1564 }
   1565 
   1566 // If aNode is not optimizable, but is an element
   1567 // with a frame in a document which has currently active presshell,
   1568 // we can act as if it was optimizable. When the primary frame dies, aNode
   1569 // will end up to the purple buffer because of the refcount change.
   1570 bool NodeHasActiveFrame(Document* aCurrentDoc, nsINode* aNode) {
   1571  return aCurrentDoc->GetPresShell() && aNode->IsElement() &&
   1572         aNode->AsElement()->GetPrimaryFrame();
   1573 }
   1574 
   1575 // CanSkip checks if aNode is known-live, and if it is, returns true. If aNode
   1576 // is in a known-live DOM tree, CanSkip may also remove other objects from
   1577 // purple buffer and unmark event listeners and user data.  If the root of the
   1578 // DOM tree is a document, less optimizations are done since checking the
   1579 // liveness of the current document is usually fast and we don't want slow down
   1580 // such common cases.
   1581 bool FragmentOrElement::CanSkip(nsINode* aNode, bool aRemovingAllowed) {
   1582  // Don't try to optimize anything during shutdown.
   1583  if (nsCCUncollectableMarker::sGeneration == 0) {
   1584    return false;
   1585  }
   1586 
   1587  bool unoptimizable = aNode->UnoptimizableCCNode();
   1588  Document* currentDoc = aNode->GetComposedDoc();
   1589  if (currentDoc && IsCertainlyAliveNode(aNode, currentDoc) &&
   1590      (!unoptimizable || NodeHasActiveFrame(currentDoc, aNode))) {
   1591    MarkNodeChildren(aNode);
   1592    return true;
   1593  }
   1594 
   1595  if (unoptimizable) {
   1596    return false;
   1597  }
   1598 
   1599  nsINode* root = currentDoc ? static_cast<nsINode*>(currentDoc)
   1600                             : FindOptimizableSubtreeRoot(aNode);
   1601  if (!root) {
   1602    return false;
   1603  }
   1604 
   1605  // Subtree has been traversed already, and aNode has
   1606  // been handled in a way that doesn't require revisiting it.
   1607  if (root->IsPurpleRoot()) {
   1608    return false;
   1609  }
   1610 
   1611  // nodesToClear contains nodes which are either purple or
   1612  // gray.
   1613  AutoTArray<nsIContent*, 1020> nodesToClear;
   1614 
   1615  bool foundLiveWrapper = root->HasKnownLiveWrapper();
   1616  bool domOnlyCycle = false;
   1617  if (root != currentDoc) {
   1618    currentDoc = nullptr;
   1619    if (!foundLiveWrapper) {
   1620      domOnlyCycle = static_cast<nsIContent*>(root)->OwnedOnlyByTheDOMTree();
   1621    }
   1622    if (ShouldClearPurple(static_cast<nsIContent*>(root))) {
   1623      nodesToClear.AppendElement(static_cast<nsIContent*>(root));
   1624    }
   1625  }
   1626 
   1627  // Traverse the subtree and check if we could know without CC
   1628  // that it is known-live.
   1629  // Note, this traverse is non-virtual and inline, so it should be a lot faster
   1630  // than CC's generic traverse.
   1631  for (nsIContent* node = root->GetFirstChild(); node;
   1632       node = node->GetNextNode(root)) {
   1633    foundLiveWrapper = foundLiveWrapper || node->HasKnownLiveWrapper();
   1634    if (foundLiveWrapper) {
   1635      domOnlyCycle = false;
   1636      if (currentDoc) {
   1637        // If we can mark the whole document live, no need to optimize
   1638        // so much, since when the next purple node in the document will be
   1639        // handled, it is fast to check that the currentDoc is in CCGeneration.
   1640        break;
   1641      }
   1642      // No need to put stuff to the nodesToClear array, if we can clear it
   1643      // already here.
   1644      if (node->IsPurple() && (node != aNode || aRemovingAllowed)) {
   1645        node->RemovePurple();
   1646      }
   1647      MarkNodeChildren(node);
   1648    } else {
   1649      domOnlyCycle = domOnlyCycle && node->OwnedOnlyByTheDOMTree();
   1650      if (ShouldClearPurple(node)) {
   1651        // Collect interesting nodes which we can clear if we find that
   1652        // they are kept alive in a known-live tree or are in a DOM-only cycle.
   1653        nodesToClear.AppendElement(node);
   1654      }
   1655    }
   1656  }
   1657 
   1658  if (!currentDoc || !foundLiveWrapper) {
   1659    root->SetIsPurpleRoot(true);
   1660    if (domOnlyCycle) {
   1661      if (!gNodesToUnbind) {
   1662        gNodesToUnbind = new AutoTArray<nsIContent*, 1020>();
   1663      }
   1664      gNodesToUnbind->AppendElement(static_cast<nsIContent*>(root));
   1665      for (uint32_t i = 0; i < nodesToClear.Length(); ++i) {
   1666        nsIContent* n = nodesToClear[i];
   1667        if ((n != aNode || aRemovingAllowed) && n->IsPurple()) {
   1668          n->RemovePurple();
   1669        }
   1670      }
   1671      return true;
   1672    } else {
   1673      if (!gPurpleRoots) {
   1674        gPurpleRoots = new AutoTArray<nsINode*, 1020>();
   1675      }
   1676      gPurpleRoots->AppendElement(root);
   1677    }
   1678  }
   1679 
   1680  if (!foundLiveWrapper) {
   1681    return false;
   1682  }
   1683 
   1684  if (currentDoc) {
   1685    // Special case documents. If we know the document is known-live,
   1686    // we can mark the document to be in CCGeneration.
   1687    currentDoc->MarkUncollectableForCCGeneration(
   1688        nsCCUncollectableMarker::sGeneration);
   1689    MarkNodeChildren(currentDoc);
   1690  }
   1691 
   1692  // Subtree is known-live, so we can remove purple nodes from
   1693  // purple buffer and mark stuff that to be certainly alive.
   1694  for (uint32_t i = 0; i < nodesToClear.Length(); ++i) {
   1695    nsIContent* n = nodesToClear[i];
   1696    MarkNodeChildren(n);
   1697    // Can't remove currently handled purple node,
   1698    // unless aRemovingAllowed is true.
   1699    if ((n != aNode || aRemovingAllowed) && n->IsPurple()) {
   1700      n->RemovePurple();
   1701    }
   1702  }
   1703  return true;
   1704 }
   1705 
   1706 bool FragmentOrElement::CanSkipThis(nsINode* aNode) {
   1707  if (nsCCUncollectableMarker::sGeneration == 0) {
   1708    return false;
   1709  }
   1710  if (aNode->HasKnownLiveWrapper()) {
   1711    return true;
   1712  }
   1713  Document* c = aNode->GetComposedDoc();
   1714  return ((c && IsCertainlyAliveNode(aNode, c)) || aNode->InCCBlackTree()) &&
   1715         !NeedsScriptTraverse(aNode);
   1716 }
   1717 
   1718 void FragmentOrElement::InitCCCallbacks() {
   1719  nsCycleCollector_setForgetSkippableCallback(ClearCycleCollectorCleanupData);
   1720  nsCycleCollector_setBeforeUnlinkCallback(ClearBlackMarkedNodes);
   1721 }
   1722 
   1723 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(FragmentOrElement)
   1724  return FragmentOrElement::CanSkip(tmp, aRemovingAllowed);
   1725 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
   1726 
   1727 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(FragmentOrElement)
   1728  return FragmentOrElement::CanSkipInCC(tmp);
   1729 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
   1730 
   1731 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(FragmentOrElement)
   1732  return FragmentOrElement::CanSkipThis(tmp);
   1733 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
   1734 
   1735 // We purposefully don't TRAVERSE_BEGIN_INHERITED here.  All the bits
   1736 // we should traverse should be added here or in nsINode::Traverse.
   1737 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(FragmentOrElement)
   1738  if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
   1739    char name[512];
   1740    uint32_t nsid = tmp->GetNameSpaceID();
   1741    nsAtomCString localName(tmp->NodeInfo()->NameAtom());
   1742    nsAutoCString uri;
   1743    if (tmp->OwnerDoc()->GetDocumentURI()) {
   1744      uri = tmp->OwnerDoc()->GetDocumentURI()->GetSpecOrDefault();
   1745    }
   1746 
   1747    nsAutoString id;
   1748    nsAtom* idAtom = tmp->GetID();
   1749    if (idAtom) {
   1750      id.AppendLiteral(" id='");
   1751      id.Append(nsDependentAtomString(idAtom));
   1752      id.Append('\'');
   1753    }
   1754 
   1755    nsAutoString classes;
   1756    const nsAttrValue* classAttrValue =
   1757        tmp->IsElement() ? tmp->AsElement()->GetClasses() : nullptr;
   1758    if (classAttrValue) {
   1759      classes.AppendLiteral(" class='");
   1760      nsAutoString classString;
   1761      classAttrValue->ToString(classString);
   1762      classString.ReplaceChar(char16_t('\n'), char16_t(' '));
   1763      classes.Append(classString);
   1764      classes.Append('\'');
   1765    }
   1766 
   1767    nsAutoCString orphan;
   1768    if (!tmp->IsInComposedDoc()) {
   1769      orphan.AppendLiteral(" (orphan)");
   1770    }
   1771 
   1772    const char* nsuri = nsNameSpaceManager::GetNameSpaceDisplayName(nsid);
   1773    SprintfLiteral(name, "FragmentOrElement %s %s%s%s%s %s", nsuri,
   1774                   localName.get(), NS_ConvertUTF16toUTF8(id).get(),
   1775                   NS_ConvertUTF16toUTF8(classes).get(), orphan.get(),
   1776                   uri.get());
   1777    cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
   1778  } else {
   1779    NS_IMPL_CYCLE_COLLECTION_DESCRIBE(FragmentOrElement, tmp->mRefCnt.get())
   1780  }
   1781 
   1782  if (!nsIContent::Traverse(tmp, cb)) {
   1783    return NS_SUCCESS_INTERRUPTED_TRAVERSE;
   1784  }
   1785  if (tmp->IsElement()) {
   1786    Element* element = tmp->AsElement();
   1787    // Traverse attribute names.
   1788    uint32_t i;
   1789    uint32_t attrs = element->GetAttrCount();
   1790    for (i = 0; i < attrs; i++) {
   1791      const nsAttrName* name = element->GetUnsafeAttrNameAt(i);
   1792      if (!name->IsAtom()) {
   1793        NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mAttrs[i]->NodeInfo()");
   1794        cb.NoteNativeChild(name->NodeInfo(),
   1795                           NS_CYCLE_COLLECTION_PARTICIPANT(NodeInfo));
   1796      }
   1797    }
   1798  }
   1799 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   1800 
   1801 NS_INTERFACE_MAP_BEGIN(FragmentOrElement)
   1802  NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(FragmentOrElement)
   1803 NS_INTERFACE_MAP_END_INHERITING(nsIContent)
   1804 
   1805 //----------------------------------------------------------------------
   1806 
   1807 const CharacterDataBuffer* FragmentOrElement::GetCharacterDataBuffer() const {
   1808  return nullptr;
   1809 }
   1810 
   1811 uint32_t FragmentOrElement::TextLength() const {
   1812  // We can remove this assertion if it turns out to be useful to be able
   1813  // to depend on this returning 0
   1814  MOZ_ASSERT_UNREACHABLE("called FragmentOrElement::TextLength");
   1815 
   1816  return 0;
   1817 }
   1818 
   1819 bool FragmentOrElement::TextIsOnlyWhitespace() { return false; }
   1820 
   1821 bool FragmentOrElement::ThreadSafeTextIsOnlyWhitespace() const { return false; }
   1822 
   1823 static inline bool IsVoidTag(const nsAtom* aTag) {
   1824  static const nsAtom* voidElements[] = {
   1825      nsGkAtoms::area,    nsGkAtoms::base,  nsGkAtoms::basefont,
   1826      nsGkAtoms::bgsound, nsGkAtoms::br,    nsGkAtoms::col,
   1827      nsGkAtoms::embed,   nsGkAtoms::frame, nsGkAtoms::hr,
   1828      nsGkAtoms::img,     nsGkAtoms::input, nsGkAtoms::keygen,
   1829      nsGkAtoms::link,    nsGkAtoms::meta,  nsGkAtoms::param,
   1830      nsGkAtoms::source,  nsGkAtoms::track, nsGkAtoms::wbr};
   1831 
   1832  static mozilla::BitBloomFilter<12, nsAtom> sFilter;
   1833  static bool sInitialized = false;
   1834  if (!sInitialized) {
   1835    sInitialized = true;
   1836    for (uint32_t i = 0; i < std::size(voidElements); ++i) {
   1837      sFilter.add(voidElements[i]);
   1838    }
   1839  }
   1840 
   1841  if (sFilter.mightContain(aTag)) {
   1842    for (uint32_t i = 0; i < std::size(voidElements); ++i) {
   1843      if (aTag == voidElements[i]) {
   1844        return true;
   1845      }
   1846    }
   1847  }
   1848  return false;
   1849 }
   1850 
   1851 /* static */
   1852 bool FragmentOrElement::IsHTMLVoid(const nsAtom* aLocalName) {
   1853  return aLocalName && IsVoidTag(aLocalName);
   1854 }
   1855 
   1856 void FragmentOrElement::GetMarkup(bool aIncludeSelf, nsAString& aMarkup) {
   1857  aMarkup.Truncate();
   1858 
   1859  Document* doc = OwnerDoc();
   1860  if (IsInHTMLDocument()) {
   1861    nsContentUtils::SerializeNodeToMarkup(this, !aIncludeSelf, aMarkup, false,
   1862                                          {});
   1863    return;
   1864  }
   1865 
   1866  nsAutoString contentType;
   1867  doc->GetContentType(contentType);
   1868  bool tryToCacheEncoder = !aIncludeSelf;
   1869 
   1870  nsCOMPtr<nsIDocumentEncoder> docEncoder = doc->GetCachedEncoder();
   1871  if (!docEncoder) {
   1872    docEncoder = do_createDocumentEncoder(
   1873        PromiseFlatCString(NS_ConvertUTF16toUTF8(contentType)).get());
   1874  }
   1875  if (!docEncoder) {
   1876    // This could be some type for which we create a synthetic document.  Try
   1877    // again as XML
   1878    contentType.AssignLiteral("application/xml");
   1879    docEncoder = do_createDocumentEncoder("application/xml");
   1880    // Don't try to cache the encoder since it would point to a different
   1881    // contentType once it has been reinitialized.
   1882    tryToCacheEncoder = false;
   1883  }
   1884 
   1885  NS_ENSURE_TRUE_VOID(docEncoder);
   1886 
   1887  uint32_t flags = nsIDocumentEncoder::OutputEncodeBasicEntities |
   1888                   // Output DOM-standard newlines
   1889                   nsIDocumentEncoder::OutputLFLineBreak |
   1890                   // Don't do linebreaking that's not present in
   1891                   // the source
   1892                   nsIDocumentEncoder::OutputRaw |
   1893                   // Only check for mozdirty when necessary (bug 599983)
   1894                   nsIDocumentEncoder::OutputIgnoreMozDirty;
   1895 
   1896  if (IsEditable()) {
   1897    nsCOMPtr<Element> elem = do_QueryInterface(this);
   1898    TextEditor* textEditor = elem ? elem->GetTextEditorInternal() : nullptr;
   1899    if (textEditor && textEditor->OutputsMozDirty()) {
   1900      flags &= ~nsIDocumentEncoder::OutputIgnoreMozDirty;
   1901    }
   1902  }
   1903 
   1904  DebugOnly<nsresult> rv = docEncoder->Init(doc, contentType, flags);
   1905  MOZ_ASSERT(NS_SUCCEEDED(rv));
   1906 
   1907  if (aIncludeSelf) {
   1908    docEncoder->SetNode(this);
   1909  } else {
   1910    docEncoder->SetContainerNode(this);
   1911  }
   1912  rv = docEncoder->EncodeToString(aMarkup);
   1913  MOZ_ASSERT(NS_SUCCEEDED(rv));
   1914  if (tryToCacheEncoder) {
   1915    doc->SetCachedEncoder(docEncoder.forget());
   1916  }
   1917 }
   1918 
   1919 static bool ContainsMarkup(const nsAString& aStr) {
   1920  // Note: we can't use FindCharInSet because null is one of the characters we
   1921  // want to search for.
   1922  const char16_t* start = aStr.BeginReading();
   1923  const char16_t* end = aStr.EndReading();
   1924 
   1925 #ifdef MOZ_MAY_HAVE_HTMLACCEL
   1926  if (mozilla::htmlaccel::htmlaccelEnabled()) {
   1927    // We need to check for the empty string in order to
   1928    // dereference `start` for the '<' check. We might as well
   1929    // check that we have a full SIMD stride.
   1930    if (end - start >= 16) {
   1931      // Optimize the case where the input starts with a tag.
   1932      if (*start == u'<') {
   1933        return true;
   1934      }
   1935      // Curiously, this doesn't look like much of an optimization on Zen 3,
   1936      // but since it is an optimization on M3 Pro and Skylake, let's do this.
   1937      return mozilla::htmlaccel::ContainsMarkup(start, end);
   1938    }
   1939  }
   1940 #endif
   1941 
   1942  while (start != end) {
   1943    char16_t c = *start;
   1944    if (c == char16_t('<') || c == char16_t('&') || c == char16_t('\r') ||
   1945        c == char16_t('\0')) {
   1946      return true;
   1947    }
   1948    ++start;
   1949  }
   1950 
   1951  return false;
   1952 }
   1953 
   1954 void FragmentOrElement::SetInnerHTMLInternal(const nsAString& aInnerHTML,
   1955                                             ErrorResult& aError) {
   1956  // Keep "this" alive should be guaranteed by the caller, and also the content
   1957  // of a template element (if this is one) should never been released by from
   1958  // this during this call.  Therefore, using raw pointer here is safe.
   1959  FragmentOrElement* target = this;
   1960  // Handle template case.
   1961  if (target->IsTemplateElement()) {
   1962    DocumentFragment* frag =
   1963        static_cast<HTMLTemplateElement*>(target)->Content();
   1964    MOZ_ASSERT(frag);
   1965    target = frag;
   1966  }
   1967  // Fast-path for strings with no markup. Limit this to short strings, to
   1968  // avoid ContainsMarkup taking too long. The choice for 100 is based on
   1969  // gut feeling.
   1970  //
   1971  // Don't do this for elements with a weird parser insertion mode, for
   1972  // instance setting innerHTML = "" on a <html> element should add the
   1973  // optional <head> and <body> elements.
   1974  if (!target->HasWeirdParserInsertionMode() && aInnerHTML.Length() < 100 &&
   1975      !ContainsMarkup(aInnerHTML)) {
   1976    aError = nsContentUtils::SetNodeTextContent(target, aInnerHTML, false);
   1977    return;
   1978  }
   1979 
   1980  const RefPtr<Document> doc = target->OwnerDoc();
   1981 
   1982  target->NotifyDevToolsOfRemovalsOfChildren();
   1983 
   1984  // Needed when innerHTML is used in combination with contenteditable
   1985  mozAutoDocUpdate updateBatch(doc, true);
   1986 
   1987  // Remove childnodes.
   1988  nsAutoMutationBatch mb(target, true, false);
   1989  target->RemoveAllChildren(true);
   1990  mb.RemovalDone();
   1991 
   1992  nsAutoScriptLoaderDisabler sld(doc);
   1993 
   1994  FragmentOrElement* parseContext = this;
   1995  if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(this)) {
   1996    // Fix up the context to be the host of the ShadowRoot.  See
   1997    // https://w3c.github.io/DOM-Parsing/#dom-innerhtml-innerhtml setter step 1.
   1998    parseContext = shadowRoot->GetHost();
   1999  }
   2000 
   2001  if (doc->IsHTMLDocument()) {
   2002    doc->SuspendDOMNotifications();
   2003    nsAtom* contextLocalName = parseContext->NodeInfo()->NameAtom();
   2004    int32_t contextNameSpaceID = parseContext->GetNameSpaceID();
   2005 
   2006    aError = nsContentUtils::ParseFragmentHTML(
   2007        aInnerHTML, target, contextLocalName, contextNameSpaceID,
   2008        doc->GetCompatibilityMode() == eCompatibility_NavQuirks, true);
   2009    doc->ResumeDOMNotifications();
   2010    if (target->GetFirstChild()) {
   2011      MutationObservers::NotifyContentAppended(target, target->GetFirstChild(),
   2012                                               {});
   2013    }
   2014    mb.NodesAdded();
   2015  } else {
   2016    RefPtr<DocumentFragment> df = nsContentUtils::CreateContextualFragment(
   2017        parseContext, aInnerHTML, true, aError);
   2018    if (!aError.Failed()) {
   2019      // Suppress assertion about node removal mutation events that can't have
   2020      // listeners anyway, because no one has had the chance to register
   2021      // mutation listeners on the fragment that comes from the parser.
   2022      nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
   2023 
   2024      target->AppendChild(*df, aError);
   2025      mb.NodesAdded();
   2026    }
   2027  }
   2028 }
   2029 
   2030 void FragmentOrElement::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
   2031                                               size_t* aNodeSize) const {
   2032  nsIContent::AddSizeOfExcludingThis(aSizes, aNodeSize);
   2033 
   2034  nsDOMSlots* slots = GetExistingDOMSlots();
   2035  if (slots) {
   2036    *aNodeSize += slots->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf);
   2037  }
   2038 }