Automatic update from web-platform-tests [SDA] Fix naming conflict resolution Resolving name conflicts by selecting last in flat tree order was already implemented behind a flag. The same flag was used for making names global by default. This patch decouples conflict resolution from global scope. Though resolutions on the same issue (#12581) handling the full set of resolutions requires updating more tests, which in turns entails more risk, and potentially slowing the upstream merge. Remaining resolutions on this issue will be tackled in follow up CLs. https://chromestatus.com/feature/5139380420345856 Bug: 519612140 Change-Id: Id0d53dbb27b8bedeb246aa4091f6ab04840cf2f4 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7899178 Commit-Queue: Kevin Ellis <kevers@chromium.org> Reviewed-by: Robert Flack <flackr@chromium.org> Cr-Commit-Position: refs/heads/main@{#1642366} -- wpt-commits: d80bfc6ef0909bb6f71d41339532e37ffd30cad8 wpt-pr: 60427
324 lines
9.5 KiB
HTML
324 lines
9.5 KiB
HTML
<!DOCTYPE html>
|
|
<title>Behavior of the timeline-scope property</title>
|
|
<link rel="help" src="https://github.com/w3c/csswg-drafts/issues/7759">
|
|
<script src="/resources/testharness.js"></script>
|
|
<script src="/resources/testharnessreport.js"></script>
|
|
<script src="/web-animations/testcommon.js"></script>
|
|
|
|
<main id=main></main>
|
|
<script>
|
|
async function inflate(t, template) {
|
|
t.add_cleanup(() => main.replaceChildren());
|
|
return runAndWaitForFrameUpdate(() => {
|
|
main.append(template.content.cloneNode(true));
|
|
});
|
|
}
|
|
|
|
async function scrollTop(e, value) {
|
|
e.scrollTop = value;
|
|
await waitForNextFrame();
|
|
}
|
|
</script>
|
|
<style>
|
|
@keyframes anim {
|
|
from { width: 0px; }
|
|
to { width: 200px; }
|
|
}
|
|
|
|
.scroller {
|
|
overflow-y: hidden;
|
|
width: 200px;
|
|
height: 200px;
|
|
}
|
|
.scroller > .content {
|
|
margin: 400px 0px;
|
|
width: 100px;
|
|
height: 100px;
|
|
background-color: green;
|
|
}
|
|
.target {
|
|
background-color: coral;
|
|
width: 0px;
|
|
animation: anim auto linear;
|
|
animation-timeline: --t1;
|
|
}
|
|
.timeline {
|
|
scroll-timeline-name: --t1;
|
|
}
|
|
.scope {
|
|
timeline-scope: --t1;
|
|
}
|
|
|
|
</style>
|
|
|
|
<!-- Basic Behavior -->
|
|
|
|
<template id=deferred_timeline>
|
|
<div class="scope">
|
|
<div class=target>Test</div>
|
|
<div class="scroller timeline">
|
|
<div class=content></div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
promise_test(async (t) => {
|
|
await inflate(t, deferred_timeline);
|
|
let scroller = main.querySelector('.scroller');
|
|
let target = main.querySelector('.target');
|
|
|
|
const anim = target.getAnimations()[0];
|
|
await anim.ready;
|
|
|
|
await scrollTop(scroller, 350); // 50%
|
|
assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
|
|
}, 'Descendant can attach to deferred timeline');
|
|
</script>
|
|
|
|
<template id=deferred_timeline_no_attachments>
|
|
<div class="scope">
|
|
<div class=target>Test</div>
|
|
<div class="scroller">
|
|
<div class=content></div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
promise_test(async (t) => {
|
|
await inflate(t, deferred_timeline_no_attachments);
|
|
let scroller = main.querySelector('.scroller');
|
|
let target = main.querySelector('.target');
|
|
await scrollTop(scroller, 350); // 50%
|
|
assert_equals(getComputedStyle(target).width, '0px');
|
|
}, 'Deferred timeline with no attachments');
|
|
</script>
|
|
|
|
<template id=scroll_timeline_inner_interference>
|
|
<div class="scroller timeline">
|
|
<div class=content>
|
|
<div class=target>Test</div>
|
|
<div class="scroller timeline">
|
|
<div class=content></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
promise_test(async (t) => {
|
|
await inflate(t, scroll_timeline_inner_interference);
|
|
let scroller = main.querySelector('.scroller');
|
|
let target = main.querySelector('.target');
|
|
await scrollTop(scroller, 350); // 50%
|
|
assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
|
|
}, 'Inner timeline does not interfere with outer timeline');
|
|
</script>
|
|
|
|
<template id=deferred_timeline_two_attachments>
|
|
<div class="scope">
|
|
<div class=target>Test</div>
|
|
<div class="scroller timeline">
|
|
<div class=content></div>
|
|
</div>
|
|
<!-- Extra attachment -->
|
|
<div class="timeline"></div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
promise_test(async (t) => {
|
|
await inflate(t, deferred_timeline_two_attachments);
|
|
let scroller = main.querySelector('.scroller');
|
|
let target = main.querySelector('.target');
|
|
await scrollTop(scroller, 350); // 50%
|
|
assert_equals(getComputedStyle(target).width, '0px');
|
|
}, 'Deferred timeline with two attachments');
|
|
</script>
|
|
|
|
<!-- Dynamic Reattachment -->
|
|
|
|
<template id=deferred_timeline_reattach>
|
|
<div class="scope">
|
|
<div class=target>Test</div>
|
|
<div class="scroller timeline">
|
|
<div class=content></div>
|
|
</div>
|
|
<div class="scroller">
|
|
<div class=content></div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
promise_test(async (t) => {
|
|
await inflate(t, deferred_timeline_reattach);
|
|
let scrollers = main.querySelectorAll('.scroller');
|
|
assert_equals(scrollers.length, 2);
|
|
let target = main.querySelector('.target');
|
|
await scrollTop(scrollers[0], 350); // 50%
|
|
await scrollTop(scrollers[1], 175); // 25%
|
|
|
|
// Attached to scrollers[0].
|
|
assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
|
|
|
|
// Reattach to scrollers[1].
|
|
await runAndWaitForFrameUpdate(() => {
|
|
scrollers[0].classList.remove('timeline');
|
|
scrollers[1].classList.add('timeline');
|
|
});
|
|
|
|
assert_equals(getComputedStyle(target).width, '50px'); // 0px => 200px, 25%
|
|
}, 'Dynamically re-attaching');
|
|
</script>
|
|
|
|
<template id=deferred_timeline_dynamic_detach>
|
|
<div class="scope">
|
|
<div class=target>Test</div>
|
|
<div class="scroller timeline">
|
|
<div class=content></div>
|
|
</div>
|
|
<div class="scroller timeline">
|
|
<div class=content></div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
promise_test(async (t) => {
|
|
await inflate(t, deferred_timeline_dynamic_detach);
|
|
let scrollers = main.querySelectorAll('.scroller');
|
|
assert_equals(scrollers.length, 2);
|
|
let target = main.querySelector('.target');
|
|
await scrollTop(scrollers[0], 350); // 50%
|
|
await scrollTop(scrollers[1], 175); // 25%
|
|
|
|
// Attached to two timelines initially:
|
|
// Resolve conflict by selecting last match in flat tree order.
|
|
assert_equals(getComputedStyle(target).width, '50px');
|
|
|
|
// Detach scrollers[1].
|
|
await runAndWaitForFrameUpdate(() => {
|
|
scrollers[1].classList.remove('timeline');
|
|
});
|
|
|
|
assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
|
|
|
|
// Also detach scrollers[0].
|
|
scrollers[0].classList.remove('timeline');
|
|
|
|
await waitForNextFrame();
|
|
assert_equals(getComputedStyle(target).width, '0px');
|
|
}, 'Dynamically detaching');
|
|
</script>
|
|
|
|
<template id=deferred_timeline_attached_removed>
|
|
<div class="scope">
|
|
<div class=target>Test</div>
|
|
<div class="scroller timeline">
|
|
<div class=content></div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
promise_test(async (t) => {
|
|
await inflate(t, deferred_timeline_attached_removed);
|
|
let scroller = main.querySelector('.scroller');
|
|
let target = main.querySelector('.target');
|
|
await scrollTop(scroller, 350); // 50%
|
|
assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
|
|
|
|
let scroller_parent = scroller.parentElement;
|
|
scroller.remove();
|
|
await waitForNextFrame();
|
|
assert_equals(getComputedStyle(target).width, '0px');
|
|
|
|
scroller_parent.append(scroller);
|
|
await scrollTop(scroller, 350); // 50%
|
|
assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
|
|
}, 'Removing/inserting element with attaching timeline');
|
|
</script>
|
|
|
|
<template id=deferred_timeline_attached_display_none>
|
|
<div class="scope">
|
|
<div class=target>Test</div>
|
|
<div class="scroller timeline">
|
|
<div class=content></div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
promise_test(async (t) => {
|
|
await inflate(t, deferred_timeline_attached_display_none);
|
|
let scroller = main.querySelector('.scroller');
|
|
let target = main.querySelector('.target');
|
|
await scrollTop(scroller, 350); // 50%
|
|
assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
|
|
|
|
scroller.style.display = 'none';
|
|
await waitForNextFrame();
|
|
assert_equals(getComputedStyle(target).width, '0px');
|
|
|
|
scroller.style.display = 'block';
|
|
await scrollTop(scroller, 350); // 50%
|
|
assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
|
|
}, 'Ancestor attached element becoming display:none/block');
|
|
</script>
|
|
|
|
<template id=deferred_timeline_appearing>
|
|
<div class=container>
|
|
<div class=target>Test</div>
|
|
<div class="scroller timeline">
|
|
<div class=content></div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
promise_test(async (t) => {
|
|
await inflate(t, deferred_timeline_appearing);
|
|
let container = main.querySelector('.container');
|
|
let scroller = main.querySelector('.scroller');
|
|
let target = main.querySelector('.target');
|
|
|
|
await scrollTop(scroller, 350); // 50%
|
|
|
|
// Not attached to any timeline initially.
|
|
assert_equals(getComputedStyle(target).width, '0px');
|
|
|
|
// Add the deferred timeline.
|
|
container.classList.add('scope');
|
|
await waitForNextFrame();
|
|
assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
|
|
|
|
// Remove the deferred timeline.
|
|
container.classList.remove('scope');
|
|
await waitForNextFrame();
|
|
assert_equals(getComputedStyle(target).width, '0px');
|
|
}, 'A deferred timeline appearing dynamically in the ancestor chain');
|
|
</script>
|
|
|
|
<template id=deferred_timeline_on_self>
|
|
<div class="scroller timeline scope">
|
|
<div class=content>
|
|
<div class=target></div>
|
|
</div>
|
|
<div class=scroller2></div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
promise_test(async (t) => {
|
|
await inflate(t, deferred_timeline_on_self);
|
|
let scroller = main.querySelector('.scroller');
|
|
let target = main.querySelector('.target');
|
|
await scrollTop(scroller, 525); // 75%
|
|
|
|
assert_equals(getComputedStyle(target).width, '150px'); // 0px => 200px, 75%
|
|
|
|
// A second scroll-timeline now attaches to the same root.
|
|
let scroller2 = main.querySelector('.scroller2');
|
|
scroller2.classList.add('timeline');
|
|
await waitForNextFrame();
|
|
|
|
// The deferred timeline produced by timeline-scope is now inactive,
|
|
// but it doesn't matter, because we preferred to attach
|
|
// to the non-deferred timeline.
|
|
assert_equals(getComputedStyle(target).width, '150px'); // 0px => 200px, 75%
|
|
}, 'Animations prefer non-deferred timelines');
|
|
|
|
</script>
|