tor-browser

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

DocumentOrShadowRoot.cpp (26185B)


      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 #include "DocumentOrShadowRoot.h"
      8 
      9 #include "mozilla/AnimationComparator.h"
     10 #include "mozilla/EventStateManager.h"
     11 #include "mozilla/PointerLockManager.h"
     12 #include "mozilla/PresShell.h"
     13 #include "mozilla/StyleSheet.h"
     14 #include "mozilla/dom/AnimatableBinding.h"
     15 #include "mozilla/dom/Document.h"
     16 #include "mozilla/dom/HTMLInputElement.h"
     17 #include "mozilla/dom/ShadowRoot.h"
     18 #include "mozilla/dom/StyleSheetList.h"
     19 #include "nsContentUtils.h"
     20 #include "nsFocusManager.h"
     21 #include "nsIFormControl.h"
     22 #include "nsLayoutUtils.h"
     23 #include "nsNameSpaceManager.h"
     24 #include "nsTHashtable.h"
     25 #include "nsWindowSizes.h"
     26 
     27 namespace mozilla::dom {
     28 
     29 DocumentOrShadowRoot::DocumentOrShadowRoot(ShadowRoot* aShadowRoot)
     30    : mAsNode(aShadowRoot), mKind(Kind::ShadowRoot) {
     31  MOZ_ASSERT(mAsNode);
     32 }
     33 
     34 DocumentOrShadowRoot::DocumentOrShadowRoot(Document* aDoc)
     35    : mAsNode(aDoc), mKind(Kind::Document) {
     36  MOZ_ASSERT(mAsNode);
     37 }
     38 
     39 void DocumentOrShadowRoot::AddSizeOfOwnedSheetArrayExcludingThis(
     40    nsWindowSizes& aSizes, const nsTArray<RefPtr<StyleSheet>>& aSheets) const {
     41  size_t n = 0;
     42  n += aSheets.ShallowSizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
     43  for (StyleSheet* sheet : aSheets) {
     44    if (!sheet->GetAssociatedDocumentOrShadowRoot()) {
     45      // Avoid over-reporting shared sheets.
     46      continue;
     47    }
     48    n += sheet->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf);
     49  }
     50 
     51  if (mKind == Kind::ShadowRoot) {
     52    aSizes.mLayoutShadowDomStyleSheetsSize += n;
     53  } else {
     54    aSizes.mLayoutStyleSheetsSize += n;
     55  }
     56 }
     57 
     58 void DocumentOrShadowRoot::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
     59  AddSizeOfOwnedSheetArrayExcludingThis(aSizes, mStyleSheets);
     60  aSizes.mDOMSizes.mDOMOtherSize +=
     61      mIdentifierMap.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
     62 }
     63 
     64 DocumentOrShadowRoot::~DocumentOrShadowRoot() {
     65  for (StyleSheet* sheet : mStyleSheets) {
     66    sheet->ClearAssociatedDocumentOrShadowRoot();
     67  }
     68 }
     69 
     70 StyleSheetList* DocumentOrShadowRoot::StyleSheets() {
     71  if (!mDOMStyleSheets) {
     72    mDOMStyleSheets = new StyleSheetList(*this);
     73  }
     74  return mDOMStyleSheets;
     75 }
     76 
     77 void DocumentOrShadowRoot::InsertSheetAt(size_t aIndex, StyleSheet& aSheet) {
     78  aSheet.SetAssociatedDocumentOrShadowRoot(this);
     79  mStyleSheets.InsertElementAt(aIndex, &aSheet);
     80 }
     81 
     82 void DocumentOrShadowRoot::RemoveStyleSheet(StyleSheet& aSheet) {
     83  auto index = mStyleSheets.IndexOf(&aSheet);
     84  if (index == mStyleSheets.NoIndex) {
     85    // We should only hit this case if we are unlinking
     86    // in which case mStyleSheets should be cleared.
     87    MOZ_ASSERT(mKind != Kind::Document ||
     88               AsNode().AsDocument()->InUnlinkOrDeletion());
     89    MOZ_ASSERT(mStyleSheets.IsEmpty());
     90    return;
     91  }
     92  RefPtr<StyleSheet> sheet = std::move(mStyleSheets[index]);
     93  mStyleSheets.RemoveElementAt(index);
     94  RemoveSheetFromStylesIfApplicable(*sheet);
     95  sheet->ClearAssociatedDocumentOrShadowRoot();
     96  AsNode().OwnerDoc()->PostStyleSheetRemovedEvent(aSheet);
     97 }
     98 
     99 void DocumentOrShadowRoot::RemoveSheetFromStylesIfApplicable(
    100    StyleSheet& aSheet) {
    101  if (!aSheet.IsApplicable()) {
    102    return;
    103  }
    104  if (mKind == Kind::Document) {
    105    AsNode().AsDocument()->RemoveStyleSheetFromStyleSets(aSheet);
    106  } else {
    107    MOZ_ASSERT(AsNode().IsShadowRoot());
    108    static_cast<ShadowRoot&>(AsNode()).RemoveSheetFromStyles(aSheet);
    109  }
    110 }
    111 
    112 // https://drafts.csswg.org/cssom/#dom-documentorshadowroot-adoptedstylesheets
    113 void DocumentOrShadowRoot::OnSetAdoptedStyleSheets(StyleSheet& aSheet,
    114                                                   uint32_t aIndex,
    115                                                   ErrorResult& aRv) {
    116  Document& doc = *AsNode().OwnerDoc();
    117  // 1. If value’s constructed flag is not set, or its constructor document is
    118  // not equal to this DocumentOrShadowRoot's node document, throw a
    119  // "NotAllowedError" DOMException.
    120 
    121  if (!StaticPrefs::
    122          dom_webcomponents_lift_adoptedstylesheets_restriction_enabled()) {
    123    if (!aSheet.IsConstructed()) {
    124      return aRv.ThrowNotAllowedError(
    125          "Adopted style sheet must be created through the Constructable "
    126          "StyleSheets API");
    127    }
    128 
    129    if (!aSheet.ConstructorDocumentMatches(doc)) {
    130      return aRv.ThrowNotAllowedError(
    131          "Adopted style sheet's constructor document must match the "
    132          "document or shadow root's node document");
    133    }
    134  }
    135 
    136  auto* shadow = ShadowRoot::FromNode(AsNode());
    137  MOZ_ASSERT((mKind == Kind::ShadowRoot) == !!shadow);
    138 
    139  auto existingIndex = mAdoptedStyleSheets.LastIndexOf(&aSheet);
    140  // Ensure it's in the backing array at the right index.
    141  mAdoptedStyleSheets.InsertElementAt(aIndex, &aSheet);
    142  if (existingIndex == mAdoptedStyleSheets.NoIndex) {
    143    // common case: we're not already adopting this sheet.
    144    aSheet.AddAdopter(*this);
    145  } else if (existingIndex < aIndex) {
    146    // We're inserting an already-adopted stylesheet in a later position, so
    147    // this one should take precedent and we should remove the old one.
    148    RemoveSheetFromStylesIfApplicable(aSheet);
    149  } else {
    150    // The sheet is already at a position later than or equal to the current
    151    // one, and is already adopted by us, we have nothing to do here other than
    152    // adding to the current list.
    153    return;
    154  }
    155 
    156  if (aSheet.IsApplicable()) {
    157    if (mKind == Kind::Document) {
    158      doc.AddStyleSheetToStyleSets(aSheet);
    159    } else {
    160      shadow->InsertSheetIntoAuthorData(aIndex, aSheet, mAdoptedStyleSheets);
    161    }
    162  }
    163 }
    164 
    165 void DocumentOrShadowRoot::OnDeleteAdoptedStyleSheets(StyleSheet& aSheet,
    166                                                      uint32_t aIndex,
    167                                                      ErrorResult&) {
    168  MOZ_ASSERT(mAdoptedStyleSheets.ElementAt(aIndex) == &aSheet);
    169  mAdoptedStyleSheets.RemoveElementAt(aIndex);
    170  auto existingIndex = mAdoptedStyleSheets.LastIndexOf(&aSheet);
    171  if (existingIndex != mAdoptedStyleSheets.NoIndex && existingIndex >= aIndex) {
    172    // The sheet is still adopted by us and was already later from the one we're
    173    // removing, so nothing to do.
    174    return;
    175  }
    176 
    177  RemoveSheetFromStylesIfApplicable(aSheet);
    178  if (existingIndex == mAdoptedStyleSheets.NoIndex) {
    179    // The sheet is no longer adopted by us.
    180    aSheet.RemoveAdopter(*this);
    181  } else if (aSheet.IsApplicable()) {
    182    // We need to re-insert the sheet at the right (pre-existing) index.
    183    nsINode& node = AsNode();
    184    if (mKind == Kind::Document) {
    185      node.AsDocument()->AddStyleSheetToStyleSets(aSheet);
    186    } else {
    187      ShadowRoot::FromNode(node)->InsertSheetIntoAuthorData(
    188          existingIndex, aSheet, mAdoptedStyleSheets);
    189    }
    190  }
    191 }
    192 
    193 void DocumentOrShadowRoot::ClearAdoptedStyleSheets() {
    194  auto* shadow = ShadowRoot::FromNode(AsNode());
    195  auto* doc = shadow ? nullptr : AsNode().AsDocument();
    196  MOZ_ASSERT(shadow || doc);
    197  IgnoredErrorResult rv;
    198  while (!mAdoptedStyleSheets.IsEmpty()) {
    199    if (shadow) {
    200      ShadowRoot_Binding::AdoptedStyleSheetsHelpers::RemoveLastElement(shadow,
    201                                                                       rv);
    202    } else {
    203      Document_Binding::AdoptedStyleSheetsHelpers::RemoveLastElement(doc, rv);
    204    }
    205    MOZ_DIAGNOSTIC_ASSERT(!rv.Failed(), "Removal doesn't fail");
    206  }
    207 }
    208 
    209 void DocumentOrShadowRoot::CloneAdoptedSheetsFrom(
    210    const DocumentOrShadowRoot& aSource) {
    211  if (aSource.mAdoptedStyleSheets.IsEmpty()) {
    212    return;
    213  }
    214 
    215  Document& ownerDoc = *AsNode().OwnerDoc();
    216  const Document& sourceDoc = *aSource.AsNode().OwnerDoc();
    217  auto* clonedSheetMap = static_cast<Document::AdoptedStyleSheetCloneCache*>(
    218      sourceDoc.GetProperty(nsGkAtoms::adoptedsheetclones));
    219  MOZ_ASSERT(clonedSheetMap);
    220 
    221  // We don't need to care about the reflector (AdoptedStyleSheetsHelpers and
    222  // so) because this is only used for static documents.
    223  for (const StyleSheet* sheet : aSource.mAdoptedStyleSheets) {
    224    RefPtr<StyleSheet> clone = clonedSheetMap->LookupOrInsertWith(
    225        sheet, [&] { return sheet->CloneAdoptedSheet(ownerDoc); });
    226    MOZ_ASSERT(clone);
    227    MOZ_DIAGNOSTIC_ASSERT(clone->ConstructorDocumentMatches(ownerDoc));
    228    ErrorResult rv;
    229    OnSetAdoptedStyleSheets(*clone, mAdoptedStyleSheets.Length(), rv);
    230    MOZ_ASSERT(!rv.Failed());
    231  }
    232 }
    233 
    234 Element* DocumentOrShadowRoot::GetElementById(
    235    const nsAString& aElementId) const {
    236  if (MOZ_UNLIKELY(aElementId.IsEmpty())) {
    237    ReportEmptyGetElementByIdArg();
    238    return nullptr;
    239  }
    240 
    241  if (IdentifierMapEntry* entry = mIdentifierMap.GetEntry(aElementId)) {
    242    return entry->GetIdElement();
    243  }
    244 
    245  return nullptr;
    246 }
    247 
    248 Element* DocumentOrShadowRoot::GetElementById(nsAtom* aElementId) const {
    249  if (MOZ_UNLIKELY(aElementId == nsGkAtoms::_empty)) {
    250    ReportEmptyGetElementByIdArg();
    251    return nullptr;
    252  }
    253 
    254  if (IdentifierMapEntry* entry = mIdentifierMap.GetEntry(aElementId)) {
    255    return entry->GetIdElement();
    256  }
    257 
    258  return nullptr;
    259 }
    260 
    261 already_AddRefed<nsContentList> DocumentOrShadowRoot::GetElementsByTagNameNS(
    262    const nsAString& aNamespaceURI, const nsAString& aLocalName) {
    263  ErrorResult rv;
    264  RefPtr<nsContentList> list =
    265      GetElementsByTagNameNS(aNamespaceURI, aLocalName, rv);
    266  if (rv.Failed()) {
    267    return nullptr;
    268  }
    269  return list.forget();
    270 }
    271 
    272 already_AddRefed<nsContentList> DocumentOrShadowRoot::GetElementsByTagNameNS(
    273    const nsAString& aNamespaceURI, const nsAString& aLocalName,
    274    ErrorResult& aResult) {
    275  int32_t nameSpaceId = kNameSpaceID_Wildcard;
    276 
    277  if (!aNamespaceURI.EqualsLiteral("*")) {
    278    aResult = nsNameSpaceManager::GetInstance()->RegisterNameSpace(
    279        aNamespaceURI, nameSpaceId);
    280    if (aResult.Failed()) {
    281      return nullptr;
    282    }
    283  }
    284 
    285  NS_ASSERTION(nameSpaceId != kNameSpaceID_Unknown, "Unexpected namespace ID!");
    286  return NS_GetContentList(&AsNode(), nameSpaceId, aLocalName);
    287 }
    288 
    289 already_AddRefed<nsContentList> DocumentOrShadowRoot::GetElementsByClassName(
    290    const nsAString& aClasses) {
    291  return nsContentUtils::GetElementsByClassName(&AsNode(), aClasses);
    292 }
    293 
    294 nsINode* DocumentOrShadowRoot::Retarget(nsINode* aNode) const {
    295  for (nsINode* cur = aNode; cur; cur = cur->GetContainingShadowHost()) {
    296    if (cur->SubtreeRoot() == &AsNode()) {
    297      return cur;
    298    }
    299  }
    300  return nullptr;
    301 }
    302 
    303 Element* DocumentOrShadowRoot::GetRetargetedFocusedElement() {
    304  auto* content = AsNode().OwnerDoc()->GetUnretargetedFocusedContent();
    305  if (!content) {
    306    return nullptr;
    307  }
    308  if (nsINode* retarget = Retarget(content)) {
    309    return retarget->AsElement();
    310  }
    311  return nullptr;
    312 }
    313 
    314 Element* DocumentOrShadowRoot::GetPointerLockElement() {
    315  nsCOMPtr<Element> pointerLockedElement =
    316      PointerLockManager::GetLockedElement();
    317  return Element::FromNodeOrNull(Retarget(pointerLockedElement));
    318 }
    319 
    320 Element* DocumentOrShadowRoot::GetFullscreenElement() const {
    321  if (!AsNode().IsInComposedDoc()) {
    322    return nullptr;
    323  }
    324 
    325  Element* element = AsNode().OwnerDoc()->GetUnretargetedFullscreenElement();
    326  NS_ASSERTION(!element || element->State().HasState(ElementState::FULLSCREEN),
    327               "Fullscreen element should have fullscreen styles applied");
    328  return Element::FromNodeOrNull(Retarget(element));
    329 }
    330 
    331 namespace {
    332 
    333 using FrameForPointOption = nsLayoutUtils::FrameForPointOption;
    334 using FrameForPointOptions = nsLayoutUtils::FrameForPointOptions;
    335 
    336 // Whether only one node or multiple nodes is requested.
    337 enum class Multiple {
    338  No,
    339  Yes,
    340 };
    341 
    342 // Whether we should flush layout or not.
    343 enum class FlushLayout {
    344  No,
    345  Yes,
    346 };
    347 
    348 enum class PerformRetargeting {
    349  No,
    350  Yes,
    351 };
    352 
    353 template <typename NodeOrElement>
    354 NodeOrElement* CastTo(nsINode*);
    355 
    356 template <>
    357 Element* CastTo<Element>(nsINode* aNode) {
    358  return aNode->AsElement();
    359 }
    360 
    361 template <>
    362 nsINode* CastTo<nsINode>(nsINode* aNode) {
    363  return aNode;
    364 }
    365 
    366 template <typename NodeOrElement>
    367 static void QueryNodesFromRect(DocumentOrShadowRoot& aRoot, const nsRect& aRect,
    368                               FrameForPointOptions aOptions,
    369                               FlushLayout aShouldFlushLayout,
    370                               Multiple aMultiple, ViewportType aViewportType,
    371                               PerformRetargeting aPerformRetargeting,
    372                               nsTArray<RefPtr<NodeOrElement>>& aNodes) {
    373  static_assert(std::is_same<nsINode, NodeOrElement>::value ||
    374                    std::is_same<Element, NodeOrElement>::value,
    375                "Should returning nodes or elements");
    376 
    377  constexpr bool returningElements =
    378      std::is_same<Element, NodeOrElement>::value;
    379  const bool retargeting = aPerformRetargeting == PerformRetargeting::Yes;
    380 
    381  nsCOMPtr<Document> doc = aRoot.AsNode().OwnerDoc();
    382 
    383  // Make sure the layout information we get is up-to-date, and
    384  // ensure we get a root frame (for everything but XUL)
    385  if (aShouldFlushLayout == FlushLayout::Yes) {
    386    doc->FlushPendingNotifications(FlushType::Layout);
    387  }
    388 
    389  PresShell* presShell = doc->GetPresShell();
    390  if (!presShell) {
    391    return;
    392  }
    393 
    394  nsIFrame* rootFrame = presShell->GetRootFrame();
    395  // XUL docs, unlike HTML, have no frame tree until everything's done loading
    396  if (!rootFrame) {
    397    return;  // return null to premature XUL callers as a reminder to wait
    398  }
    399 
    400  aOptions.mBits += FrameForPointOption::IgnorePaintSuppression;
    401  aOptions.mBits += FrameForPointOption::IgnoreCrossDoc;
    402 
    403  AutoTArray<nsIFrame*, 8> frames;
    404  nsLayoutUtils::GetFramesForArea({rootFrame, aViewportType}, aRect, frames,
    405                                  aOptions);
    406 
    407  for (nsIFrame* frame : frames) {
    408    nsINode* node = doc->GetContentInThisDocument(frame);
    409    while (node && node->IsInNativeAnonymousSubtree()) {
    410      nsIContent* root = node->GetClosestNativeAnonymousSubtreeRoot();
    411      MOZ_ASSERT(root, "content is connected");
    412      MOZ_ASSERT(root->IsRootOfNativeAnonymousSubtree(), "wat");
    413      if (root == &aRoot.AsNode()) {
    414        // If we're in the anonymous subtree root we care about, don't retarget.
    415        break;
    416      }
    417      node = root->GetParentOrShadowHostNode();
    418    }
    419 
    420    if (!node) {
    421      continue;
    422    }
    423 
    424    if (returningElements && !node->IsElement()) {
    425      // If this helper is called via ElementsFromPoint, we need to make sure
    426      // our frame is an element. Otherwise return whatever the top frame is
    427      // even if it isn't the top-painted element.
    428      // SVG 'text' element's SVGTextFrame doesn't respond to hit-testing, so
    429      // if 'content' is a child of such an element then we need to manually
    430      // defer to the parent here.
    431      if (aMultiple == Multiple::Yes && !frame->IsInSVGTextSubtree()) {
    432        continue;
    433      }
    434 
    435      node = node->GetParent();
    436      if (ShadowRoot* shadow = ShadowRoot::FromNodeOrNull(node)) {
    437        node = shadow->Host();
    438      }
    439    }
    440 
    441    // XXXsmaug There is plenty of unspec'ed behavior here
    442    //         https://github.com/w3c/webcomponents/issues/735
    443    //         https://github.com/w3c/webcomponents/issues/736
    444    if (retargeting) {
    445      node = aRoot.Retarget(node);
    446    }
    447 
    448    if (node && node != aNodes.SafeLastElement(nullptr)) {
    449      aNodes.AppendElement(CastTo<NodeOrElement>(node));
    450      if (aMultiple == Multiple::No) {
    451        return;
    452      }
    453    }
    454  }
    455 }
    456 
    457 template <typename NodeOrElement>
    458 static void QueryNodesFromPoint(DocumentOrShadowRoot& aRoot, float aX, float aY,
    459                                FrameForPointOptions aOptions,
    460                                FlushLayout aShouldFlushLayout,
    461                                Multiple aMultiple, ViewportType aViewportType,
    462                                PerformRetargeting aPerformRetargeting,
    463                                nsTArray<RefPtr<NodeOrElement>>& aNodes) {
    464  // As per the spec, we return null if either coord is negative.
    465  if (!aOptions.mBits.contains(FrameForPointOption::IgnoreRootScrollFrame) &&
    466      (aX < 0 || aY < 0)) {
    467    return;
    468  }
    469 
    470  nscoord x = nsPresContext::CSSPixelsToAppUnits(aX);
    471  nscoord y = nsPresContext::CSSPixelsToAppUnits(aY);
    472  nsPoint pt(x, y);
    473  QueryNodesFromRect(aRoot, nsRect(pt, nsSize(1, 1)), aOptions,
    474                     aShouldFlushLayout, aMultiple, aViewportType,
    475                     aPerformRetargeting, aNodes);
    476 }
    477 
    478 }  // namespace
    479 
    480 Element* DocumentOrShadowRoot::ElementFromPoint(float aX, float aY) {
    481  return ElementFromPointHelper(aX, aY, false, true, ViewportType::Layout);
    482 }
    483 
    484 void DocumentOrShadowRoot::ElementsFromPoint(
    485    float aX, float aY, nsTArray<RefPtr<Element>>& aElements) {
    486  QueryNodesFromPoint(*this, aX, aY, {}, FlushLayout::Yes, Multiple::Yes,
    487                      ViewportType::Layout, PerformRetargeting::Yes, aElements);
    488 }
    489 
    490 void DocumentOrShadowRoot::NodesFromPoint(float aX, float aY,
    491                                          nsTArray<RefPtr<nsINode>>& aNodes) {
    492  QueryNodesFromPoint(*this, aX, aY, {}, FlushLayout::Yes, Multiple::Yes,
    493                      ViewportType::Layout, PerformRetargeting::Yes, aNodes);
    494 }
    495 
    496 nsINode* DocumentOrShadowRoot::NodeFromPoint(float aX, float aY) {
    497  AutoTArray<RefPtr<nsINode>, 1> nodes;
    498  QueryNodesFromPoint(*this, aX, aY, {}, FlushLayout::Yes, Multiple::No,
    499                      ViewportType::Layout, PerformRetargeting::Yes, nodes);
    500  return nodes.SafeElementAt(0);
    501 }
    502 
    503 Element* DocumentOrShadowRoot::ElementFromPointHelper(
    504    float aX, float aY, bool aIgnoreRootScrollFrame, bool aFlushLayout,
    505    ViewportType aViewportType, bool aPerformRetargeting) {
    506  EnumSet<FrameForPointOption> options;
    507  if (aIgnoreRootScrollFrame) {
    508    options += FrameForPointOption::IgnoreRootScrollFrame;
    509  }
    510 
    511  auto flush = aFlushLayout ? FlushLayout::Yes : FlushLayout::No;
    512  auto performRetargeting =
    513      aPerformRetargeting ? PerformRetargeting::Yes : PerformRetargeting::No;
    514 
    515  AutoTArray<RefPtr<Element>, 1> elements;
    516  QueryNodesFromPoint(*this, aX, aY, options, flush, Multiple::No,
    517                      aViewportType, performRetargeting, elements);
    518  return elements.SafeElementAt(0);
    519 }
    520 
    521 void DocumentOrShadowRoot::NodesFromRect(float aX, float aY, float aTopSize,
    522                                         float aRightSize, float aBottomSize,
    523                                         float aLeftSize,
    524                                         bool aIgnoreRootScrollFrame,
    525                                         bool aFlushLayout, bool aOnlyVisible,
    526                                         float aVisibleThreshold,
    527                                         nsTArray<RefPtr<nsINode>>& aReturn) {
    528  // Following the same behavior of elementFromPoint,
    529  // we don't return anything if either coord is negative
    530  if (!aIgnoreRootScrollFrame && (aX < 0 || aY < 0)) {
    531    return;
    532  }
    533 
    534  nscoord x = nsPresContext::CSSPixelsToAppUnits(aX - aLeftSize);
    535  nscoord y = nsPresContext::CSSPixelsToAppUnits(aY - aTopSize);
    536  nscoord w = nsPresContext::CSSPixelsToAppUnits(aLeftSize + aRightSize) + 1;
    537  nscoord h = nsPresContext::CSSPixelsToAppUnits(aTopSize + aBottomSize) + 1;
    538 
    539  nsRect rect(x, y, w, h);
    540 
    541  FrameForPointOptions options;
    542  if (aIgnoreRootScrollFrame) {
    543    options.mBits += FrameForPointOption::IgnoreRootScrollFrame;
    544  }
    545  if (aOnlyVisible) {
    546    options.mBits += FrameForPointOption::OnlyVisible;
    547    options.mVisibleThreshold = aVisibleThreshold;
    548  }
    549 
    550  auto flush = aFlushLayout ? FlushLayout::Yes : FlushLayout::No;
    551  QueryNodesFromRect(*this, rect, options, flush, Multiple::Yes,
    552                     ViewportType::Layout, PerformRetargeting::No, aReturn);
    553 }
    554 
    555 Element* DocumentOrShadowRoot::AddIDTargetObserver(nsAtom* aID,
    556                                                   IDTargetObserver aObserver,
    557                                                   void* aData,
    558                                                   bool aForImage) {
    559  nsDependentAtomString id(aID);
    560 
    561  if (!CheckGetElementByIdArg(id)) {
    562    return nullptr;
    563  }
    564 
    565  IdentifierMapEntry* entry = mIdentifierMap.PutEntry(aID);
    566  NS_ENSURE_TRUE(entry, nullptr);
    567 
    568  entry->AddContentChangeCallback(aObserver, aData, aForImage);
    569  return aForImage ? entry->GetImageIdElement() : entry->GetIdElement();
    570 }
    571 
    572 void DocumentOrShadowRoot::RemoveIDTargetObserver(nsAtom* aID,
    573                                                  IDTargetObserver aObserver,
    574                                                  void* aData, bool aForImage) {
    575  nsDependentAtomString id(aID);
    576 
    577  if (!CheckGetElementByIdArg(id)) {
    578    return;
    579  }
    580 
    581  IdentifierMapEntry* entry = mIdentifierMap.GetEntry(aID);
    582  if (!entry) {
    583    return;
    584  }
    585 
    586  entry->RemoveContentChangeCallback(aObserver, aData, aForImage);
    587 }
    588 
    589 Element* DocumentOrShadowRoot::LookupImageElement(nsAtom* aId) {
    590  if (aId->IsEmpty()) {
    591    return nullptr;
    592  }
    593 
    594  IdentifierMapEntry* entry = mIdentifierMap.GetEntry(aId);
    595  return entry ? entry->GetImageIdElement() : nullptr;
    596 }
    597 
    598 void DocumentOrShadowRoot::ReportEmptyGetElementByIdArg() const {
    599  nsContentUtils::ReportEmptyGetElementByIdArg(AsNode().OwnerDoc());
    600 }
    601 
    602 void DocumentOrShadowRoot::GetAnimations(
    603    nsTArray<RefPtr<Animation>>& aAnimations) {
    604  // As with Element::GetAnimations we initially flush style here.
    605  // This should ensure that there are no subsequent changes to the tree
    606  // structure while iterating over the children below.
    607  if (Document* doc = AsNode().GetComposedDoc()) {
    608    doc->FlushPendingNotifications(
    609        ChangesToFlush(FlushType::Style, /* aFlushAnimations = */ false,
    610                       /* aUpdateRelevancy = */ false));
    611  }
    612 
    613  GetAnimationsOptions options;
    614  options.mSubtree = true;
    615 
    616  for (RefPtr<nsIContent> child = AsNode().GetFirstChild(); child;
    617       child = child->GetNextSibling()) {
    618    if (RefPtr<Element> element = Element::FromNode(child)) {
    619      nsTArray<RefPtr<Animation>> result;
    620      element->GetAnimationsWithoutFlush(options, result);
    621      aAnimations.AppendElements(std::move(result));
    622    }
    623  }
    624 
    625  aAnimations.Sort(AnimationPtrComparator<RefPtr<Animation>>());
    626 }
    627 
    628 struct SheetTreeOrderComparator {
    629  nsINode* mNode = nullptr;
    630  mutable nsContentUtils::NodeIndexCache mCache;
    631 
    632  int operator()(StyleSheet* aSheet) const {
    633    auto* sheetNode = aSheet->GetOwnerNode();
    634    MOZ_ASSERT(sheetNode != mNode, "Sheet already in the list?");
    635    if (!sheetNode) {
    636      // We go after all the link headers.
    637      return 1;
    638    }
    639    return nsContentUtils::CompareTreePosition<TreeKind::DOM>(mNode, sheetNode,
    640                                                              nullptr, &mCache);
    641  }
    642 };
    643 
    644 size_t DocumentOrShadowRoot::FindSheetInsertionPointInTree(
    645    const StyleSheet& aSheet) const {
    646  MOZ_ASSERT(!aSheet.IsConstructed());
    647  nsINode* owningNode = aSheet.GetOwnerNode();
    648  // We should always have an owning node unless Link: headers are at play.
    649  MOZ_ASSERT_IF(!owningNode, AsNode().IsDocument());
    650  MOZ_ASSERT_IF(owningNode, owningNode->SubtreeRoot() == &AsNode());
    651  if (mStyleSheets.IsEmpty()) {
    652    return 0;
    653  }
    654 
    655  if (!owningNode) {
    656    // We come from a link header, our position is after all the other link
    657    // headers, but before any link-owned node.
    658    size_t i = 0;
    659    for (const auto& sheet : mStyleSheets) {
    660      if (sheet->GetOwnerNode()) {
    661        break;
    662      }
    663      i++;
    664    }
    665    return i;
    666  }
    667 
    668  SheetTreeOrderComparator cmp{owningNode};
    669  if (cmp(mStyleSheets.LastElement()) > 0) {
    670    // Optimize for append.
    671    return mStyleSheets.Length();
    672  }
    673  size_t idx;
    674  BinarySearchIf(mStyleSheets, 0, mStyleSheets.Length(), cmp, &idx);
    675  return idx;
    676 }
    677 
    678 size_t DocumentOrShadowRoot::StyleOrderIndexOfSheet(
    679    const StyleSheet& aSheet) const {
    680  if (aSheet.IsConstructed()) {
    681    // NOTE: constructable sheets can have duplicates, so we need to start
    682    // looking from behind.
    683    size_t index = mAdoptedStyleSheets.LastIndexOf(&aSheet);
    684    return index == mAdoptedStyleSheets.NoIndex ? index : index + SheetCount();
    685  }
    686  return mStyleSheets.LastIndexOf(&aSheet);
    687 }
    688 
    689 void DocumentOrShadowRoot::TraverseSheetRefInStylesIfApplicable(
    690    StyleSheet& aSheet, nsCycleCollectionTraversalCallback& cb) {
    691  if (!aSheet.IsApplicable()) {
    692    return;
    693  }
    694  if (mKind == Kind::ShadowRoot) {
    695    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mServoStyles->sheets[i]");
    696    cb.NoteXPCOMChild(&aSheet);
    697  } else if (AsNode().AsDocument()->StyleSetFilled()) {
    698    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
    699        cb, "mStyleSet->mRawSet.stylist.stylesheets.<origin>[i]");
    700    cb.NoteXPCOMChild(&aSheet);
    701  }
    702 }
    703 
    704 void DocumentOrShadowRoot::TraverseStyleSheets(
    705    nsTArray<RefPtr<StyleSheet>>& aSheets, const char* aEdgeName,
    706    nsCycleCollectionTraversalCallback& cb) {
    707  MOZ_ASSERT(aEdgeName);
    708  MOZ_ASSERT(&aSheets != &mAdoptedStyleSheets);
    709  for (StyleSheet* sheet : aSheets) {
    710    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, aEdgeName);
    711    cb.NoteXPCOMChild(sheet);
    712    TraverseSheetRefInStylesIfApplicable(*sheet, cb);
    713  }
    714 }
    715 
    716 void DocumentOrShadowRoot::Traverse(DocumentOrShadowRoot* tmp,
    717                                    nsCycleCollectionTraversalCallback& cb) {
    718  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMStyleSheets)
    719  tmp->TraverseStyleSheets(tmp->mStyleSheets, "mStyleSheets[i]", cb);
    720 
    721  tmp->EnumerateUniqueAdoptedStyleSheetsBackToFront([&](StyleSheet& aSheet) {
    722    tmp->TraverseSheetRefInStylesIfApplicable(aSheet, cb);
    723  });
    724  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAdoptedStyleSheets);
    725 
    726  for (auto iter = tmp->mIdentifierMap.Iter(); !iter.Done(); iter.Next()) {
    727    iter.Get()->Traverse(&cb);
    728  }
    729 }
    730 
    731 void DocumentOrShadowRoot::UnlinkStyleSheets(
    732    nsTArray<RefPtr<StyleSheet>>& aSheets) {
    733  MOZ_ASSERT(&aSheets != &mAdoptedStyleSheets);
    734  for (StyleSheet* sheet : aSheets) {
    735    sheet->ClearAssociatedDocumentOrShadowRoot();
    736    RemoveSheetFromStylesIfApplicable(*sheet);
    737  }
    738  aSheets.Clear();
    739 }
    740 
    741 void DocumentOrShadowRoot::Unlink(DocumentOrShadowRoot* tmp) {
    742  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMStyleSheets);
    743  tmp->UnlinkStyleSheets(tmp->mStyleSheets);
    744  tmp->EnumerateUniqueAdoptedStyleSheetsBackToFront([&](StyleSheet& aSheet) {
    745    aSheet.RemoveAdopter(*tmp);
    746    tmp->RemoveSheetFromStylesIfApplicable(aSheet);
    747  });
    748  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAdoptedStyleSheets);
    749  tmp->mIdentifierMap.Clear();
    750 }
    751 
    752 }  // namespace mozilla::dom