tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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:
Mdom/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>>()); }