/*
 * This test duplicates image/test/mochitest/test_animated_css_image.html, so keep them in sync.
 * This is because we need a browser-chrome test in order to test invalidation (the getSnapshot method here
 * uses the same path as painting to the screen, whereas test_animated_css_image.html is doing a
 * separate paint to a surface), but browser-chrome isn't run on android, so test_animated_css_image.html
 * gets us android coverage.
 */

/* This test is based on
     https://searchfox.org/mozilla-central/rev/25d26b0a62cc5bb4aa3bb90a11f3b0b7c52859c4/gfx/layers/apz/test/mochitest/browser_test_position_sticky.js
*/

"use strict";

requestLongerTimeout(3);

Services.scriptloader.loadSubScript(
  "chrome://mochitests/content/browser/gfx/layers/apz/test/mochitest/apz_test_utils.js",
  this
);

Services.scriptloader.loadSubScript(
  "chrome://mochitests/content/browser/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js",
  this
);

// this contains the kTests array
Services.scriptloader.loadSubScript(
  "chrome://mochitests/content/browser/image/test/browser/animated_image_test_list.js",
  this
);

async function assertAnimates(thehtml) {
  function httpURL(sfilename) {
    let chromeURL = getRootDirectory(gTestPath) + sfilename;
    return chromeURL.replace(
      "chrome://mochitests/content/",
      "http://mochi.test:8888/"
    );
  }

  const url = httpURL("helper_animated_css_image.html");
  const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);

  const { rect } = await SpecialPowers.spawn(
    tab.linkedBrowser,
    [],
    async () => {
      let rect = content.document.documentElement.getBoundingClientRect();
      rect.x += content.window.mozInnerScreenX;
      rect.y += content.window.mozInnerScreenY;

      return {
        rect,
      };
    }
  );

  let blankSnapshot = await getSnapshot({
    x: rect.x,
    y: rect.y,
    width: rect.width,
    height: rect.height,
  });

  const kNumRetries = 600;

  info("testing: " + thehtml);

  await SpecialPowers.spawn(tab.linkedBrowser, [thehtml], async thehtml => {
    const theiframe = content.document.getElementById("iframe");
    let load = new Promise(resolve => {
      theiframe.addEventListener("load", resolve, { once: true });
    });
    theiframe.srcdoc = thehtml;
    await load;

    // give time for content/test load handlers to run before we do anything
    await new Promise(resolve => {
      content.window.requestAnimationFrame(() => {
        content.window.requestAnimationFrame(resolve);
      });
    });

    // make sure we are flushed and rendered.
    content.document.documentElement.getBoundingClientRect();

    await new Promise(resolve => {
      content.window.requestAnimationFrame(() => {
        content.window.requestAnimationFrame(resolve);
      });
    });
  });

  let initial = await getSnapshot({
    x: rect.x,
    y: rect.y,
    width: rect.width,
    height: rect.height,
  });

  {
    // One test (bug 1730834) loads an image as the background of a div in the
    // load handler, so there's no good way to wait for it to be loaded and
    // rendered except to poll.
    let equal = initial == blankSnapshot;
    for (let i = 0; i < kNumRetries; ++i) {
      if (!equal) {
        break;
      }

      await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
        await new Promise(resolve => {
          content.window.requestAnimationFrame(() => {
            content.window.requestAnimationFrame(resolve);
          });
        });
      });

      initial = await getSnapshot({
        x: rect.x,
        y: rect.y,
        width: rect.width,
        height: rect.height,
      });
      equal = initial == blankSnapshot;
    }
    ok(!equal, "Initial snapshot shouldn't be blank");
  }

  async function checkFrames() {
    let foundDifferent = false;
    let foundInitialAgain = false;
    for (let i = 0; i < kNumRetries; ++i) {
      let current = await getSnapshot({
        x: rect.x,
        y: rect.y,
        width: rect.width,
        height: rect.height,
      });

      let equal = initial == current;
      if (!foundDifferent && !equal) {
        ok(true, `Found different image after ${i} retries`);
        foundDifferent = true;
      }

      // Ensure that we go back to the initial state (animated1.gif) is an
      // infinite gif.
      if (foundDifferent && equal) {
        ok(true, `Found same image again after ${i} retries`);

        foundInitialAgain = true;
        break;
      }

      await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
        await new Promise(resolve => {
          content.window.requestAnimationFrame(() => {
            content.window.requestAnimationFrame(resolve);
          });
        });
      });
    }

    ok(
      foundDifferent && foundInitialAgain,
      `Should've found a different snapshot and then an equal one, after ${kNumRetries} retries`
    );
  }

  for (let j = 0; j < 5; j++) {
    await checkFrames();
  }

  BrowserTestUtils.removeTab(tab);
}

add_task(async () => {
  // kTests is defined in the imported animated_image_test_list.js so it can
  // be shared between tests.
  // eslint-disable-next-line no-undef
  for (let { html } of kTests) {
    await assertAnimates(html);
  }
});
