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
) {
33 DocumentOrShadowRoot::DocumentOrShadowRoot(Document
* aDoc
)
34 : mAsNode(aDoc
), mKind(Kind::Document
) {
38 void DocumentOrShadowRoot::AddSizeOfOwnedSheetArrayExcludingThis(
39 nsWindowSizes
& aSizes
, const nsTArray
<RefPtr
<StyleSheet
>>& aSheets
) const {
41 n
+= aSheets
.ShallowSizeOfExcludingThis(aSizes
.mState
.mMallocSizeOf
);
42 for (StyleSheet
* sheet
: aSheets
) {
43 if (!sheet
->GetAssociatedDocumentOrShadowRoot()) {
44 // Avoid over-reporting shared sheets.
47 n
+= sheet
->SizeOfIncludingThis(aSizes
.mState
.mMallocSizeOf
);
50 if (mKind
== Kind::ShadowRoot
) {
51 aSizes
.mLayoutShadowDomStyleSheetsSize
+= n
;
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());
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(
100 if (!aSheet
.IsApplicable()) {
103 if (mKind
== Kind::Document
) {
104 AsNode().AsDocument()->RemoveStyleSheetFromStyleSets(aSheet
);
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
,
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 "
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
);
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.
150 if (aSheet
.IsApplicable()) {
151 if (mKind
== Kind::Document
) {
152 doc
.AddStyleSheetToStyleSets(aSheet
);
154 shadow
->InsertSheetIntoAuthorData(aIndex
, aSheet
, mAdoptedStyleSheets
);
159 void DocumentOrShadowRoot::OnDeleteAdoptedStyleSheets(StyleSheet
& aSheet
,
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.
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
);
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()) {
194 ShadowRoot_Binding::AdoptedStyleSheetsHelpers::RemoveLastElement(shadow
,
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()) {
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
); });
221 MOZ_DIAGNOSTIC_ASSERT(clone
->ConstructorDocumentMatches(ownerDoc
));
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();
235 if (IdentifierMapEntry
* entry
= mIdentifierMap
.GetEntry(aElementId
)) {
236 return entry
->GetIdElement();
242 Element
* DocumentOrShadowRoot::GetElementById(nsAtom
* aElementId
) const {
243 if (MOZ_UNLIKELY(aElementId
== nsGkAtoms::_empty
)) {
244 ReportEmptyGetElementByIdArg();
248 if (IdentifierMapEntry
* entry
= mIdentifierMap
.GetEntry(aElementId
)) {
249 return entry
->GetIdElement();
255 already_AddRefed
<nsContentList
> DocumentOrShadowRoot::GetElementsByTagNameNS(
256 const nsAString
& aNamespaceURI
, const nsAString
& aLocalName
) {
258 RefPtr
<nsContentList
> list
=
259 GetElementsByTagNameNS(aNamespaceURI
, aLocalName
, rv
);
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()) {
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()) {
297 Element
* DocumentOrShadowRoot::GetRetargetedFocusedElement() {
298 auto* content
= AsNode().OwnerDoc()->GetUnretargetedFocusedContent();
302 if (nsINode
* retarget
= Retarget(content
)) {
303 return retarget
->AsElement();
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()) {
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
));
327 using FrameForPointOption
= nsLayoutUtils::FrameForPointOption
;
328 using FrameForPointOptions
= nsLayoutUtils::FrameForPointOptions
;
330 // Whether only one node or multiple nodes is requested.
331 enum class Multiple
{
336 // Whether we should flush layout or not.
337 enum class FlushLayout
{
342 enum class PerformRetargeting
{
347 template <typename NodeOrElement
>
348 NodeOrElement
* CastTo(nsINode
*);
351 Element
* CastTo
<Element
>(nsINode
* aNode
) {
352 return aNode
->AsElement();
356 nsINode
* CastTo
<nsINode
>(nsINode
* 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();
388 nsIFrame
* rootFrame
= presShell
->GetRootFrame();
389 // XUL docs, unlike HTML, have no frame tree until everything's done loading
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
,
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.
411 node
= root
->GetParentOrShadowHostNode();
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()) {
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
439 node
= aRoot
.Retarget(node
);
442 if (node
&& node
!= aNodes
.SafeLastElement(nullptr)) {
443 aNodes
.AppendElement(CastTo
<NodeOrElement
>(node
));
444 if (aMultiple
== Multiple::No
) {
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)) {
464 nscoord x
= nsPresContext::CSSPixelsToAppUnits(aX
);
465 nscoord y
= nsPresContext::CSSPixelsToAppUnits(aY
);
467 QueryNodesFromRect(aRoot
, nsRect(pt
, nsSize(1, 1)), aOptions
,
468 aShouldFlushLayout
, aMultiple
, aViewportType
,
469 aPerformRetargeting
, aNodes
);
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
,
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)) {
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
;
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
,
551 nsDependentAtomString
id(aID
);
553 if (!CheckGetElementByIdArg(id
)) {
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
)) {
573 IdentifierMapEntry
* entry
= mIdentifierMap
.GetEntry(aID
);
578 entry
->RemoveContentChangeCallback(aObserver
, aData
, aForImage
);
581 Element
* DocumentOrShadowRoot::LookupImageElement(const nsAString
& aId
) {
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()) {
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
);
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