Backed out changeset 9d8b4c0b99ed (bug 1945683) for causing btime failures. CLOSED...
[gecko.git] / dom / base / DocumentOrShadowRoot.cpp
blobe1ce1bac6c2e5159b8c3d737d683bd3ca3070b88
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "DocumentOrShadowRoot.h"
8 #include "mozilla/AnimationComparator.h"
9 #include "mozilla/EventStateManager.h"
10 #include "mozilla/PointerLockManager.h"
11 #include "mozilla/PresShell.h"
12 #include "mozilla/StyleSheet.h"
13 #include "mozilla/dom/AnimatableBinding.h"
14 #include "mozilla/dom/Document.h"
15 #include "mozilla/dom/HTMLInputElement.h"
16 #include "mozilla/dom/ShadowRoot.h"
17 #include "mozilla/dom/StyleSheetList.h"
18 #include "nsTHashtable.h"
19 #include "nsContentUtils.h"
20 #include "nsFocusManager.h"
21 #include "nsIFormControl.h"
22 #include "nsLayoutUtils.h"
23 #include "nsNameSpaceManager.h"
24 #include "nsWindowSizes.h"
26 namespace mozilla::dom {
28 DocumentOrShadowRoot::DocumentOrShadowRoot(ShadowRoot* aShadowRoot)
29 : mAsNode(aShadowRoot), mKind(Kind::ShadowRoot) {
30 MOZ_ASSERT(mAsNode);
33 DocumentOrShadowRoot::DocumentOrShadowRoot(Document* aDoc)
34 : mAsNode(aDoc), mKind(Kind::Document) {
35 MOZ_ASSERT(mAsNode);
38 void DocumentOrShadowRoot::AddSizeOfOwnedSheetArrayExcludingThis(
39 nsWindowSizes& aSizes, const nsTArray<RefPtr<StyleSheet>>& aSheets) const {
40 size_t n = 0;
41 n += aSheets.ShallowSizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
42 for (StyleSheet* sheet : aSheets) {
43 if (!sheet->GetAssociatedDocumentOrShadowRoot()) {
44 // Avoid over-reporting shared sheets.
45 continue;
47 n += sheet->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf);
50 if (mKind == Kind::ShadowRoot) {
51 aSizes.mLayoutShadowDomStyleSheetsSize += n;
52 } else {
53 aSizes.mLayoutStyleSheetsSize += n;
57 void DocumentOrShadowRoot::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
58 AddSizeOfOwnedSheetArrayExcludingThis(aSizes, mStyleSheets);
59 aSizes.mDOMSizes.mDOMOtherSize +=
60 mIdentifierMap.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
63 DocumentOrShadowRoot::~DocumentOrShadowRoot() {
64 for (StyleSheet* sheet : mStyleSheets) {
65 sheet->ClearAssociatedDocumentOrShadowRoot();
69 StyleSheetList* DocumentOrShadowRoot::StyleSheets() {
70 if (!mDOMStyleSheets) {
71 mDOMStyleSheets = new StyleSheetList(*this);
73 return mDOMStyleSheets;
76 void DocumentOrShadowRoot::InsertSheetAt(size_t aIndex, StyleSheet& aSheet) {
77 aSheet.SetAssociatedDocumentOrShadowRoot(this);
78 mStyleSheets.InsertElementAt(aIndex, &aSheet);
81 void DocumentOrShadowRoot::RemoveStyleSheet(StyleSheet& aSheet) {
82 auto index = mStyleSheets.IndexOf(&aSheet);
83 if (index == mStyleSheets.NoIndex) {
84 // We should only hit this case if we are unlinking
85 // in which case mStyleSheets should be cleared.
86 MOZ_ASSERT(mKind != Kind::Document ||
87 AsNode().AsDocument()->InUnlinkOrDeletion());
88 MOZ_ASSERT(mStyleSheets.IsEmpty());
89 return;
91 RefPtr<StyleSheet> sheet = std::move(mStyleSheets[index]);
92 mStyleSheets.RemoveElementAt(index);
93 RemoveSheetFromStylesIfApplicable(*sheet);
94 sheet->ClearAssociatedDocumentOrShadowRoot();
95 AsNode().OwnerDoc()->PostStyleSheetRemovedEvent(aSheet);
98 void DocumentOrShadowRoot::RemoveSheetFromStylesIfApplicable(
99 StyleSheet& aSheet) {
100 if (!aSheet.IsApplicable()) {
101 return;
103 if (mKind == Kind::Document) {
104 AsNode().AsDocument()->RemoveStyleSheetFromStyleSets(aSheet);
105 } else {
106 MOZ_ASSERT(AsNode().IsShadowRoot());
107 static_cast<ShadowRoot&>(AsNode()).RemoveSheetFromStyles(aSheet);
111 // https://drafts.csswg.org/cssom/#dom-documentorshadowroot-adoptedstylesheets
112 void DocumentOrShadowRoot::OnSetAdoptedStyleSheets(StyleSheet& aSheet,
113 uint32_t aIndex,
114 ErrorResult& aRv) {
115 Document& doc = *AsNode().OwnerDoc();
116 // 1. If value’s constructed flag is not set, or its constructor document is
117 // not equal to this DocumentOrShadowRoot's node document, throw a
118 // "NotAllowedError" DOMException.
120 if (!StaticPrefs::
121 dom_webcomponents_lift_adoptedstylesheets_restriction_enabled()) {
122 if (!aSheet.IsConstructed()) {
123 return aRv.ThrowNotAllowedError(
124 "Adopted style sheet must be created through the Constructable "
125 "StyleSheets API");
128 if (!aSheet.ConstructorDocumentMatches(doc)) {
129 return aRv.ThrowNotAllowedError(
130 "Adopted style sheet's constructor document must match the "
131 "document or shadow root's node document");
135 auto* shadow = ShadowRoot::FromNode(AsNode());
136 MOZ_ASSERT((mKind == Kind::ShadowRoot) == !!shadow);
138 auto existingIndex = mAdoptedStyleSheets.LastIndexOf(&aSheet);
139 // Ensure it's in the backing array at the right index.
140 mAdoptedStyleSheets.InsertElementAt(aIndex, &aSheet);
141 if (existingIndex == mAdoptedStyleSheets.NoIndex) {
142 // common case: we're not already adopting this sheet.
143 aSheet.AddAdopter(*this);
144 } else if (existingIndex < aIndex) {
145 // We're inserting an already-adopted stylesheet in a later position, so
146 // this one should take precedent and we should remove the old one.
147 RemoveSheetFromStylesIfApplicable(aSheet);
148 } else {
149 // The sheet is already at a position later than or equal to the current
150 // one, and is already adopted by us, we have nothing to do here other than
151 // adding to the current list.
152 return;
155 if (aSheet.IsApplicable()) {
156 if (mKind == Kind::Document) {
157 doc.AddStyleSheetToStyleSets(aSheet);
158 } else {
159 shadow->InsertSheetIntoAuthorData(aIndex, aSheet, mAdoptedStyleSheets);
164 void DocumentOrShadowRoot::OnDeleteAdoptedStyleSheets(StyleSheet& aSheet,
165 uint32_t aIndex,
166 ErrorResult&) {
167 MOZ_ASSERT(mAdoptedStyleSheets.ElementAt(aIndex) == &aSheet);
168 mAdoptedStyleSheets.RemoveElementAt(aIndex);
169 auto existingIndex = mAdoptedStyleSheets.LastIndexOf(&aSheet);
170 if (existingIndex != mAdoptedStyleSheets.NoIndex && existingIndex >= aIndex) {
171 // The sheet is still adopted by us and was already later from the one we're
172 // removing, so nothing to do.
173 return;
176 RemoveSheetFromStylesIfApplicable(aSheet);
177 if (existingIndex == mAdoptedStyleSheets.NoIndex) {
178 // The sheet is no longer adopted by us.
179 aSheet.RemoveAdopter(*this);
180 } else if (aSheet.IsApplicable()) {
181 // We need to re-insert the sheet at the right (pre-existing) index.
182 nsINode& node = AsNode();
183 if (mKind == Kind::Document) {
184 node.AsDocument()->AddStyleSheetToStyleSets(aSheet);
185 } else {
186 ShadowRoot::FromNode(node)->InsertSheetIntoAuthorData(
187 existingIndex, aSheet, mAdoptedStyleSheets);
192 void DocumentOrShadowRoot::ClearAdoptedStyleSheets() {
193 auto* shadow = ShadowRoot::FromNode(AsNode());
194 auto* doc = shadow ? nullptr : AsNode().AsDocument();
195 MOZ_ASSERT(shadow || doc);
196 IgnoredErrorResult rv;
197 while (!mAdoptedStyleSheets.IsEmpty()) {
198 if (shadow) {
199 ShadowRoot_Binding::AdoptedStyleSheetsHelpers::RemoveLastElement(shadow,
200 rv);
201 } else {
202 Document_Binding::AdoptedStyleSheetsHelpers::RemoveLastElement(doc, rv);
204 MOZ_DIAGNOSTIC_ASSERT(!rv.Failed(), "Removal doesn't fail");
208 void DocumentOrShadowRoot::CloneAdoptedSheetsFrom(
209 const DocumentOrShadowRoot& aSource) {
210 if (aSource.mAdoptedStyleSheets.IsEmpty()) {
211 return;
214 Document& ownerDoc = *AsNode().OwnerDoc();
215 const Document& sourceDoc = *aSource.AsNode().OwnerDoc();
216 auto* clonedSheetMap = static_cast<Document::AdoptedStyleSheetCloneCache*>(
217 sourceDoc.GetProperty(nsGkAtoms::adoptedsheetclones));
218 MOZ_ASSERT(clonedSheetMap);
220 // We don't need to care about the reflector (AdoptedStyleSheetsHelpers and
221 // so) because this is only used for static documents.
222 for (const StyleSheet* sheet : aSource.mAdoptedStyleSheets) {
223 RefPtr<StyleSheet> clone = clonedSheetMap->LookupOrInsertWith(
224 sheet, [&] { return sheet->CloneAdoptedSheet(ownerDoc); });
225 MOZ_ASSERT(clone);
226 MOZ_DIAGNOSTIC_ASSERT(clone->ConstructorDocumentMatches(ownerDoc));
227 ErrorResult rv;
228 OnSetAdoptedStyleSheets(*clone, mAdoptedStyleSheets.Length(), rv);
229 MOZ_ASSERT(!rv.Failed());
233 Element* DocumentOrShadowRoot::GetElementById(
234 const nsAString& aElementId) const {
235 if (MOZ_UNLIKELY(aElementId.IsEmpty())) {
236 ReportEmptyGetElementByIdArg();
237 return nullptr;
240 if (IdentifierMapEntry* entry = mIdentifierMap.GetEntry(aElementId)) {
241 return entry->GetIdElement();
244 return nullptr;
247 Element* DocumentOrShadowRoot::GetElementById(nsAtom* aElementId) const {
248 if (MOZ_UNLIKELY(aElementId == nsGkAtoms::_empty)) {
249 ReportEmptyGetElementByIdArg();
250 return nullptr;
253 if (IdentifierMapEntry* entry = mIdentifierMap.GetEntry(aElementId)) {
254 return entry->GetIdElement();
257 return nullptr;
260 already_AddRefed<nsContentList> DocumentOrShadowRoot::GetElementsByTagNameNS(
261 const nsAString& aNamespaceURI, const nsAString& aLocalName) {
262 ErrorResult rv;
263 RefPtr<nsContentList> list =
264 GetElementsByTagNameNS(aNamespaceURI, aLocalName, rv);
265 if (rv.Failed()) {
266 return nullptr;
268 return list.forget();
271 already_AddRefed<nsContentList> DocumentOrShadowRoot::GetElementsByTagNameNS(
272 const nsAString& aNamespaceURI, const nsAString& aLocalName,
273 ErrorResult& aResult) {
274 int32_t nameSpaceId = kNameSpaceID_Wildcard;
276 if (!aNamespaceURI.EqualsLiteral("*")) {
277 aResult = nsNameSpaceManager::GetInstance()->RegisterNameSpace(
278 aNamespaceURI, nameSpaceId);
279 if (aResult.Failed()) {
280 return nullptr;
284 NS_ASSERTION(nameSpaceId != kNameSpaceID_Unknown, "Unexpected namespace ID!");
285 return NS_GetContentList(&AsNode(), nameSpaceId, aLocalName);
288 already_AddRefed<nsContentList> DocumentOrShadowRoot::GetElementsByClassName(
289 const nsAString& aClasses) {
290 return nsContentUtils::GetElementsByClassName(&AsNode(), aClasses);
293 nsINode* DocumentOrShadowRoot::Retarget(nsINode* aNode) const {
294 for (nsINode* cur = aNode; cur; cur = cur->GetContainingShadowHost()) {
295 if (cur->SubtreeRoot() == &AsNode()) {
296 return cur;
299 return nullptr;
302 Element* DocumentOrShadowRoot::GetRetargetedFocusedElement() {
303 auto* content = AsNode().OwnerDoc()->GetUnretargetedFocusedContent();
304 if (!content) {
305 return nullptr;
307 if (nsINode* retarget = Retarget(content)) {
308 return retarget->AsElement();
310 return nullptr;
313 Element* DocumentOrShadowRoot::GetPointerLockElement() {
314 nsCOMPtr<Element> pointerLockedElement =
315 PointerLockManager::GetLockedElement();
316 return Element::FromNodeOrNull(Retarget(pointerLockedElement));
319 Element* DocumentOrShadowRoot::GetFullscreenElement() const {
320 if (!AsNode().IsInComposedDoc()) {
321 return nullptr;
324 Element* element = AsNode().OwnerDoc()->GetUnretargetedFullscreenElement();
325 NS_ASSERTION(!element || element->State().HasState(ElementState::FULLSCREEN),
326 "Fullscreen element should have fullscreen styles applied");
327 return Element::FromNodeOrNull(Retarget(element));
330 namespace {
332 using FrameForPointOption = nsLayoutUtils::FrameForPointOption;
333 using FrameForPointOptions = nsLayoutUtils::FrameForPointOptions;
335 // Whether only one node or multiple nodes is requested.
336 enum class Multiple {
338 Yes,
341 // Whether we should flush layout or not.
342 enum class FlushLayout {
344 Yes,
347 enum class PerformRetargeting {
349 Yes,
352 template <typename NodeOrElement>
353 NodeOrElement* CastTo(nsINode*);
355 template <>
356 Element* CastTo<Element>(nsINode* aNode) {
357 return aNode->AsElement();
360 template <>
361 nsINode* CastTo<nsINode>(nsINode* aNode) {
362 return aNode;
365 template <typename NodeOrElement>
366 static void QueryNodesFromRect(DocumentOrShadowRoot& aRoot, const nsRect& aRect,
367 FrameForPointOptions aOptions,
368 FlushLayout aShouldFlushLayout,
369 Multiple aMultiple, ViewportType aViewportType,
370 PerformRetargeting aPerformRetargeting,
371 nsTArray<RefPtr<NodeOrElement>>& aNodes) {
372 static_assert(std::is_same<nsINode, NodeOrElement>::value ||
373 std::is_same<Element, NodeOrElement>::value,
374 "Should returning nodes or elements");
376 constexpr bool returningElements =
377 std::is_same<Element, NodeOrElement>::value;
378 const bool retargeting = aPerformRetargeting == PerformRetargeting::Yes;
380 nsCOMPtr<Document> doc = aRoot.AsNode().OwnerDoc();
382 // Make sure the layout information we get is up-to-date, and
383 // ensure we get a root frame (for everything but XUL)
384 if (aShouldFlushLayout == FlushLayout::Yes) {
385 doc->FlushPendingNotifications(FlushType::Layout);
388 PresShell* presShell = doc->GetPresShell();
389 if (!presShell) {
390 return;
393 nsIFrame* rootFrame = presShell->GetRootFrame();
394 // XUL docs, unlike HTML, have no frame tree until everything's done loading
395 if (!rootFrame) {
396 return; // return null to premature XUL callers as a reminder to wait
399 aOptions.mBits += FrameForPointOption::IgnorePaintSuppression;
400 aOptions.mBits += FrameForPointOption::IgnoreCrossDoc;
402 AutoTArray<nsIFrame*, 8> frames;
403 nsLayoutUtils::GetFramesForArea({rootFrame, aViewportType}, aRect, frames,
404 aOptions);
406 for (nsIFrame* frame : frames) {
407 nsINode* node = doc->GetContentInThisDocument(frame);
408 while (node && node->IsInNativeAnonymousSubtree()) {
409 nsIContent* root = node->GetClosestNativeAnonymousSubtreeRoot();
410 MOZ_ASSERT(root, "content is connected");
411 MOZ_ASSERT(root->IsRootOfNativeAnonymousSubtree(), "wat");
412 if (root == &aRoot.AsNode()) {
413 // If we're in the anonymous subtree root we care about, don't retarget.
414 break;
416 node = root->GetParentOrShadowHostNode();
419 if (!node) {
420 continue;
423 if (returningElements && !node->IsElement()) {
424 // If this helper is called via ElementsFromPoint, we need to make sure
425 // our frame is an element. Otherwise return whatever the top frame is
426 // even if it isn't the top-painted element.
427 // SVG 'text' element's SVGTextFrame doesn't respond to hit-testing, so
428 // if 'content' is a child of such an element then we need to manually
429 // defer to the parent here.
430 if (aMultiple == Multiple::Yes && !frame->IsInSVGTextSubtree()) {
431 continue;
434 node = node->GetParent();
435 if (ShadowRoot* shadow = ShadowRoot::FromNodeOrNull(node)) {
436 node = shadow->Host();
440 // XXXsmaug There is plenty of unspec'ed behavior here
441 // https://github.com/w3c/webcomponents/issues/735
442 // https://github.com/w3c/webcomponents/issues/736
443 if (retargeting) {
444 node = aRoot.Retarget(node);
447 if (node && node != aNodes.SafeLastElement(nullptr)) {
448 aNodes.AppendElement(CastTo<NodeOrElement>(node));
449 if (aMultiple == Multiple::No) {
450 return;
456 template <typename NodeOrElement>
457 static void QueryNodesFromPoint(DocumentOrShadowRoot& aRoot, float aX, float aY,
458 FrameForPointOptions aOptions,
459 FlushLayout aShouldFlushLayout,
460 Multiple aMultiple, ViewportType aViewportType,
461 PerformRetargeting aPerformRetargeting,
462 nsTArray<RefPtr<NodeOrElement>>& aNodes) {
463 // As per the spec, we return null if either coord is negative.
464 if (!aOptions.mBits.contains(FrameForPointOption::IgnoreRootScrollFrame) &&
465 (aX < 0 || aY < 0)) {
466 return;
469 nscoord x = nsPresContext::CSSPixelsToAppUnits(aX);
470 nscoord y = nsPresContext::CSSPixelsToAppUnits(aY);
471 nsPoint pt(x, y);
472 QueryNodesFromRect(aRoot, nsRect(pt, nsSize(1, 1)), aOptions,
473 aShouldFlushLayout, aMultiple, aViewportType,
474 aPerformRetargeting, aNodes);
477 } // namespace
479 Element* DocumentOrShadowRoot::ElementFromPoint(float aX, float aY) {
480 return ElementFromPointHelper(aX, aY, false, true, ViewportType::Layout);
483 void DocumentOrShadowRoot::ElementsFromPoint(
484 float aX, float aY, nsTArray<RefPtr<Element>>& aElements) {
485 QueryNodesFromPoint(*this, aX, aY, {}, FlushLayout::Yes, Multiple::Yes,
486 ViewportType::Layout, PerformRetargeting::Yes, aElements);
489 void DocumentOrShadowRoot::NodesFromPoint(float aX, float aY,
490 nsTArray<RefPtr<nsINode>>& aNodes) {
491 QueryNodesFromPoint(*this, aX, aY, {}, FlushLayout::Yes, Multiple::Yes,
492 ViewportType::Layout, PerformRetargeting::Yes, aNodes);
495 nsINode* DocumentOrShadowRoot::NodeFromPoint(float aX, float aY) {
496 AutoTArray<RefPtr<nsINode>, 1> nodes;
497 QueryNodesFromPoint(*this, aX, aY, {}, FlushLayout::Yes, Multiple::No,
498 ViewportType::Layout, PerformRetargeting::Yes, nodes);
499 return nodes.SafeElementAt(0);
502 Element* DocumentOrShadowRoot::ElementFromPointHelper(
503 float aX, float aY, bool aIgnoreRootScrollFrame, bool aFlushLayout,
504 ViewportType aViewportType, bool aPerformRetargeting) {
505 EnumSet<FrameForPointOption> options;
506 if (aIgnoreRootScrollFrame) {
507 options += FrameForPointOption::IgnoreRootScrollFrame;
510 auto flush = aFlushLayout ? FlushLayout::Yes : FlushLayout::No;
511 auto performRetargeting =
512 aPerformRetargeting ? PerformRetargeting::Yes : PerformRetargeting::No;
514 AutoTArray<RefPtr<Element>, 1> elements;
515 QueryNodesFromPoint(*this, aX, aY, options, flush, Multiple::No,
516 aViewportType, performRetargeting, elements);
517 return elements.SafeElementAt(0);
520 void DocumentOrShadowRoot::NodesFromRect(float aX, float aY, float aTopSize,
521 float aRightSize, float aBottomSize,
522 float aLeftSize,
523 bool aIgnoreRootScrollFrame,
524 bool aFlushLayout, bool aOnlyVisible,
525 float aVisibleThreshold,
526 nsTArray<RefPtr<nsINode>>& aReturn) {
527 // Following the same behavior of elementFromPoint,
528 // we don't return anything if either coord is negative
529 if (!aIgnoreRootScrollFrame && (aX < 0 || aY < 0)) {
530 return;
533 nscoord x = nsPresContext::CSSPixelsToAppUnits(aX - aLeftSize);
534 nscoord y = nsPresContext::CSSPixelsToAppUnits(aY - aTopSize);
535 nscoord w = nsPresContext::CSSPixelsToAppUnits(aLeftSize + aRightSize) + 1;
536 nscoord h = nsPresContext::CSSPixelsToAppUnits(aTopSize + aBottomSize) + 1;
538 nsRect rect(x, y, w, h);
540 FrameForPointOptions options;
541 if (aIgnoreRootScrollFrame) {
542 options.mBits += FrameForPointOption::IgnoreRootScrollFrame;
544 if (aOnlyVisible) {
545 options.mBits += FrameForPointOption::OnlyVisible;
546 options.mVisibleThreshold = aVisibleThreshold;
549 auto flush = aFlushLayout ? FlushLayout::Yes : FlushLayout::No;
550 QueryNodesFromRect(*this, rect, options, flush, Multiple::Yes,
551 ViewportType::Layout, PerformRetargeting::No, aReturn);
554 Element* DocumentOrShadowRoot::AddIDTargetObserver(nsAtom* aID,
555 IDTargetObserver aObserver,
556 void* aData,
557 bool aForImage) {
558 nsDependentAtomString id(aID);
560 if (!CheckGetElementByIdArg(id)) {
561 return nullptr;
564 IdentifierMapEntry* entry = mIdentifierMap.PutEntry(aID);
565 NS_ENSURE_TRUE(entry, nullptr);
567 entry->AddContentChangeCallback(aObserver, aData, aForImage);
568 return aForImage ? entry->GetImageIdElement() : entry->GetIdElement();
571 void DocumentOrShadowRoot::RemoveIDTargetObserver(nsAtom* aID,
572 IDTargetObserver aObserver,
573 void* aData, bool aForImage) {
574 nsDependentAtomString id(aID);
576 if (!CheckGetElementByIdArg(id)) {
577 return;
580 IdentifierMapEntry* entry = mIdentifierMap.GetEntry(aID);
581 if (!entry) {
582 return;
585 entry->RemoveContentChangeCallback(aObserver, aData, aForImage);
588 Element* DocumentOrShadowRoot::LookupImageElement(const nsAString& aId) {
589 if (aId.IsEmpty()) {
590 return nullptr;
593 IdentifierMapEntry* entry = mIdentifierMap.GetEntry(aId);
594 return entry ? entry->GetImageIdElement() : nullptr;
597 void DocumentOrShadowRoot::ReportEmptyGetElementByIdArg() const {
598 nsContentUtils::ReportEmptyGetElementByIdArg(AsNode().OwnerDoc());
601 void DocumentOrShadowRoot::GetAnimations(
602 nsTArray<RefPtr<Animation>>& aAnimations) {
603 // As with Element::GetAnimations we initially flush style here.
604 // This should ensure that there are no subsequent changes to the tree
605 // structure while iterating over the children below.
606 if (Document* doc = AsNode().GetComposedDoc()) {
607 doc->FlushPendingNotifications(
608 ChangesToFlush(FlushType::Style, false /* flush animations */));
611 GetAnimationsOptions options;
612 options.mSubtree = true;
614 for (RefPtr<nsIContent> child = AsNode().GetFirstChild(); child;
615 child = child->GetNextSibling()) {
616 if (RefPtr<Element> element = Element::FromNode(child)) {
617 nsTArray<RefPtr<Animation>> result;
618 element->GetAnimationsWithoutFlush(options, result);
619 aAnimations.AppendElements(std::move(result));
623 aAnimations.Sort(AnimationPtrComparator<RefPtr<Animation>>());
626 int32_t DocumentOrShadowRoot::StyleOrderIndexOfSheet(
627 const StyleSheet& aSheet) const {
628 if (aSheet.IsConstructed()) {
629 // NOTE: constructable sheets can have duplicates, so we need to start
630 // looking from behind.
631 int32_t index = mAdoptedStyleSheets.LastIndexOf(&aSheet);
632 return (index < 0) ? index : index + SheetCount();
634 return mStyleSheets.IndexOf(&aSheet);
637 void DocumentOrShadowRoot::TraverseSheetRefInStylesIfApplicable(
638 StyleSheet& aSheet, nsCycleCollectionTraversalCallback& cb) {
639 if (!aSheet.IsApplicable()) {
640 return;
642 if (mKind == Kind::ShadowRoot) {
643 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mServoStyles->sheets[i]");
644 cb.NoteXPCOMChild(&aSheet);
645 } else if (AsNode().AsDocument()->StyleSetFilled()) {
646 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
647 cb, "mStyleSet->mRawSet.stylist.stylesheets.<origin>[i]");
648 cb.NoteXPCOMChild(&aSheet);
652 void DocumentOrShadowRoot::TraverseStyleSheets(
653 nsTArray<RefPtr<StyleSheet>>& aSheets, const char* aEdgeName,
654 nsCycleCollectionTraversalCallback& cb) {
655 MOZ_ASSERT(aEdgeName);
656 MOZ_ASSERT(&aSheets != &mAdoptedStyleSheets);
657 for (StyleSheet* sheet : aSheets) {
658 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, aEdgeName);
659 cb.NoteXPCOMChild(sheet);
660 TraverseSheetRefInStylesIfApplicable(*sheet, cb);
664 void DocumentOrShadowRoot::Traverse(DocumentOrShadowRoot* tmp,
665 nsCycleCollectionTraversalCallback& cb) {
666 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMStyleSheets)
667 tmp->TraverseStyleSheets(tmp->mStyleSheets, "mStyleSheets[i]", cb);
669 tmp->EnumerateUniqueAdoptedStyleSheetsBackToFront([&](StyleSheet& aSheet) {
670 tmp->TraverseSheetRefInStylesIfApplicable(aSheet, cb);
672 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAdoptedStyleSheets);
674 for (auto iter = tmp->mIdentifierMap.Iter(); !iter.Done(); iter.Next()) {
675 iter.Get()->Traverse(&cb);
679 void DocumentOrShadowRoot::UnlinkStyleSheets(
680 nsTArray<RefPtr<StyleSheet>>& aSheets) {
681 MOZ_ASSERT(&aSheets != &mAdoptedStyleSheets);
682 for (StyleSheet* sheet : aSheets) {
683 sheet->ClearAssociatedDocumentOrShadowRoot();
684 RemoveSheetFromStylesIfApplicable(*sheet);
686 aSheets.Clear();
689 void DocumentOrShadowRoot::Unlink(DocumentOrShadowRoot* tmp) {
690 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMStyleSheets);
691 tmp->UnlinkStyleSheets(tmp->mStyleSheets);
692 tmp->EnumerateUniqueAdoptedStyleSheetsBackToFront([&](StyleSheet& aSheet) {
693 aSheet.RemoveAdopter(*tmp);
694 tmp->RemoveSheetFromStylesIfApplicable(aSheet);
696 NS_IMPL_CYCLE_COLLECTION_UNLINK(mAdoptedStyleSheets);
697 tmp->mIdentifierMap.Clear();
700 } // namespace mozilla::dom