tor-browser

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

browser_offscreen_image_in_out_of_process_iframe.js (5765B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 "use strict";
      6 
      7 const DIRPATH = getRootDirectory(gTestPath).replace(
      8  "chrome://mochitests/content/",
      9  ""
     10 );
     11 const parentPATH = DIRPATH + "empty.html";
     12 const iframePATH = DIRPATH + "empty.html";
     13 
     14 const parentURL = `http://example.com/${parentPATH}`;
     15 const iframeURL = `http://example.org/${iframePATH}`;
     16 
     17 add_task(async function setup_pref() {
     18  await SpecialPowers.pushPrefEnv({
     19    set: [
     20      // To avoid throttling requestAnimationFrame callbacks in invisible
     21      // iframes
     22      ["layout.throttled_frame_rate", 60],
     23    ],
     24  });
     25 });
     26 
     27 add_task(async function () {
     28  const win = await BrowserTestUtils.openNewBrowserWindow({
     29    fission: true,
     30  });
     31 
     32  try {
     33    const browser = win.gBrowser.selectedTab.linkedBrowser;
     34 
     35    BrowserTestUtils.startLoadingURIString(browser, parentURL);
     36    await BrowserTestUtils.browserLoaded(browser, false, parentURL);
     37 
     38    async function setup(url) {
     39      const scroller = content.document.createElement("div");
     40      scroller.style = "width: 300px; height: 300px; overflow: scroll;";
     41      scroller.setAttribute("id", "scroller");
     42      content.document.body.appendChild(scroller);
     43 
     44      // Make a space bigger than display port.
     45      const spacer = content.document.createElement("div");
     46      spacer.style = "width: 100%; height: 10000px;";
     47      scroller.appendChild(spacer);
     48 
     49      const iframe = content.document.createElement("iframe");
     50      scroller.appendChild(iframe);
     51 
     52      iframe.contentWindow.location = url;
     53      await new Promise(resolve => {
     54        iframe.addEventListener("load", resolve, { once: true });
     55      });
     56 
     57      return iframe.browsingContext;
     58    }
     59 
     60    async function setupImage() {
     61      const img = content.document.createElement("img");
     62      // This GIF is a 100ms interval animation.
     63      img.setAttribute("src", "animated.gif");
     64      img.setAttribute("id", "img");
     65      content.document.body.appendChild(img);
     66 
     67      const spacer = content.document.createElement("div");
     68      spacer.style = "width: 100%; height: 10000px;";
     69      content.document.body.appendChild(spacer);
     70      await new Promise(resolve => {
     71        img.addEventListener("load", resolve, { once: true });
     72      });
     73    }
     74 
     75    // Returns the count of frameUpdate during |time| (in ms) period.
     76    async function observeFrameUpdate(time) {
     77      function ImageDecoderObserverStub() {
     78        this.sizeAvailable = function sizeAvailable() {};
     79        this.frameComplete = function frameComplete() {};
     80        this.decodeComplete = function decodeComplete() {};
     81        this.loadComplete = function loadComplete() {};
     82        this.frameUpdate = function frameUpdate() {};
     83        this.discard = function discard() {};
     84        this.isAnimated = function isAnimated() {};
     85        this.hasTransparency = function hasTransparency() {};
     86      }
     87 
     88      // Start from the callback of setTimeout.
     89      await new Promise(resolve => content.window.setTimeout(resolve, 0));
     90 
     91      let frameCount = 0;
     92      const observer = new ImageDecoderObserverStub();
     93      observer.frameUpdate = () => {
     94        frameCount++;
     95      };
     96      observer.loadComplete = () => {
     97        // Ignore the frameUpdate callback along with loadComplete. It seems
     98        // a frameUpdate sometimes happens with a loadComplete when attatching
     99        // observer in fission world.
    100        frameCount--;
    101      };
    102 
    103      const gObserver = SpecialPowers.Cc["@mozilla.org/image/tools;1"]
    104        .getService(SpecialPowers.Ci.imgITools)
    105        .createScriptedObserver(observer);
    106      const img = content.document.getElementById("img");
    107 
    108      SpecialPowers.wrap(img).addObserver(gObserver);
    109      await new Promise(resolve => content.window.setTimeout(resolve, time));
    110      SpecialPowers.wrap(img).removeObserver(gObserver);
    111 
    112      return frameCount;
    113    }
    114 
    115    // Setup an iframe which is initially scrolled out.
    116    const iframe = await SpecialPowers.spawn(browser, [iframeURL], setup);
    117 
    118    // Setup a 100ms interval animated GIF image in the iframe.
    119    await SpecialPowers.spawn(iframe, [], setupImage);
    120 
    121    let frameCount = await SpecialPowers.spawn(
    122      iframe,
    123      [1000],
    124      observeFrameUpdate
    125    );
    126    // Bug 1577084.
    127    if (frameCount == 0) {
    128      is(frameCount, 0, "no frameupdates");
    129    } else {
    130      todo_is(frameCount, 0, "no frameupdates");
    131    }
    132 
    133    // Scroll the iframe into view.
    134    await SpecialPowers.spawn(browser, [], async () => {
    135      const scroller = content.document.getElementById("scroller");
    136      scroller.scrollTo({ left: 0, top: 9800, behavior: "smooth" });
    137      await new Promise(resolve => content.window.setTimeout(resolve, 1000));
    138    });
    139 
    140    await new Promise(resolve => requestAnimationFrame(resolve));
    141 
    142    frameCount = await SpecialPowers.spawn(iframe, [1000], observeFrameUpdate);
    143    Assert.greater(frameCount, 0, "There should be frameUpdate(s)");
    144 
    145    await new Promise(resolve => requestAnimationFrame(resolve));
    146 
    147    await SpecialPowers.spawn(iframe, [], async () => {
    148      const img = content.document.getElementById("img");
    149      // Move the image outside of the scroll port.  'position: absolute' causes
    150      // a relow on the image element.
    151      img.style = "position: absolute; top: 9000px;";
    152      await new Promise(resolve =>
    153        content.window.requestAnimationFrame(resolve)
    154      );
    155    });
    156 
    157    await new Promise(resolve => requestAnimationFrame(resolve));
    158 
    159    frameCount = await SpecialPowers.spawn(iframe, [1000], observeFrameUpdate);
    160    is(frameCount, 0, "No frameUpdate should happen");
    161  } finally {
    162    await BrowserTestUtils.closeWindow(win);
    163  }
    164 });