Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

<!DOCTYPE html>
<html>
<head>
<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>