tor-browser

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

timeline-scope.html (9666B)


      1 <!DOCTYPE html>
      2 <title>Behavior of the timeline-scope property</title>
      3 <link rel="help" src="https://github.com/w3c/csswg-drafts/issues/7759">
      4 <script src="/resources/testharness.js"></script>
      5 <script src="/resources/testharnessreport.js"></script>
      6 <script src="/web-animations/testcommon.js"></script>
      7 
      8 <main id=main></main>
      9 <script>
     10  async function inflate(t, template) {
     11    t.add_cleanup(() => main.replaceChildren());
     12    return runAndWaitForFrameUpdate(() => {
     13       main.append(template.content.cloneNode(true));
     14    });
     15  }
     16 
     17  async function scrollTop(e, value) {
     18    e.scrollTop = value;
     19    await waitForNextFrame();
     20  }
     21 </script>
     22 <style>
     23  @keyframes anim {
     24    from { width: 0px; }
     25    to { width: 200px; }
     26  }
     27 
     28  .scroller {
     29    overflow-y: hidden;
     30    width: 200px;
     31    height: 200px;
     32  }
     33  .scroller > .content {
     34    margin: 400px 0px;
     35    width: 100px;
     36    height: 100px;
     37    background-color: green;
     38  }
     39  .target {
     40    background-color: coral;
     41    width: 0px;
     42    animation: anim auto linear;
     43    animation-timeline: --t1;
     44  }
     45  .timeline {
     46    scroll-timeline-name: --t1;
     47  }
     48  .scope {
     49    timeline-scope: --t1;
     50  }
     51 
     52 </style>
     53 
     54 <!-- Basic Behavior -->
     55 
     56 <template id=deferred_timeline>
     57  <div class="scope">
     58    <div class=target>Test</div>
     59    <div class="scroller timeline">
     60      <div class=content></div>
     61    </div>
     62  </div>
     63 </template>
     64 <script>
     65  promise_test(async (t) => {
     66    await inflate(t, deferred_timeline);
     67    let scroller = main.querySelector('.scroller');
     68    let target = main.querySelector('.target');
     69 
     70    const anim = target.getAnimations()[0];
     71    await anim.ready;
     72 
     73    await scrollTop(scroller, 350); // 50%
     74    assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
     75  }, 'Descendant can attach to deferred timeline');
     76 </script>
     77 
     78 <template id=deferred_timeline_no_attachments>
     79  <div class="scope">
     80    <div class=target>Test</div>
     81    <div class="scroller">
     82      <div class=content></div>
     83    </div>
     84  </div>
     85 </template>
     86 <script>
     87  promise_test(async (t) => {
     88    await inflate(t, deferred_timeline_no_attachments);
     89    let scroller = main.querySelector('.scroller');
     90    let target = main.querySelector('.target');
     91    await scrollTop(scroller, 350); // 50%
     92    assert_equals(getComputedStyle(target).width, '0px');
     93  }, 'Deferred timeline with no attachments');
     94 </script>
     95 
     96 <template id=scroll_timeline_inner_interference>
     97  <div class="scroller timeline">
     98    <div class=content>
     99      <div class=target>Test</div>
    100      <div class="scroller timeline">
    101        <div class=content></div>
    102      </div>
    103    </div>
    104  </div>
    105 </template>
    106 <script>
    107  promise_test(async (t) => {
    108    await inflate(t, scroll_timeline_inner_interference);
    109    let scroller = main.querySelector('.scroller');
    110    let target = main.querySelector('.target');
    111    await scrollTop(scroller, 350); // 50%
    112    assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
    113  }, 'Inner timeline does not interfere with outer timeline');
    114 </script>
    115 
    116 <template id=deferred_timeline_two_attachments>
    117  <div class="scope">
    118    <div class=target>Test</div>
    119    <div class="scroller timeline">
    120      <div class=content></div>
    121    </div>
    122    <!-- Extra attachment -->
    123    <div class="timeline"></div>
    124  </div>
    125 </template>
    126 <script>
    127  promise_test(async (t) => {
    128    await inflate(t, deferred_timeline_two_attachments);
    129    let scroller = main.querySelector('.scroller');
    130    let target = main.querySelector('.target');
    131    await scrollTop(scroller, 350); // 50%
    132    assert_equals(getComputedStyle(target).width, '0px');
    133  }, 'Deferred timeline with two attachments');
    134 </script>
    135 
    136 <!-- Dynamic Reattachment -->
    137 
    138 <template id=deferred_timeline_reattach>
    139  <div class="scope">
    140    <div class=target>Test</div>
    141    <div class="scroller timeline">
    142      <div class=content></div>
    143    </div>
    144    <div class="scroller">
    145      <div class=content></div>
    146    </div>
    147  </div>
    148 </template>
    149 <script>
    150  promise_test(async (t) => {
    151    await inflate(t, deferred_timeline_reattach);
    152    let scrollers = main.querySelectorAll('.scroller');
    153    assert_equals(scrollers.length, 2);
    154    let target = main.querySelector('.target');
    155    await scrollTop(scrollers[0], 350); // 50%
    156    await scrollTop(scrollers[1], 175); // 25%
    157 
    158    // Attached to scrollers[0].
    159    assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
    160 
    161    // Reattach to scrollers[1].
    162    await runAndWaitForFrameUpdate(() => {
    163      scrollers[0].classList.remove('timeline');
    164      scrollers[1].classList.add('timeline');
    165    });
    166 
    167    assert_equals(getComputedStyle(target).width, '50px'); // 0px => 200px, 25%
    168  }, 'Dynamically re-attaching');
    169 </script>
    170 
    171 <template id=deferred_timeline_dynamic_detach>
    172  <div class="scope">
    173    <div class=target>Test</div>
    174    <div class="scroller timeline">
    175      <div class=content></div>
    176    </div>
    177    <div class="scroller timeline">
    178      <div class=content></div>
    179    </div>
    180  </div>
    181 </template>
    182 <script>
    183  promise_test(async (t) => {
    184    await inflate(t, deferred_timeline_dynamic_detach);
    185    let scrollers = main.querySelectorAll('.scroller');
    186    assert_equals(scrollers.length, 2);
    187    let target = main.querySelector('.target');
    188    await scrollTop(scrollers[0], 350); // 50%
    189    await scrollTop(scrollers[1], 175); // 25%
    190 
    191    // Attached to two timelines initially:
    192    assert_equals(getComputedStyle(target).width, '0px');
    193 
    194    // Detach scrollers[1].
    195    await runAndWaitForFrameUpdate(() => {
    196      scrollers[1].classList.remove('timeline');
    197    });
    198 
    199    assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
    200 
    201    // Also detach scrollers[0].
    202    scrollers[0].classList.remove('timeline');
    203 
    204    await waitForNextFrame();
    205    assert_equals(getComputedStyle(target).width, '0px');
    206  }, 'Dynamically detaching');
    207 </script>
    208 
    209 <template id=deferred_timeline_attached_removed>
    210  <div class="scope">
    211    <div class=target>Test</div>
    212    <div class="scroller timeline">
    213      <div class=content></div>
    214    </div>
    215  </div>
    216 </template>
    217 <script>
    218  promise_test(async (t) => {
    219    await inflate(t, deferred_timeline_attached_removed);
    220    let scroller = main.querySelector('.scroller');
    221    let target = main.querySelector('.target');
    222    await scrollTop(scroller, 350); // 50%
    223    assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
    224 
    225    let scroller_parent = scroller.parentElement;
    226    scroller.remove();
    227    await waitForNextFrame();
    228    assert_equals(getComputedStyle(target).width, '0px');
    229 
    230    scroller_parent.append(scroller);
    231    await scrollTop(scroller, 350); // 50%
    232    assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
    233  }, 'Removing/inserting element with attaching timeline');
    234 </script>
    235 
    236 <template id=deferred_timeline_attached_display_none>
    237  <div class="scope">
    238    <div class=target>Test</div>
    239    <div class="scroller timeline">
    240      <div class=content></div>
    241    </div>
    242  </div>
    243 </template>
    244 <script>
    245  promise_test(async (t) => {
    246    await inflate(t, deferred_timeline_attached_display_none);
    247    let scroller = main.querySelector('.scroller');
    248    let target = main.querySelector('.target');
    249    await scrollTop(scroller, 350); // 50%
    250    assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
    251 
    252    scroller.style.display = 'none';
    253    await waitForNextFrame();
    254    assert_equals(getComputedStyle(target).width, '0px');
    255 
    256    scroller.style.display = 'block';
    257    await scrollTop(scroller, 350); // 50%
    258    assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
    259  }, 'Ancestor attached element becoming display:none/block');
    260 </script>
    261 
    262 <template id=deferred_timeline_appearing>
    263  <div class=container>
    264    <div class=target>Test</div>
    265    <div class="scroller timeline">
    266      <div class=content></div>
    267    </div>
    268  </div>
    269 </template>
    270 <script>
    271  promise_test(async (t) => {
    272    await inflate(t, deferred_timeline_appearing);
    273    let container = main.querySelector('.container');
    274    let scroller = main.querySelector('.scroller');
    275    let target = main.querySelector('.target');
    276 
    277    await scrollTop(scroller, 350); // 50%
    278 
    279    // Not attached to any timeline initially.
    280    assert_equals(getComputedStyle(target).width, '0px');
    281 
    282    // Add the deferred timeline.
    283    container.classList.add('scope');
    284    await waitForNextFrame();
    285    assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
    286 
    287    // Remove the deferred timeline.
    288    container.classList.remove('scope');
    289    await waitForNextFrame();
    290    assert_equals(getComputedStyle(target).width, '0px');
    291  }, 'A deferred timeline appearing dynamically in the ancestor chain');
    292 </script>
    293 
    294 <template id=deferred_timeline_on_self>
    295  <div class="scroller timeline scope">
    296    <div class=content>
    297      <div class=target></div>
    298    </div>
    299    <div class=scroller2></div>
    300  </div>
    301 </template>
    302 <script>
    303  promise_test(async (t) => {
    304    await inflate(t, deferred_timeline_on_self);
    305    let scroller = main.querySelector('.scroller');
    306    let target = main.querySelector('.target');
    307    await scrollTop(scroller, 525); // 75%
    308 
    309    assert_equals(getComputedStyle(target).width, '150px'); // 0px => 200px, 75%
    310 
    311    // A second scroll-timeline now attaches to the same root.
    312    let scroller2 = main.querySelector('.scroller2');
    313    scroller2.classList.add('timeline');
    314    await waitForNextFrame();
    315 
    316    // The deferred timeline produced by timeline-scope is now inactive,
    317    // but it doesn't matter, because we preferred to attach
    318    // to the non-deferred timeline.
    319    assert_equals(getComputedStyle(target).width, '150px'); // 0px => 200px, 75%
    320  }, 'Animations prefer non-deferred timelines');
    321 
    322 </script>