tor-browser

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

scroll-timeline-dynamic.tentative.html (8817B)


      1 <!DOCTYPE html>
      2 <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1">
      3 <link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timelines">
      4 <script src="/resources/testharness.js"></script>
      5 <script src="/resources/testharnessreport.js"></script>
      6 <script src="/web-animations/testcommon.js"></script>
      7 <script src="support/testcommon.js"></script>
      8 <style>
      9  main {
     10    timeline-scope: --timeline;
     11  }
     12 
     13  main > div {
     14    overflow: hidden;
     15    width: 100px;
     16    height: 100px;
     17  }
     18  main > div > div {
     19    height: 200px;
     20  }
     21  @keyframes expand {
     22    from { width: 100px; }
     23    to { width: 200px; }
     24  }
     25  #element {
     26    width: 0px;
     27    height: 20px;
     28    animation-name: expand;
     29    /* Some of the tests in this file assume animations attached to the
     30       DocumentTimeline are "stopped" without actually being paused.
     31       Using 600s + steps(10, end) achieves this for one minute.*/
     32    animation-duration: 600s;
     33    animation-timing-function: steps(10, end);
     34  }
     35 </style>
     36 <main id=main>
     37  <div id=scroller1 class=scroller>
     38    <div></div>
     39  </div>
     40  <div id=scroller2 class=scroller>
     41    <div></div>
     42  </div>
     43  <div id=container></div>
     44 </main>
     45 <script>
     46  // Force layout of scrollers.
     47  scroller1.offsetTop;
     48  scroller2.offsetTop;
     49 
     50  // Note the steps(10, end) timing function and height:100px. (10px scroll
     51  // resolution).
     52  scroller1.scrollTop = 20;
     53  scroller2.scrollTop = 40;
     54 
     55  function insertElement() {
     56    let element = document.createElement('div');
     57    element.id = 'element';
     58    container.append(element);
     59    return element;
     60  }
     61 
     62  // Runs a test with dynamically added/removed elements or CSS rules.
     63  // Each test is instantiated twice: once for the initial style resolve where
     64  // the DOM change was effectuated, and once after scrolling.
     65  function dynamic_rule_test(func, description) {
     66    // assert_width is an async function which verifies that the computed value
     67    // of 'width' is as expected.
     68    const instantiate = (assert_width, description) => {
     69      promise_test(async (t) => {
     70        try {
     71          await func(t, assert_width);
     72        } finally {
     73          while (container.firstChild)
     74            container.firstChild.remove();
     75          main.style = '';
     76          scroller1.style = '';
     77          scroller2.style = '';
     78        }
     79      }, description);
     80    };
     81 
     82    // Verify that the computed style is as expected after a full frame update
     83    // following the rule change took place.
     84    instantiate(async (element, expected) => {
     85      await waitForCSSScrollTimelineStyle();
     86      assert_equals(getComputedStyle(element).width, expected);
     87    }, description + ' [immediate]');
     88 
     89    // Verify the computed style after scrolling a bit.
     90    instantiate(async (element, expected) => {
     91      await waitForNextFrame();
     92      scroller1.scrollTop = scroller1.scrollTop + 10;
     93      scroller2.scrollTop = scroller2.scrollTop + 10;
     94      await waitForNextFrame();
     95      scroller1.scrollTop = scroller1.scrollTop - 10;
     96      scroller2.scrollTop = scroller2.scrollTop - 10;
     97      await waitForNextFrame();
     98      assert_equals(getComputedStyle(element).width, expected);
     99    }, description + ' [scroll]');
    100  }
    101 
    102  dynamic_rule_test(async (t, assert_width) => {
    103    let element = insertElement();
    104 
    105    // This element initially has a DocumentTimeline.
    106    await assert_width(element, '100px');
    107 
    108    // Switch to scroll timeline.
    109    scroller1.style.scrollTimelineName = '--timeline';
    110    element.style.animationTimeline = '--timeline';
    111    await assert_width(element, '120px');
    112 
    113    // Switching from ScrollTimeline -> DocumentTimeline should preserve
    114    // current time.
    115    scroller1.style = '';
    116    element.style = '';
    117    await assert_width(element, '120px');
    118  }, 'Switching between document and scroll timelines');
    119 
    120  dynamic_rule_test(async (t, assert_width) => {
    121    let element = insertElement();
    122 
    123    // Flush style and create the animation with play pending.
    124    getComputedStyle(element).animation;
    125 
    126    let anim = element.getAnimations()[0];
    127    assert_true(anim.pending, "The animation is in play pending");
    128 
    129    // Switch to scroll timeline for a pending animation.
    130    scroller1.style.scrollTimelineName = '--timeline';
    131    element.style.animationTimeline = '--timeline';
    132 
    133    await anim.ready;
    134    assert_false(anim.pending, "The animation is not pending");
    135 
    136    await assert_width(element, '120px');
    137  }, 'Switching pending animation from document to scroll timelines');
    138 
    139  dynamic_rule_test(async (t, assert_width) => {
    140    let element = insertElement();
    141 
    142    // Note: #scroller1 is at 20%, and #scroller2 is at 40%.
    143    scroller1.style.scrollTimelineName = '--timeline1';
    144    scroller2.style.scrollTimelineName = '--timeline2';
    145    main.style.timelineScope = "--timeline1, --timeline2";
    146 
    147    await assert_width(element, '100px');
    148 
    149    element.style.animationTimeline = '--timeline1';
    150    await assert_width(element, '120px');
    151 
    152    element.style.animationTimeline = '--timeline2';
    153    await assert_width(element, '140px');
    154 
    155    element.style.animationTimeline = '--timeline1';
    156    await assert_width(element, '120px');
    157 
    158    // Switching from ScrollTimeline -> DocumentTimeline should preserve
    159    // current time.
    160    element.style.animationTimeline = '';
    161    await assert_width(element, '120px');
    162 
    163  }, 'Changing computed value of animation-timeline changes effective timeline');
    164 
    165  dynamic_rule_test(async (t, assert_width) => {
    166    let element = insertElement();
    167 
    168    scroller1.style.scrollTimelineName = '--timeline';
    169 
    170    // DocumentTimeline applies by default.
    171    await assert_width(element, '100px');
    172 
    173    // Wait for the animation to be ready so that we a start time and no hold
    174    // time.
    175    await element.getAnimations()[0].ready;
    176 
    177    // DocumentTimeline -> none
    178    element.style.animationTimeline = '--none';
    179    await assert_width(element, '0px');
    180 
    181    // none -> DocumentTimeline
    182    element.style.animationTimeline = '';
    183    await assert_width(element, '100px');
    184 
    185    // DocumentTimeline -> ScrollTimeline
    186    element.style.animationTimeline = '--timeline';
    187    await assert_width(element, '120px');
    188 
    189    // ScrollTimeline -> none
    190    element.style.animationTimeline = '--none';
    191    await assert_width(element, '120px');
    192 
    193    // none -> ScrollTimeline
    194    element.style.animationTimeline = '--timeline';
    195    await assert_width(element, '120px');
    196  }, 'Changing to/from animation-timeline:none');
    197 
    198 
    199  dynamic_rule_test(async (t, assert_width) => {
    200    let element = insertElement();
    201 
    202    element.style.animationDirection = 'reverse';
    203    element.style.animationTimeline = '--timeline';
    204 
    205    // Inactive animation-timeline.  Animation is inactive.
    206    await assert_width(element, '0px');
    207 
    208    // Note: #scroller1 is at 20%.
    209    scroller1.style.scrollTimelineName = '--timeline';
    210    await assert_width(element, '180px');
    211 
    212    // Note: #scroller2 is at 40%.
    213    scroller1.style.scrollTimelineName = '';
    214    scroller2.style.scrollTimelineName = '--timeline';
    215    await assert_width(element, '160px');
    216 
    217    element.style.animationDirection = '';
    218    await assert_width(element, '140px');
    219  }, 'Reverse animation direction');
    220 
    221  dynamic_rule_test(async (t, assert_width) => {
    222    let element = insertElement();
    223    element.style.animationTimeline = '--timeline';
    224 
    225    // Inactive animation-timeline. Animation effect is inactive.
    226    await assert_width(element, '0px');
    227 
    228    // Note: #scroller1 is at 20%.
    229    scroller1.style.scrollTimelineName = '--timeline';
    230    await assert_width(element, '120px');
    231 
    232    element.style.animationPlayState = 'paused';
    233 
    234    // We should still be at the same position after pausing.
    235    await assert_width(element, '120px');
    236 
    237    // Note: #scroller2 is at 40%.
    238    scroller1.style.scrollTimelineName = '';
    239    scroller2.style.scrollTimelineName = '--timeline';
    240 
    241    // Should be at the same position until we unpause.
    242    await assert_width(element, '120px');
    243 
    244    // Unpausing should synchronize to the scroll position.
    245    element.style.animationPlayState = '';
    246    await assert_width(element, '140px');
    247  }, 'Change to timeline attachment while paused');
    248 
    249  dynamic_rule_test(async (t, assert_width) => {
    250    let element = insertElement();
    251 
    252    // Note: #scroller1 is at 20%.
    253    scroller1.style.scrollTimelineName = '--timeline';
    254 
    255    await assert_width(element, '100px');
    256 
    257    element.style.animationTimeline = '--timeline';
    258    element.style.animationPlayState = 'paused';
    259 
    260    // Pausing should happen after the timeline was modified.
    261    // https://github.com/w3c/csswg-drafts/issues/5653
    262    await assert_width(element, '120px');
    263 
    264    // Changing the play state should not change the animation current time.
    265    element.style.animationPlayState = 'running';
    266    await assert_width(element, '120px');
    267  }, 'Switching timelines and pausing at the same time');
    268 </script>