tor-browser

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

file_animations_omta_scroll.html (12951B)


      1 <!DOCTYPE HTML>
      2 <html>
      3 <head>
      4  <meta charset="utf-8">
      5  <meta name="viewport" content="width=device-width,initial-scale=1">
      6  <title>Test for css-animations running on the compositor thread with scroll-timeline</title>
      7  <script src="/tests/SimpleTest/SimpleTest.js"></script>
      8  <script src="/tests/SimpleTest/paint_listener.js"></script>
      9  <script src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
     10  <script type="application/javascript" src="animation_utils.js"></script>
     11  <style type="text/css">
     12    @keyframes transform_anim {
     13      from { transform: translate(50px); }
     14      to { transform: translate(150px); }
     15    }
     16 
     17    @keyframes always_fifty {
     18      from, to { transform: translate(50px); }
     19    }
     20 
     21    @keyframes geometry {
     22      from { width: 50px; }
     23      to { width: 100px; }
     24    }
     25 
     26    .target {
     27      /* The animation target needs geometry in order to qualify for OMTA */
     28      width: 100px;
     29      height: 100px;
     30      background-color: green;
     31    }
     32 
     33    .scroller {
     34      width: 100px;
     35      height: 100px;
     36      overflow: scroll;
     37      scroll-timeline-name: --scroll_timeline;
     38    }
     39 
     40    .content {
     41      block-size: 100%;
     42      padding-block-end: 100px;
     43    }
     44  </style>
     45 </head>
     46 <body>
     47  <div id="display"></div>
     48  <pre id="test"></pre>
     49 </body>
     50 <script type="application/javascript">
     51 "use strict";
     52 
     53 // Global state
     54 var gScroller = null;
     55 var gDiv = null;
     56 
     57 // Shortcut omta_is and friends by filling in the initial 'elem' argument
     58 // with gDiv.
     59 [ 'omta_is', 'omta_todo_is', 'omta_is_approx' ].forEach(function(fn) {
     60  var origFn = window[fn];
     61  window[fn] = function() {
     62    var args = Array.from(arguments);
     63    if (!(args[0] instanceof Element)) {
     64      args.unshift(gDiv);
     65    }
     66    return origFn.apply(window, args);
     67  };
     68 });
     69 
     70 // Shortcut new_div and done_div to update gDiv
     71 var originalNewDiv = window.new_div;
     72 window.new_div = function(style) {
     73  [ gDiv ] = originalNewDiv(style);
     74 };
     75 var originalDoneDiv = window.done_div;
     76 window.done_div = function() {
     77  originalDoneDiv();
     78  gDiv = null;
     79 };
     80 
     81 // Bind the ok and todo to the opener, and close this window when we finish.
     82 var ok = opener.ok.bind(opener);
     83 var todo = opener.todo.bind(opener);
     84 function finish() {
     85  var o = opener;
     86  self.close();
     87  o.SimpleTest.finish();
     88 }
     89 
     90 function new_scroller() {
     91  gScroller = document.createElement('div');
     92  gScroller.className = `scroller`;
     93 
     94  let content = document.createElement('div');
     95  content.className = 'content';
     96 
     97  gScroller.appendChild(content);
     98  document.getElementById("display").appendChild(gScroller);
     99  return gScroller;
    100 }
    101 
    102 function done_scroller() {
    103  gScroller.firstChild.remove();
    104  gScroller.remove();
    105  gScroller = null;
    106 }
    107 
    108 waitUntilApzStable().then(() => {
    109  runOMTATest(function() {
    110    var onAbort = function() {
    111      if (gDiv) {
    112        done_div();
    113      }
    114      if (gScroller) {
    115        done_scroller();
    116      }
    117    };
    118    runAllAsyncAnimTests(onAbort).then(finish);
    119  }, finish);
    120 });
    121 
    122 //----------------------------------------------------------------------
    123 //
    124 // Test cases
    125 //
    126 //----------------------------------------------------------------------
    127 
    128 // The non-omta property with scroll-timeline together with an omta property
    129 // with document-timeline.
    130 addAsyncAnimTest(async function() {
    131  new_scroller();
    132  new_div("animation: geometry 10s, always_fifty 1s infinite; " +
    133          "animation-timeline: --scroll_timeline, auto");
    134  await waitForPaintsFlushed();
    135 
    136  // Note: width is not a OMTA property, so it must be running on the main
    137  // thread.
    138  omta_is("transform", { tx: 50 }, RunningOn.Compositor,
    139          "transform animations should runs on compositor thread");
    140 
    141  done_div();
    142  done_scroller();
    143 });
    144 
    145 // transform property with scroll-driven animations.
    146 addAsyncAnimTest(async function() {
    147  let scroller = new_scroller();
    148  new_div("animation: transform_anim 1s linear; " +
    149          "animation-timeline: --scroll_timeline;");
    150  await waitForPaintsFlushed();
    151 
    152  scroller.scrollTop = 50;
    153  await waitForPaintsFlushed();
    154 
    155  omta_is_approx("transform", { tx: 100 }, 0.1, RunningOn.Compositor,
    156                 "scroll transform animations should runs on compositor " +
    157                 "thread");
    158 
    159  done_div();
    160  done_scroller();
    161 });
    162 
    163 
    164 // The scroll-driven animation with an underlying value and make it go from the
    165 // active phase to the before phase.
    166 addAsyncAnimTest(async function() {
    167  let scroller = new_scroller();
    168  new_div("animation: always_fifty 5s linear 5s; " +
    169          "animation-timeline: --scroll_timeline; " +
    170          "transform: translate(25px);");
    171  await waitForPaintsFlushed();
    172 
    173  // NOTE: getOMTAStyle() can't detect the animation is running on the
    174  // compositor during the delay phase.
    175  omta_is_approx("transform", { tx: 25 }, 0.1, RunningOn.Either,
    176                 "The scroll animation is in delay");
    177 
    178  scroller.scrollTop = 75;
    179  await waitForPaintsFlushed();
    180 
    181  omta_is_approx("transform", { tx: 50 }, 0.1, RunningOn.Compositor,
    182                 "scroll transform animations should runs on compositor " +
    183                 "thread");
    184 
    185  // Use setAsyncScrollOffset() to update apz (compositor thread only) to make
    186  // sure Bug 1776077 is reproducible.
    187  let utils = SpecialPowers.wrap(window).windowUtils;
    188  utils.setAsyncScrollOffset(scroller, 0, -50);
    189 
    190  // NOTE: setAsyncScrollOffset() doesn't update main thread, so we check the
    191  // OMTA style directly.
    192  let compositorStr =
    193    SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "transform");
    194  ok(compositorStr === "matrix(1, 0, 0, 1, 25, 0)",
    195     "scroll animations is in delay phase before advancing time to next tick");
    196 
    197  utils.advanceTimeAndRefresh(16);
    198  utils.restoreNormalRefresh();
    199  await waitForPaints();
    200 
    201  // We update the OMTA in the following tick, so the OMTA style gets cleared.
    202  compositorStr = SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "transform");
    203  ok(compositorStr === "",
    204     "scroll animation in delay phase clears its OMTA style");
    205 
    206  scroller.scrollTop = 25;
    207  await waitForPaintsFlushed();
    208 
    209  compositorStr = SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "transform");
    210  ok(compositorStr === "",
    211     "scroll animation in delay phase clears its OMTA style");
    212  omta_is_approx("transform", { tx: 25 }, 0.1, RunningOn.Either,
    213                 "The scroll animation is in delay");
    214 
    215  done_div();
    216  done_scroller();
    217 });
    218 
    219 // The scroll-driven animation without an underlying value and make it go from
    220 // the active phase to the before phase.
    221 addAsyncAnimTest(async function() {
    222  let scroller = new_scroller();
    223  new_div("animation: always_fifty 5s linear 5s; " +
    224          "animation-timeline: --scroll_timeline;");
    225  await waitForPaintsFlushed();
    226 
    227  // NOTE: getOMTAStyle() can't detect the animation is running on the
    228  // compositor during the delay phase.
    229  omta_is_approx("transform", { tx: 0 }, 0.1, RunningOn.Either,
    230                 "The scroll animation is in delay");
    231 
    232  scroller.scrollTop = 75;
    233  await waitForPaintsFlushed();
    234 
    235  omta_is_approx("transform", { tx: 50 }, 0.1, RunningOn.Compositor,
    236                 "scroll transform animations should runs on compositor " +
    237                 "thread");
    238 
    239  // Use setAsyncScrollOffset() to update apz (compositor thread only) to make
    240  // sure Bug 1776077 is reproducible.
    241  let utils = SpecialPowers.wrap(window).windowUtils;
    242  utils.setAsyncScrollOffset(scroller, 0, -50);
    243 
    244  // NOTE: setAsyncScrollOffset() doesn't update main thread, so we check the
    245  // OMTA style directly.
    246  let compositorStr =
    247    SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "transform");
    248  ok(compositorStr === "matrix(1, 0, 0, 1, 0, 0)",
    249     "scroll animations is in delay phase before advancing time to next tick");
    250 
    251  utils.advanceTimeAndRefresh(16);
    252  utils.restoreNormalRefresh();
    253  await waitForPaints();
    254 
    255  // We update the OMTA in the following tick, so the OMTA style gets cleared.
    256  compositorStr = SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "transform");
    257  ok(compositorStr === "",
    258     "scroll animation in delay phase clears its OMTA style");
    259 
    260  done_div();
    261  done_scroller();
    262 });
    263 
    264 // The scroll-driven animation is in delay, together with other runing
    265 // animations.
    266 addAsyncAnimTest(async function() {
    267  let scroller = new_scroller();
    268  new_div("animation: transform_anim 10s linear -5s paused, " +
    269          "           always_fifty 5s linear 5s; " +
    270          "animation-timeline: auto, --scroll_timeline;");
    271  await waitForPaintsFlushed();
    272 
    273  omta_is_approx("transform", { tx: 100 }, 0.1, RunningOn.Compositor,
    274                 "The scroll animation is in delay");
    275 
    276  scroller.scrollTop = 75;
    277  await waitForPaintsFlushed();
    278 
    279  omta_is_approx("transform", { tx: 50 }, 0.1, RunningOn.Compositor,
    280                 "scroll transform animations should runs on compositor " +
    281                 "thread");
    282 
    283  let utils = SpecialPowers.wrap(window).windowUtils;
    284  utils.setAsyncScrollOffset(scroller, 0, -50);
    285  utils.advanceTimeAndRefresh(16);
    286  utils.restoreNormalRefresh();
    287  await waitForPaints();
    288 
    289  // NOTE: setAsyncScrollOffset() doesn't update main thread, so we check the
    290  // OMTA style directly.
    291  let compositorStr =
    292    SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "transform");
    293  ok(compositorStr === "matrix(1, 0, 0, 1, 100, 0)",
    294     "scroll animations is in delay phase before calling main thread style " +
    295     "udpate");
    296 
    297  done_div();
    298  done_scroller();
    299 });
    300 
    301 addAsyncAnimTest(async function() {
    302  let iframe = document.createElement("iframe");
    303  iframe.setAttribute("style", "width: 200px; height: 200px");
    304  iframe.setAttribute("scrolling", "no");
    305  iframe.srcdoc =
    306    "<!DOCTYPE HTML>" +
    307    "<html style='min-height: 100%; padding-bottom: 100px;'>" +
    308    "<style>" +
    309    "@keyframes anim { from, to { transform: translate(50px) } }" +
    310    "</style>" +
    311    "<div id='target_in_iframe' " +
    312    "     style='width:50px; height:50px; background:green; " +
    313    "            animation: anim 10s linear; " +
    314    "            animation-timeline: scroll(root);'>" +
    315    "</div>" +
    316    "</html>";
    317 
    318  await new Promise(resolve => {
    319    iframe.onload = resolve;
    320    document.body.appendChild(iframe);
    321  });
    322 
    323  gDiv = iframe.contentDocument.getElementById("target_in_iframe");
    324 
    325  const root = iframe.contentDocument.scrollingElement;
    326  const maxScroll = root.scrollHeight - root.clientHeight;
    327  root.scrollTop = 0.5 * maxScroll;
    328  await waitForPaintsFlushed();
    329 
    330  omta_is_approx("transform", { tx: 50 }, 0, RunningOn.MainThread,
    331                 "scroll transform animations inside an iframe with " +
    332                 "scrolling:no should run on the main thread");
    333 
    334  gDiv = null;
    335  iframe.remove();
    336 });
    337 
    338 // FIXME: Bug 1818346. Support OMTA for view-timeline.
    339 addAsyncAnimTest(async function() {
    340  let scroller = document.createElement("div");
    341  scroller.style.width = "100px";
    342  scroller.style.height = "100px";
    343  // Use hidden so we don't have scrollbar, to make sure the scrollport size
    344  // is 100px x 100px, so view progress visibility range is 100px on block axis.
    345  scroller.style.overflow = "hidden";
    346 
    347  let content1 = document.createElement("div");
    348  content1.style.height = "150px";
    349  scroller.appendChild(content1);
    350 
    351  let subject = document.createElement("div");
    352  subject.style.width = "50px";
    353  subject.style.height = "50px";
    354  subject.style.viewTimelineName = "--view_timeline";
    355  scroller.appendChild(subject);
    356 
    357  // Let |target| be the child of |subject|, so view-timeline-name property of
    358  // |subject| is referenceable.
    359  let target = document.createElement("div");
    360  target.style.width = "10px";
    361  target.style.height = "10px";
    362  subject.appendChild(target);
    363  gDiv = target;
    364 
    365  let content2 = document.createElement("div");
    366  content2.style.height = "150px";
    367  scroller.appendChild(content2);
    368 
    369  // So the DOM tree looks like this:
    370  // <div class=scroller>  <!-- "scroller", height: 100px; -->
    371  //   <div></div>         <!-- "",         height: 150px -->
    372  //   <div></div>         <!-- "subject",  height: 50px; -->
    373  //   <div></div>         <!-- "",         height: 150px; -->
    374  // </div>
    375  // The subject is in view when scroller.scrollTop is [50px, 200px].
    376  document.getElementById("display").appendChild(scroller);
    377  await waitForPaintsFlushed();
    378 
    379  scroller.scrollTop = 0;
    380  target.style.animation = "transform_anim 10s linear";
    381  target.style.animationTimeline = "--view_timeline";
    382  await waitForPaintsFlushed();
    383 
    384  omta_is_approx("transform", { tx: 0 }, 0.1, RunningOn.OnMainThread,
    385                 "The scroll animation is out of view");
    386 
    387  scroller.scrollTop = 50;
    388  await waitForPaintsFlushed();
    389 
    390  omta_is_approx("transform", { tx: 50 }, 0.1, RunningOn.OnMainThread,
    391                 "The scroll animation is 0%");
    392 
    393  scroller.scrollTop = 125;
    394  await waitForPaintsFlushed();
    395 
    396  omta_is_approx("transform", { tx: 100 }, 0.1, RunningOn.OnMainThread,
    397                 "The scroll animation is 50%");
    398 
    399  target.remove();
    400  content2.remove();
    401  subject.remove();
    402  content1.remove();
    403  scroller.remove();
    404  gDiv = null;
    405 });
    406 
    407 </script>
    408 </html>