tor-browser

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

commit e90de811ad43a548a3d8fc555d574947986b7142
parent 312ba65ad0c509517d8222f91283202f291c26c8
Author: David Awogbemila <awogbemila@chromium.org>
Date:   Thu,  8 Jan 2026 17:36:45 +0000

Bug 2008953 [wpt PR 57042] - [animation-trigger] Update attachments when trigger-scope changes, a=testonly

Automatic update from web-platform-tests
[animation-trigger] Update attachments when trigger-scope changes

This patch modifies the trigger attachment code so that triggers and
their attached animations are updated accordingly when a related
trigger-scope rule changes.

The CL modifies the FragmentBuilder propagation so that during a
trigger-scope change on an ancestor of the trigger source, even if the
trigger source isn't itself re-laid out, its ancestors will update their
named_trigger maps with the up-to-date DOM scoping, rather than use the
stale map of the source.

Bug: 390314945, 466134208
Change-Id: I407cadbab965917b49011c6b23ef02cb4522f1fc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7237813
Commit-Queue: David Awogbemila <awogbemila@chromium.org>
Reviewed-by: Anders Hartvoll Ruud <andruud@chromium.org>
Reviewed-by: David Awogbemila <awogbemila@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1565614}

--

wpt-commits: 3d27afe98884c2d9a9f99dd53ceacb0545fc53f2
wpt-pr: 57042

Diffstat:
Atesting/web-platform/tests/scroll-animations/animation-trigger/trigger-scope-add.tentative.html | 195+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 195 insertions(+), 0 deletions(-)

diff --git a/testing/web-platform/tests/scroll-animations/animation-trigger/trigger-scope-add.tentative.html b/testing/web-platform/tests/scroll-animations/animation-trigger/trigger-scope-add.tentative.html @@ -0,0 +1,195 @@ +<!DOCTYPE html> +<html> + <head> + <link rel="help" src="https://drafts.csswg.org/css-animations-2/#trigger-scope"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/web-animations/testcommon.js"></script> + <script src="/dom/events/scrolling/scroll_support.js"></script> + <script src="support/support.js"></script> + <script src="support/trigger-scope-support.js"></script> + </head> + <body> + <style> + @keyframes expand { + from { transform: scaleX(1) } + to { transform: scaleX(2) } + } + #outerscroller { + position: relative; + overflow-y: scroll; + border: solid 1px; + height: 400px; + width: 400px; + } + #innerscroller { + position: relative; + overflow-y: scroll; + height: 200px; + width: 200px; + border: solid 1px; + display: block; + } + #source { + position: absolute; + top: 100%; + height: 100px; + width: 100px; + background-color: blue; + timeline-trigger: --trigger view() contain; + } + .target { + background-color: red; + height: 100px; + width: 100px; + animation: expand linear 1s both; + animation-trigger: --trigger play-forwards; + } + + #inner_target { + /* Let's it be in view when the trigger source comes into view */ + margin-top: 50%; + } + + .long { + width: 50%; + height: 100%; + } + </style> + <div id="outerscroller"> + <div id="innerscroller"> + <div id="inner_target" class="target">In-scope Target</div> + <div class="long"></div> + <div class="long"></div> + </div> + <div id="source">SOURCE</div> + <div id="outer_target" class="target"> + Out-of-scope Target + </div> + <div class="long"></div> + <div class="long"></div> + </div> + + <script> + let inner_target = document.getElementById("inner_target"); + let outer_target = document.getElementById("outer_target"); + let source = document.getElementById("source"); + + let current_scroller = outerscroller; + + async function scrollToTrigger() { + const scrollend_promise = + waitForScrollEndFallbackToDelayWithoutScrollEvent(current_scroller); + source.scrollIntoView({block: "center"}); + await scrollend_promise; + await waitForCompositorReady(); + } + + async function resetScrollPositionAndElements(test) { + await waitForScrollReset(test, current_scroller); + + // Reset the animations, so we can detect when they trigger again. + inner_target.getAnimations()[0].pause(); + inner_target.getAnimations()[0].currentTime = 0; + + outer_target.getAnimations()[0].pause(); + outer_target.getAnimations()[0].currentTime = 0; + + await waitForCompositorCommit(); + + // Both inner and outer animations should once again be paused. + await assert_playstate_and_current_time( + inner_target.id, inner_target.getAnimations()[0], "paused"); + await assert_playstate_and_current_time(outer_target.id, + outer_target.getAnimations()[0], + "paused"); + assert_equals(current_scroller.scrollTop, 0, "scroll position reset"); + } + + async function scrollAndAssert(inner_play_state, outer_play_state) { + await scrollToTrigger(); + assert_greater_than(current_scroller.scrollTop, 0, "did scroll"); + + // Both the inner and outer targets should be triggered as there is no + // scope and they are both attached to the trigger. + await assert_playstate_and_current_time(inner_target.id, + inner_target.getAnimations()[0], + inner_play_state); + await assert_playstate_and_current_time(outer_target.id, + outer_target.getAnimations()[0], + outer_play_state); + } + + promise_test(async(test) => { + assert_equals(getComputedStyle(innerscroller).triggerScope, "none"); + await assert_playstate_and_current_time( + inner_target.id, inner_target.getAnimations()[0], "paused"); + await assert_playstate_and_current_time(outer_target.id, + outer_target.getAnimations()[0], + "paused"); + + // Scroll to make the trigger fire. + // Both the inner and outer targets should be triggered as there is no + // scope and they are both attached to the trigger. + await scrollAndAssert(/*inner=*/"running", /*outer=*/"running"); + + // Now, insert a scope. + innerscroller.style.triggerScope = "--trigger"; + + await resetScrollPositionAndElements(test); + + // Scroll to make the trigger fire again. + // Only the outer animation should be triggered as the inner is + // prevented from seeing the trigger sue to the scope. + await scrollAndAssert(/*inner=*/"paused", /*outer=*/"running"); + + // Now, remove the scope. + innerscroller.style.triggerScope = "initial"; + + await resetScrollPositionAndElements(test); + + // Scroll to make the trigger fire again. + // Both the inner and outer targets should be triggered as there is no + // scope and they are both attached to the trigger. + await scrollAndAssert(/*inner=*/"running", /*outer=*/"running"); + }, "Added scope prevents subtree from searching for external trigger"); + + promise_test(async(test) => { + // Move the source within the scope so that inner_target sees the + // trigger when the scope is inserted. And set the scroller to watch to + // innerscroller. + outerscroller.removeChild(source); + innerscroller.append(source); + current_scroller = innerscroller; + + await resetScrollPositionAndElements(test); + + // Scroll to make the trigger fire. + // Both the inner and outer targets should be triggered as there is no + // scope and they are both attached to the trigger. + await scrollAndAssert(/*inner=*/"running", /*outer=*/"running"); + + // Now, insert a scope. + innerscroller.style.triggerScope = "--trigger"; + + await resetScrollPositionAndElements(test); + + // Scroll to make the trigger fire again. + // Only the inner animation should be triggered as the outer is + // prevented from seeing the trigger due to the scope. + await scrollAndAssert(/*inner=*/"running", /*outer=*/"paused"); + + // Now, remove the scope. + innerscroller.style.triggerScope = "initial"; + + await resetScrollPositionAndElements(test); + + // Scroll to make the trigger fire again. + // Both the inner and outer targets should be triggered as there is no + // scope and they are both attached to the trigger. + await scrollAndAssert(/*inner=*/"running", /*outer=*/"running"); + }, "Added scope prevents external references from finding trigger " + + "within scope."); + </script> + </body> +</html>