commit ee65bfe048ba6c2ea6a42436f493175538e185d7
parent 86bc5091a8d9df1cdc52d1977048e944ef552e22
Author: Boris Chiou <boris.chiou@gmail.com>
Date: Wed, 29 Oct 2025 21:25:41 +0000
Bug 1996006 - Make Element.getAnimations({subtree: true}) works for view transition pseudo-elements. r=view-transitions-reviewers,layout-reviewers,emilio
This is for internal usage, e.g. DevTools, because those view transition
pseudo-elements are not accessible to content.
Differential Revision: https://phabricator.services.mozilla.com/D270264
Diffstat:
| M | dom/base/Element.cpp | | | 87 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------- |
1 file changed, 62 insertions(+), 25 deletions(-)
diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp
@@ -4147,27 +4147,68 @@ static void GetAnimationsUnsorted(const Element* aElement,
}
}
-// This traverses all the view transition pseudo-elements and get all of the
-// animations on them.
-static void GetAnimationsOfViewTransitionTree(
- const Element* aOriginatingElement,
- nsTArray<RefPtr<Animation>>& aAnimations) {
- if (!aOriginatingElement->IsRootElement()) {
+static inline bool IsSupportedForGetAnimationsSubtree(PseudoStyleType aType) {
+ return aType == PseudoStyleType::NotPseudo ||
+ aType == PseudoStyleType::mozSnapshotContainingBlock ||
+ PseudoStyle::IsViewTransitionPseudoElement(aType);
+}
+
+// This traverses the subtree from the root, |aRootElement|, to get the
+// animations.
+static void GetAnimationsUnsortedForSubtree(
+ const Element* aRootElement, nsTArray<RefPtr<Animation>>& aAnimations) {
+ const PseudoStyleType type = aRootElement->GetPseudoElementType();
+ // Only elements and view transition pseudo-elements get handled in this
+ // function.
+ if (MOZ_UNLIKELY(!IsSupportedForGetAnimationsSubtree(type))) {
return;
}
- const Document* doc = aOriginatingElement->OwnerDoc();
- const ViewTransition* vt = doc->GetActiveViewTransition();
- if (!vt) {
+ // For non pseudo-elements, we have to get the animations on the element
+ // itself, ::before, ::after, and ::marker.
+ if (type == PseudoStyleType::NotPseudo) {
+ for (const nsIContent* node = aRootElement; node;
+ node = node->GetNextNode(aRootElement)) {
+ if (!node->IsElement()) {
+ continue;
+ }
+ const Element* element = node->AsElement();
+ GetAnimationsUnsorted(element, PseudoStyleRequest::NotPseudo(),
+ aAnimations);
+ GetAnimationsUnsorted(element, PseudoStyleRequest::Before(), aAnimations);
+ GetAnimationsUnsorted(element, PseudoStyleRequest::After(), aAnimations);
+ GetAnimationsUnsorted(element, PseudoStyleRequest::Marker(), aAnimations);
+ }
+ }
+
+ // If |aRootElement| is the document element, or it is a view transition
+ // pseudo-element (including the snapshot containing block), we have to
+ // traverse the view transition subtree. Otherwise, we can skip the traversal.
+ if (!aRootElement->IsRootElement() && type == PseudoStyleType::NotPseudo) {
return;
}
- Element* root = vt->GetViewTransitionTreeRoot();
- if (!root) {
+ const Document* doc = aRootElement->OwnerDoc();
+ const Element* originatingElement = doc->GetRootElement();
+ if (!originatingElement) {
return;
}
- for (const nsIContent* node = root; node; node = node->GetNextNode(root)) {
+ const Element* rootForTraversal = [&]() -> const Element* {
+ if (!aRootElement->IsRootElement()) {
+ // It is in the view transition pseudo-element tree already, so we use it
+ // directly.
+ return aRootElement;
+ }
+ // View transition pseudo-elements cannot be accessed directly from the
+ // document element, so we have to retrieve its tree root from the active
+ // view transition object.
+ const ViewTransition* vt = doc->GetActiveViewTransition();
+ return vt ? vt->GetViewTransitionTreeRoot() : nullptr;
+ }();
+
+ for (const nsIContent* node = rootForTraversal; node;
+ node = node->GetNextNode(rootForTraversal)) {
if (!node->IsElement()) {
continue;
}
@@ -4177,7 +4218,7 @@ static void GetAnimationsOfViewTransitionTree(
pseudo->HasName()
? pseudo->GetParsedAttr(nsGkAtoms::name)->GetAtomValue()
: nullptr);
- GetAnimationsUnsorted(aOriginatingElement, request, aAnimations);
+ GetAnimationsUnsorted(originatingElement, request, aAnimations);
}
}
@@ -4207,20 +4248,16 @@ void Element::GetAnimationsWithoutFlush(
if (!aOptions.mSubtree || (pseudoRequest.mType == PseudoStyleType::before ||
pseudoRequest.mType == PseudoStyleType::after ||
pseudoRequest.mType == PseudoStyleType::marker)) {
+ // Case 1: Non-subtree, or |this| is ::before, ::after, or ::marker.
+ //
+ // ::before, ::after, and ::marker doesn't have subtree on themself, so we
+ // just simply get the animations of the element itself even if mSubtree is
+ // true.
GetAnimationsUnsorted(elem, pseudoRequest, aAnimations);
} else {
- for (nsIContent* node = this; node; node = node->GetNextNode(this)) {
- if (!node->IsElement()) {
- continue;
- }
- Element* element = node->AsElement();
- GetAnimationsUnsorted(element, PseudoStyleRequest::NotPseudo(),
- aAnimations);
- GetAnimationsUnsorted(element, PseudoStyleRequest::Before(), aAnimations);
- GetAnimationsUnsorted(element, PseudoStyleRequest::After(), aAnimations);
- GetAnimationsUnsorted(element, PseudoStyleRequest::Marker(), aAnimations);
- }
- GetAnimationsOfViewTransitionTree(this, aAnimations);
+ // Case 2: Subtree. |this| is an element or a view transition
+ // pseudo-element.
+ GetAnimationsUnsortedForSubtree(elem, aAnimations);
}
aAnimations.Sort(AnimationPtrComparator<RefPtr<Animation>>());
}