tor-browser

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

helper_fission_animation_styling_in_oopif.html (7128B)


      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 scrolled out of view animation optimization in an OOPIF</title>
      7  <script src="/tests/SimpleTest/SimpleTest.js"></script>
      8  <script src="/tests/SimpleTest/paint_listener.js"></script>
      9  <script src="helper_fission_utils.js"></script>
     10  <script src="apz_test_native_event_utils.js"></script>
     11  <script src="apz_test_utils.js"></script>
     12 </head>
     13 <div style="width: 300px; height: 300px; overflow: hidden;" id="scroller">
     14  <div style="width: 100%; height: 1000px;"></div>
     15  <!-- I am not sure it's worth setting scrolling="no" and pointer-events: none. -->
     16  <!-- I just want to make sure that HitTestingTreeNode is generated even with these properties. -->
     17  <iframe scrolling="no" style="pointer-events: none;" id="testframe"></iframe>
     18 </div>
     19 <script>
     20 async function setup_in_oopif() {
     21  let iframeURL = SimpleTest.getTestFileURL("helper_fission_plain.html");
     22  iframeURL = iframeURL.replace(window.location.origin, "https://example.com");
     23 
     24  // We can't use `setupCrossOriginIFrame` directly here since the iframe here
     25  // is clipped out by an overflow: hidden ancestor, thus any APZ stuff
     26  // corresponding to the iframe document hasn't been established.
     27  const iframe = document.querySelector("iframe");
     28  const iframeLoadPromise = promiseOneEvent(iframe, "load", null);
     29  iframe.src = iframeURL;
     30  await iframeLoadPromise;
     31 
     32  await SpecialPowers.spawn(iframe, [], async () => {
     33    // Load utility functions for animation stuff.
     34    const script = content.document.createElement("script");
     35    script.setAttribute("src", "/tests/dom/animation/test/testcommon.js");
     36    content.document.head.appendChild(script);
     37 
     38    const extraStyle = content.document.createElement("style");
     39    content.document.head.appendChild(extraStyle);
     40    // an animation doesn't cause any geometric changes and doesn't run on the
     41    // compositor either
     42    extraStyle.sheet.insertRule("@keyframes anim { from { color: red; } to { color: blue; } }", 0);
     43 
     44    const div = content.document.createElement("div");
     45    // Position an element for animation at top: 20px.
     46    div.style = "position: absolute; top: 40px; animation: anim 1s infinite; box-shadow: 0px -20px red;";
     47    div.setAttribute("id", "target");
     48    div.innerHTML = "hello";
     49    content.document.body.appendChild(div);
     50    await new Promise(resolve => {
     51      script.onload = async () => {
     52        // Force to flush the first style to avoid the first style is observed.
     53        content.document.querySelector("#target").getAnimations()[0];
     54        // FIXME: Bug 1578309 use anim.ready instead.
     55        await content.wrappedJSObject.promiseFrame();
     56        resolve();
     57      };
     58    });
     59  });
     60 }
     61 
     62 async function observe_styling_in_oopif(aFrameCount) {
     63  const iframe = document.querySelector("iframe");
     64  return await SpecialPowers.spawn(iframe, [aFrameCount], async (frameCount) => {
     65    // Start in a rAF callback.
     66    await content.wrappedJSObject.waitForAnimationFrames(1);
     67 
     68    return await content.wrappedJSObject.
     69        observeStylingInTargetWindow(content.window, frameCount);
     70  });
     71 }
     72 
     73 async function promiseScrollInfoArrivalInOOPIF() {
     74  const scrollPromise = new Promise(resolve => {
     75    scroller.addEventListener("scroll", resolve, { once: true });
     76  });
     77 
     78  const transformReceivedPromise = SpecialPowers.spawn(testframe, [], async () => {
     79    await SpecialPowers.contentTransformsReceived(content);
     80  });
     81 
     82  await Promise.all([scrollPromise, transformReceivedPromise]);
     83 }
     84 
     85 function isWindows11() {
     86  return getPlatform() == "windows" &&
     87    SpecialPowers.Services.sysinfo.getProperty("version", null) == "10.0" &&
     88    SpecialPowers.Services.sysinfo.getProperty("build", null) == "22621";
     89 }
     90 
     91 // The actual test
     92 
     93 function assertThrottledRestyles(restyleCount, msg) {
     94  // Allow 1 restyle count on Windows 11 since on our CIs there's something
     95  // causing force flushing.
     96  if (isWindows11()) {
     97    ok(restyleCount <= 1, msg);
     98  } else {
     99    is(restyleCount, 0, msg);
    100  }
    101 }
    102 
    103 async function test() {
    104  // Generate an infinite animation which is initially clipped out by
    105  // overflow: hidden style in the out-of-process iframe.
    106  await setup_in_oopif();
    107 
    108  let restyleCount = await observe_styling_in_oopif(5);
    109  assertThrottledRestyles(
    110    restyleCount,
    111    "Animation in an out-of-process iframe which is initially clipped out " +
    112    "due to 'overflow: hidden' should be throttled");
    113 
    114  // Scroll synchronously to a position where the iframe gets visible.
    115  scroller.scrollTo(0, 1000);
    116  await promiseScrollInfoArrivalInOOPIF();
    117 
    118  // Wait for a frame to make sure the notification of the last scroll position
    119  // from APZC reaches the iframe process
    120  await observe_styling_in_oopif(1);
    121 
    122  restyleCount = await observe_styling_in_oopif(5);
    123  is(restyleCount, 5,
    124     "Animation in an out-of-process iframe which is no longer clipped out " +
    125       "should NOT be throttled");
    126 
    127  // Scroll synchronously to a position where the iframe is invisible again.
    128  scroller.scrollTo(0, 0);
    129  await promiseScrollInfoArrivalInOOPIF();
    130 
    131  // Wait for a frame to make sure the notification of the last scroll position
    132  // from APZC reaches the iframe process
    133  await observe_styling_in_oopif(1);
    134 
    135  restyleCount = await observe_styling_in_oopif(5);
    136  assertThrottledRestyles(
    137    restyleCount,
    138     "Animation in an out-of-process iframe which is clipped out again " +
    139       "should be throttled again");
    140 
    141  // ===== Asyncronous scrolling tests =====
    142  scroller.style.overflow = "scroll";
    143  // Scroll asynchronously to a position where the animating element gets
    144  // visible.
    145  scroller.scrollTo({ left: 0, top: 750, behavior: "smooth"});
    146 
    147  // Wait for the asyncronous scroll finish. `60` frames is the same number in
    148  // helper_fission_scroll_oopif.html
    149  await observe_styling_in_oopif(60);
    150 
    151  restyleCount = await observe_styling_in_oopif(5);
    152  is(restyleCount, 5,
    153     "Animation in an out-of-process iframe which is now visible by " +
    154       "asynchronous scrolling should NOT be throttled");
    155 
    156  // Scroll asynchronously to a position where the iframe is still visible but
    157  // the animating element gets invisible.
    158  scroller.scrollTo({ left: 0, top: 720, behavior: "smooth"});
    159 
    160  // Wait for the asyncronous scroll finish.
    161  await observe_styling_in_oopif(60);
    162 
    163  restyleCount = await observe_styling_in_oopif(5);
    164  assertThrottledRestyles(
    165    restyleCount,
    166     "Animation in an out-of-process iframe which is scrolled out of view by " +
    167       "asynchronous scrolling should be throttled");
    168 
    169  // Scroll asynchronously to a position where the animating element gets
    170  // visible again.
    171  scroller.scrollTo({ left: 0, top: 750, behavior: "smooth"});
    172 
    173  // Wait for the asyncronous scroll finish.
    174  await observe_styling_in_oopif(60);
    175 
    176  restyleCount = await observe_styling_in_oopif(5);
    177  is(restyleCount, 5,
    178     "Animation in an out-of-process iframe appeared by the asynchronous " +
    179       "scrolling should be NOT throttled");
    180 }
    181 
    182 waitUntilApzStable()
    183  .then(test)
    184  .then(subtestDone, subtestFailed);
    185 </script>
    186 </html>