Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / base / DocumentOrShadowRoot.cpp
blobe4018c3f02744ed129cea7b9edf11ea8cb60472a
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.
119 if (!aSheet.IsConstructed()) {
120 return aRv.ThrowNotAllowedError(
121 "Adopted style sheet must be created through the Constructable "
122 "StyleSheets API");
124 if (!aSheet.ConstructorDocumentMatches(doc)) {
125 return aRv.ThrowNotAllowedError(
126 "Adopted style sheet's constructor document must match the "
127 "document or shadow root's node document");
130 auto* shadow = ShadowRoot::FromNode(AsNode());
131 MOZ_ASSERT((mKind == Kind::ShadowRoot) == !!shadow);
133 auto existingIndex = mAdoptedStyleSheets.LastIndexOf(&aSheet);
134 // Ensure it's in the backing array at the right index.
135 mAdoptedStyleSheets.InsertElementAt(aIndex, &aSheet);
136 if (existingIndex == mAdoptedStyleSheets.NoIndex) {
137 // common case: we're not already adopting this sheet.
138 aSheet.AddAdopter(*this);
139 } else if (existingIndex < aIndex) {
140 // We're inserting an already-adopted stylesheet in a later position, so
141 // this one should take precedent and we should remove the old one.
142 RemoveSheetFromStylesIfApplicable(aSheet);
143 } else {
144 // The sheet is already at a position later than or equal to the current
145 // one, and is already adopted by us, we have nothing to do here other than
146 // adding to the current list.
147 return;
150 if (aSheet.IsApplicable()) {
151 if (mKind == Kind::Document) {
152 doc.AddStyleSheetToStyleSets(aSheet);
153 } else {
154 shadow->InsertSheetIntoAuthorData(aIndex, aSheet, mAdoptedStyleSheets);
159 void DocumentOrShadowRoot::OnDeleteAdoptedStyleSheets(StyleSheet& aSheet,
160 uint32_t aIndex,
161 ErrorResult&) {
162 MOZ_ASSERT(mAdoptedStyleSheets.ElementAt(aIndex) == &aSheet);
163 mAdoptedStyleSheets.RemoveElementAt(aIndex);
164 auto existingIndex = mAdoptedStyleSheets.LastIndexOf(&aSheet);
165 if (existingIndex != mAdoptedStyleSheets.NoIndex && existingIndex >= aIndex) {
166 // The sheet is still adopted by us and was already later from the one we're
167 // removing, so nothing to do.
168 return;
171 RemoveSheetFromStylesIfApplicable(aSheet);
172 if (existingIndex == mAdoptedStyleSheets.NoIndex) {
173 // The sheet is no longer adopted by us.
174 aSheet.RemoveAdopter(*this);
175 } else if (aSheet.IsApplicable()) {
176 // We need to re-insert the sheet at the right (pre-existing) index.
177 nsINode& node = AsNode();
178 if (mKind == Kind::Document) {
179 node.AsDocument()->AddStyleSheetToStyleSets(aSheet);
180 } else {
181 ShadowRoot::FromNode(node)->InsertSheetIntoAuthorData(
182 existingIndex, aSheet, mAdoptedStyleSheets);
187 void DocumentOrShadowRoot::ClearAdoptedStyleSheets() {
188 auto* shadow = ShadowRoot::FromNode(AsNode());
189 auto* doc = shadow ? nullptr : AsNode().AsDocument();
190 MOZ_ASSERT(shadow || doc);
191 IgnoredErrorResult rv;
192 while (!mAdoptedStyleSheets.IsEmpty()) {
193 if (shadow) {
194 ShadowRoot_Binding::AdoptedStyleSheetsHelpers::RemoveLastElement(shadow,
195 rv);
196 } else {
197 Document_Binding::AdoptedStyleSheetsHelpers::RemoveLastElement(doc, rv);
199 MOZ_DIAGNOSTIC_ASSERT(!rv.Failed(), "Removal doesn't fail");
203 void DocumentOrShadowRoot::CloneAdoptedSheetsFrom(
204 const DocumentOrShadowRoot& aSource) {
205 if (aSource.mAdoptedStyleSheets.IsEmpty()) {
206 return;
209 Document& ownerDoc = *AsNode().OwnerDoc();
210 const Document& sourceDoc = *aSource.AsNode().OwnerDoc();
211 auto* clonedSheetMap = static_cast<Document::AdoptedStyleSheetCloneCache*>(
212 sourceDoc.GetProperty(nsGkAtoms::adoptedsheetclones));
213 MOZ_ASSERT(clonedSheetMap);
215 // We don't need to care about the reflector (AdoptedStyleSheetsHelpers and
216 // so) because this is only used for static documents.
217 for (const StyleSheet* sheet : aSource.mAdoptedStyleSheets) {
218 RefPtr<StyleSheet> clone = clonedSheetMap->LookupOrInsertWith(
219 sheet, [&] { return sheet->CloneAdoptedSheet(ownerDoc); });
220 MOZ_ASSERT(clone);
221 MOZ_DIAGNOSTIC_ASSERT(clone->ConstructorDocumentMatches(ownerDoc));
222 ErrorResult rv;
223 OnSetAdoptedStyleSheets(*clone, mAdoptedStyleSheets.Length(), rv);
224 MOZ_ASSERT(!rv.Failed());
228 Element* DocumentOrShadowRoot::GetElementById(
229 const nsAString& aElementId) const {
230 if (MOZ_UNLIKELY(aElementId.IsEmpty())) {
231 ReportEmptyGetElementByIdArg();
232 return nullptr;
235 if (IdentifierMapEntry* entry = mIdentifierMap.GetEntry(aElementId)) {
236 return entry->GetIdElement();
239 return nullptr;
242 Element* DocumentOrShadowRoot::GetElementById(nsAtom* aElementId) const {
243 if (MOZ_UNLIKELY(aElementId == nsGkAtoms::_empty)) {
244 ReportEmptyGetElementByIdArg();
245 return nullptr;
248 if (IdentifierMapEntry* entry = mIdentifierMap.GetEntry(aElementId)) {
249 return entry->GetIdElement();
252 return nullptr;
255 already_AddRefed<nsContentList> DocumentOrShadowRoot::GetElementsByTagNameNS(
256 const nsAString& aNamespaceURI, const nsAString& aLocalName) {
257 ErrorResult rv;
258 RefPtr<nsContentList> list =
259 GetElementsByTagNameNS(aNamespaceURI, aLocalName, rv);
260 if (rv.Failed()) {
261 return nullptr;
263 return list.forget();
266 already_AddRefed<nsContentList> DocumentOrShadowRoot::GetElementsByTagNameNS(
267 const nsAString& aNamespaceURI, const nsAString& aLocalName,
268 ErrorResult& aResult) {
269 int32_t nameSpaceId = kNameSpaceID_Wildcard;
271 if (!aNamespaceURI.EqualsLiteral("*")) {
272 aResult = nsNameSpaceManager::GetInstance()->RegisterNameSpace(
273 aNamespaceURI, nameSpaceId);
274 if (aResult.Failed()) {
275 return nullptr;
279 NS_ASSERTION(nameSpaceId != kNameSpaceID_Unknown, "Unexpected namespace ID!");
280 return NS_GetContentList(&AsNode(), nameSpaceId, aLocalName);
283 already_AddRefed<nsContentList> DocumentOrShadowRoot::GetElementsByClassName(
284 const nsAString& aClasses) {
285 return nsContentUtils::GetElementsByClassName(&AsNode(), aClasses);
288 nsINode* DocumentOrShadowRoot::Retarget(nsINode* aNode) const {
289 for (nsINode* cur = aNode; cur; cur = cur->GetContainingShadowHost()) {
290 if (cur->SubtreeRoot() == &AsNode()) {
291 return cur;
294 return nullptr;
297 Element* DocumentOrShadowRoot::GetRetargetedFocusedElement() {
298 auto* content = AsNode().OwnerDoc()->GetUnretargetedFocusedContent();
299 if (!content) {
300 return nullptr;
302 if (nsINode* retarget = Retarget(content)) {
303 return retarget->AsElement();
305 return nullptr;
308 Element* DocumentOrShadowRoot::GetPointerLockElement() {
309 nsCOMPtr<Element> pointerLockedElement =
310 PointerLockManager::GetLockedElement();
311 return Element::FromNodeOrNull(Retarget(pointerLockedElement));
314 Element* DocumentOrShadowRoot::GetFullscreenElement() const {
315 if (!AsNode().IsInComposedDoc()) {
316 return nullptr;
319 Element* element = AsNode().OwnerDoc()->GetUnretargetedFullscreenElement();
320 NS_ASSERTION(!element || element->State().HasState(ElementState::FULLSCREEN),
321 "Fullscreen element should have fullscreen styles applied");
322 return Element::FromNodeOrNull(Retarget(element));
325 namespace {
327 using FrameForPointOption = nsLayoutUtils::FrameForPointOption;
328 using FrameForPointOptions = nsLayoutUtils::FrameForPointOptions;
330 // Whether only one node or multiple nodes is requested.
331 enum class Multiple {
333 Yes,
336 // Whether we should flush layout or not.
337 enum class FlushLayout {
339 Yes,
342 enum class PerformRetargeting {
344 Yes,
347 template <typename NodeOrElement>
348 NodeOrElement* CastTo(nsINode*);
350 template <>
351 Element* CastTo<Element>(nsINode* aNode) {
352 return aNode->AsElement();
355 template <>
356 nsINode* CastTo<nsINode>(nsINode* aNode) {
357 return aNode;
360 template <typename NodeOrElement>
361 static void QueryNodesFromRect(DocumentOrShadowRoot& aRoot, const nsRect& aRect,
362 FrameForPointOptions aOptions,
363 FlushLayout aShouldFlushLayout,
364 Multiple aMultiple, ViewportType aViewportType,
365 PerformRetargeting aPerformRetargeting,
366 nsTArray<RefPtr<NodeOrElement>>& aNodes) {
367 static_assert(std::is_same<nsINode, NodeOrElement>::value ||
368 std::is_same<Element, NodeOrElement>::value,
369 "Should returning nodes or elements");
371 constexpr bool returningElements =
372 std::is_same<Element, NodeOrElement>::value;
373 const bool retargeting = aPerformRetargeting == PerformRetargeting::Yes;
375 nsCOMPtr<Document> doc = aRoot.AsNode().OwnerDoc();
377 // Make sure the layout information we get is up-to-date, and
378 // ensure we get a root frame (for everything but XUL)
379 if (aShouldFlushLayout == FlushLayout::Yes) {
380 doc->FlushPendingNotifications(FlushType::Layout);
383 PresShell* presShell = doc->GetPresShell();
384 if (!presShell) {
385 return;
388 nsIFrame* rootFrame = presShell->GetRootFrame();
389 // XUL docs, unlike HTML, have no frame tree until everything's done loading
390 if (!rootFrame) {
391 return; // return null to premature XUL callers as a reminder to wait
394 aOptions.mBits += FrameForPointOption::IgnorePaintSuppression;
395 aOptions.mBits += FrameForPointOption::IgnoreCrossDoc;
397 AutoTArray<nsIFrame*, 8> frames;
398 nsLayoutUtils::GetFramesForArea({rootFrame, aViewportType}, aRect, frames,
399 aOptions);
401 for (nsIFrame* frame : frames) {
402 nsINode* node = doc->GetContentInThisDocument(frame);
403 while (node && node->IsInNativeAnonymousSubtree()) {
404 nsIContent* root = node->GetClosestNativeAnonymousSubtreeRoot();
405 MOZ_ASSERT(root, "content is connected");
406 MOZ_ASSERT(root->IsRootOfNativeAnonymousSubtree(), "wat");
407 if (root == &aRoot.AsNode()) {
408 // If we're in the anonymous subtree root we care about, don't retarget.
409 break;
411 node = root->GetParentOrShadowHostNode();
414 if (!node) {
415 continue;
418 if (returningElements && !node->IsElement()) {
419 // If this helper is called via ElementsFromPoint, we need to make sure
420 // our frame is an element. Otherwise return whatever the top frame is
421 // even if it isn't the top-painted element.
422 // SVG 'text' element's SVGTextFrame doesn't respond to hit-testing, so
423 // if 'content' is a child of such an element then we need to manually
424 // defer to the parent here.
425 if (aMultiple == Multiple::Yes && !frame->IsInSVGTextSubtree()) {
426 continue;
429 node = node->GetParent();
430 if (ShadowRoot* shadow = ShadowRoot::FromNodeOrNull(node)) {
431 node = shadow->Host();
435 // XXXsmaug There is plenty of unspec'ed behavior here
436 // https://github.com/w3c/webcomponents/issues/735
437 // https://github.com/w3c/webcomponents/issues/736
438 if (retargeting) {
439 node = aRoot.Retarget(node);
442 if (node && node != aNodes.SafeLastElement(nullptr)) {
443 aNodes.AppendElement(CastTo<NodeOrElement>(node));
444 if (aMultiple == Multiple::No) {
445 return;
451 template <typename NodeOrElement>
452 static void QueryNodesFromPoint(DocumentOrShadowRoot& aRoot, float aX, float aY,
453 FrameForPointOptions aOptions,
454 FlushLayout aShouldFlushLayout,
455 Multiple aMultiple, ViewportType aViewportType,
456 PerformRetargeting aPerformRetargeting,
457 nsTArray<RefPtr<NodeOrElement>>& aNodes) {
458 // As per the spec, we return null if either coord is negative.
459 if (!aOptions.mBits.contains(FrameForPointOption::IgnoreRootScrollFrame) &&
460 (aX < 0 || aY < 0)) {
461 return;
464 nscoord x = nsPresContext::CSSPixelsToAppUnits(aX);
465 nscoord y = nsPresContext::CSSPixelsToAppUnits(aY);
466 nsPoint pt(x, y);
467 QueryNodesFromRect(aRoot, nsRect(pt, nsSize(1, 1)), aOptions,
468 aShouldFlushLayout, aMultiple, aViewportType,
469 aPerformRetargeting, aNodes);
472 } // namespace
474 Element* DocumentOrShadowRoot::ElementFromPoint(float aX, float aY) {
475 return ElementFromPointHelper(aX, aY, false, true, ViewportType::Layout);
478 void DocumentOrShadowRoot::ElementsFromPoint(
479 float aX, float aY, nsTArray<RefPtr<Element>>& aElements) {
480 QueryNodesFromPoint(*this, aX, aY, {}, FlushLayout::Yes, Multiple::Yes,
481 ViewportType::Layout, PerformRetargeting::Yes, aElements);
484 void DocumentOrShadowRoot::NodesFromPoint(float aX, float aY,
485 nsTArray<RefPtr<nsINode>>& aNodes) {
486 QueryNodesFromPoint(*this, aX, aY, {}, FlushLayout::Yes, Multiple::Yes,
487 ViewportType::Layout, PerformRetargeting::Yes, aNodes);
490 nsINode* DocumentOrShadowRoot::NodeFromPoint(float aX, float aY) {
491 AutoTArray<RefPtr<nsINode>, 1> nodes;
492 QueryNodesFromPoint(*this, aX, aY, {}, FlushLayout::Yes, Multiple::No,
493 ViewportType::Layout, PerformRetargeting::Yes, nodes);
494 return nodes.SafeElementAt(0);
497 Element* DocumentOrShadowRoot::ElementFromPointHelper(
498 float aX, float aY, bool aIgnoreRootScrollFrame, bool aFlushLayout,
499 ViewportType aViewportType) {
500 EnumSet<FrameForPointOption> options;
501 if (aIgnoreRootScrollFrame) {
502 options += FrameForPointOption::IgnoreRootScrollFrame;
505 auto flush = aFlushLayout ? FlushLayout::Yes : FlushLayout::No;
507 AutoTArray<RefPtr<Element>, 1> elements;
508 QueryNodesFromPoint(*this, aX, aY, options, flush, Multiple::No,
509 aViewportType, PerformRetargeting::Yes, elements);
510 return elements.SafeElementAt(0);
513 void DocumentOrShadowRoot::NodesFromRect(float aX, float aY, float aTopSize,
514 float aRightSize, float aBottomSize,
515 float aLeftSize,
516 bool aIgnoreRootScrollFrame,
517 bool aFlushLayout, bool aOnlyVisible,
518 float aVisibleThreshold,
519 nsTArray<RefPtr<nsINode>>& aReturn) {
520 // Following the same behavior of elementFromPoint,
521 // we don't return anything if either coord is negative
522 if (!aIgnoreRootScrollFrame && (aX < 0 || aY < 0)) {
523 return;
526 nscoord x = nsPresContext::CSSPixelsToAppUnits(aX - aLeftSize);
527 nscoord y = nsPresContext::CSSPixelsToAppUnits(aY - aTopSize);
528 nscoord w = nsPresContext::CSSPixelsToAppUnits(aLeftSize + aRightSize) + 1;
529 nscoord h = nsPresContext::CSSPixelsToAppUnits(aTopSize + aBottomSize) + 1;
531 nsRect rect(x, y, w, h);
533 FrameForPointOptions options;
534 if (aIgnoreRootScrollFrame) {
535 options.mBits += FrameForPointOption::IgnoreRootScrollFrame;
537 if (aOnlyVisible) {
538 options.mBits += FrameForPointOption::OnlyVisible;
539 options.mVisibleThreshold = aVisibleThreshold;
542 auto flush = aFlushLayout ? FlushLayout::Yes : FlushLayout::No;
543 QueryNodesFromRect(*this, rect, options, flush, Multiple::Yes,
544 ViewportType::Layout, PerformRetargeting::No, aReturn);
547 Element* DocumentOrShadowRoot::AddIDTargetObserver(nsAtom* aID,
548 IDTargetObserver aObserver,
549 void* aData,
550 bool aForImage) {
551 nsDependentAtomString id(aID);
553 if (!CheckGetElementByIdArg(id)) {
554 return nullptr;
557 IdentifierMapEntry* entry = mIdentifierMap.PutEntry(aID);
558 NS_ENSURE_TRUE(entry, nullptr);
560 entry->AddContentChangeCallback(aObserver, aData, aForImage);
561 return aForImage ? entry->GetImageIdElement() : entry->GetIdElement();
564 void DocumentOrShadowRoot::RemoveIDTargetObserver(nsAtom* aID,
565 IDTargetObserver aObserver,
566 void* aData, bool aForImage) {
567 nsDependentAtomString id(aID);
569 if (!CheckGetElementByIdArg(id)) {
570 return;
573 IdentifierMapEntry* entry = mIdentifierMap.GetEntry(aID);
574 if (!entry) {
575 return;
578 entry->RemoveContentChangeCallback(aObserver, aData, aForImage);
581 Element* DocumentOrShadowRoot::LookupImageElement(const nsAString& aId) {
582 if (aId.IsEmpty()) {
583 return nullptr;
586 IdentifierMapEntry* entry = mIdentifierMap.GetEntry(aId);
587 return entry ? entry->GetImageIdElement() : nullptr;
590 void DocumentOrShadowRoot::ReportEmptyGetElementByIdArg() const {
591 nsContentUtils::ReportEmptyGetElementByIdArg(AsNode().OwnerDoc());
594 void DocumentOrShadowRoot::GetAnimations(
595 nsTArray<RefPtr<Animation>>& aAnimations) {
596 // As with Element::GetAnimations we initially flush style here.
597 // This should ensure that there are no subsequent changes to the tree
598 // structure while iterating over the children below.
599 if (Document* doc = AsNode().GetComposedDoc()) {
600 doc->FlushPendingNotifications(
601 ChangesToFlush(FlushType::Style, false /* flush animations */));
604 GetAnimationsOptions options;
605 options.mSubtree = true;
607 for (RefPtr<nsIContent> child = AsNode().GetFirstChild(); child;
608 child = child->GetNextSibling()) {
609 if (RefPtr<Element> element = Element::FromNode(child)) {
610 nsTArray<RefPtr<Animation>> result;
611 element->GetAnimationsWithoutFlush(options, result);
612 aAnimations.AppendElements(std::move(result));
616 aAnimations.Sort(AnimationPtrComparator<RefPtr<Animation>>());
619 int32_t DocumentOrShadowRoot::StyleOrderIndexOfSheet(
620 const StyleSheet& aSheet) const {
621 if (aSheet.IsConstructed()) {
622 // NOTE: constructable sheets can have duplicates, so we need to start
623 // looking from behind.
624 int32_t index = mAdoptedStyleSheets.LastIndexOf(&aSheet);
625 return (index < 0) ? index : index + SheetCount();
627 return mStyleSheets.IndexOf(&aSheet);
630 void DocumentOrShadowRoot::TraverseSheetRefInStylesIfApplicable(
631 StyleSheet& aSheet, nsCycleCollectionTraversalCallback& cb) {
632 if (!aSheet.IsApplicable()) {
633 return;
635 if (mKind == Kind::ShadowRoot) {
636 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mServoStyles->sheets[i]");
637 cb.NoteXPCOMChild(&aSheet);
638 } else if (AsNode().AsDocument()->StyleSetFilled()) {
639 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
640 cb, "mStyleSet->mRawSet.stylist.stylesheets.<origin>[i]");
641 cb.NoteXPCOMChild(&aSheet);
645 void DocumentOrShadowRoot::TraverseStyleSheets(
646 nsTArray<RefPtr<StyleSheet>>& aSheets, const char* aEdgeName,
647 nsCycleCollectionTraversalCallback& cb) {
648 MOZ_ASSERT(aEdgeName);
649 MOZ_ASSERT(&aSheets != &mAdoptedStyleSheets);
650 for (StyleSheet* sheet : aSheets) {
651 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, aEdgeName);
652 cb.NoteXPCOMChild(sheet);
653 TraverseSheetRefInStylesIfApplicable(*sheet, cb);
657 void DocumentOrShadowRoot::Traverse(DocumentOrShadowRoot* tmp,
658 nsCycleCollectionTraversalCallback& cb) {
659 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMStyleSheets)
660 tmp->TraverseStyleSheets(tmp->mStyleSheets, "mStyleSheets[i]", cb);
662 tmp->EnumerateUniqueAdoptedStyleSheetsBackToFront([&](StyleSheet& aSheet) {
663 tmp->TraverseSheetRefInStylesIfApplicable(aSheet, cb);
665 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAdoptedStyleSheets);
667 for (auto iter = tmp->mIdentifierMap.Iter(); !iter.Done(); iter.Next()) {
668 iter.Get()->Traverse(&cb);
672 void DocumentOrShadowRoot::UnlinkStyleSheets(
673 nsTArray<RefPtr<StyleSheet>>& aSheets) {
674 MOZ_ASSERT(&aSheets != &mAdoptedStyleSheets);
675 for (StyleSheet* sheet : aSheets) {
676 sheet->ClearAssociatedDocumentOrShadowRoot();
677 RemoveSheetFromStylesIfApplicable(*sheet);
679 aSheets.Clear();
682 void DocumentOrShadowRoot::Unlink(DocumentOrShadowRoot* tmp) {
683 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMStyleSheets);
684 tmp->UnlinkStyleSheets(tmp->mStyleSheets);
685 tmp->EnumerateUniqueAdoptedStyleSheetsBackToFront([&](StyleSheet& aSheet) {
686 aSheet.RemoveAdopter(*tmp);
687 tmp->RemoveSheetFromStylesIfApplicable(aSheet);
689 NS_IMPL_CYCLE_COLLECTION_UNLINK(mAdoptedStyleSheets);
690 tmp->mIdentifierMap.Clear();
693 } // namespace mozilla::dom