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.
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 "
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
);
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.
155 if (aSheet
.IsApplicable()) {
156 if (mKind
== Kind::Document
) {
157 doc
.AddStyleSheetToStyleSets(aSheet
);
159 shadow
->InsertSheetIntoAuthorData(aIndex
, aSheet
, mAdoptedStyleSheets
);
164 void DocumentOrShadowRoot::OnDeleteAdoptedStyleSheets(StyleSheet
& aSheet
,
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.
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
);
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()) {
199 ShadowRoot_Binding::AdoptedStyleSheetsHelpers::RemoveLastElement(shadow
,
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()) {
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
); });
226 MOZ_DIAGNOSTIC_ASSERT(clone
->ConstructorDocumentMatches(ownerDoc
));
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();
240 if (IdentifierMapEntry
* entry
= mIdentifierMap
.GetEntry(aElementId
)) {
241 return entry
->GetIdElement();
247 Element
* DocumentOrShadowRoot::GetElementById(nsAtom
* aElementId
) const {
248 if (MOZ_UNLIKELY(aElementId
== nsGkAtoms::_empty
)) {
249 ReportEmptyGetElementByIdArg();
253 if (IdentifierMapEntry
* entry
= mIdentifierMap
.GetEntry(aElementId
)) {
254 return entry
->GetIdElement();
260 already_AddRefed
<nsContentList
> DocumentOrShadowRoot::GetElementsByTagNameNS(
261 const nsAString
& aNamespaceURI
, const nsAString
& aLocalName
) {
263 RefPtr
<nsContentList
> list
=
264 GetElementsByTagNameNS(aNamespaceURI
, aLocalName
, rv
);
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()) {
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()) {
302 Element
* DocumentOrShadowRoot::GetRetargetedFocusedElement() {
303 auto* content
= AsNode().OwnerDoc()->GetUnretargetedFocusedContent();
307 if (nsINode
* retarget
= Retarget(content
)) {
308 return retarget
->AsElement();
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()) {
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
));
332 using FrameForPointOption
= nsLayoutUtils::FrameForPointOption
;
333 using FrameForPointOptions
= nsLayoutUtils::FrameForPointOptions
;
335 // Whether only one node or multiple nodes is requested.
336 enum class Multiple
{
341 // Whether we should flush layout or not.
342 enum class FlushLayout
{
347 enum class PerformRetargeting
{
352 template <typename NodeOrElement
>
353 NodeOrElement
* CastTo(nsINode
*);
356 Element
* CastTo
<Element
>(nsINode
* aNode
) {
357 return aNode
->AsElement();
361 nsINode
* CastTo
<nsINode
>(nsINode
* 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();
393 nsIFrame
* rootFrame
= presShell
->GetRootFrame();
394 // XUL docs, unlike HTML, have no frame tree until everything's done loading
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
,
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.
416 node
= root
->GetParentOrShadowHostNode();
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()) {
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
444 node
= aRoot
.Retarget(node
);
447 if (node
&& node
!= aNodes
.SafeLastElement(nullptr)) {
448 aNodes
.AppendElement(CastTo
<NodeOrElement
>(node
));
449 if (aMultiple
== Multiple::No
) {
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)) {
469 nscoord x
= nsPresContext::CSSPixelsToAppUnits(aX
);
470 nscoord y
= nsPresContext::CSSPixelsToAppUnits(aY
);
472 QueryNodesFromRect(aRoot
, nsRect(pt
, nsSize(1, 1)), aOptions
,
473 aShouldFlushLayout
, aMultiple
, aViewportType
,
474 aPerformRetargeting
, aNodes
);
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
,
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)) {
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
;
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
,
558 nsDependentAtomString
id(aID
);
560 if (!CheckGetElementByIdArg(id
)) {
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
)) {
580 IdentifierMapEntry
* entry
= mIdentifierMap
.GetEntry(aID
);
585 entry
->RemoveContentChangeCallback(aObserver
, aData
, aForImage
);
588 Element
* DocumentOrShadowRoot::LookupImageElement(const nsAString
& aId
) {
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()) {
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
);
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