<!DOCTYPE HTML>
<html>
<head>
  <title>Test that img.decode works on finished, discarded animated images</title>
  <script src="/tests/SimpleTest/SimpleTest.js"></script>
  <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
  <script type="text/javascript" src="imgutils.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1629490">Mozilla Bug 1629490</a>
<div id="container">
  <img id="finitepng" src="finite-apng.png">
</div>
<script class="testbody" type="text/javascript">

SimpleTest.waitForExplicitFinish();

window.onload = runTest;

let discardCallback = undefined;
let frameUpdateCallback = undefined;

async function runTest() {
  const kUsingWebRender = SpecialPowers.DOMWindowUtils.layerManagerType.startsWith("WebRender");

  let img = document.getElementById("finitepng");

  await img.decode();

  while (!isItGreen(img)) {
    // We hit an optimized path in WebRender that doesn't cause a repaint on the
    // main thread and doesn't seem to send MozAfterPaints.
    //
    // https://searchfox.org/mozilla-central/rev/b7f3977978922d44c7d92ae01c0d4cc2baca7bc2/layout/style/ImageLoader.cpp#553
    await new Promise(resolve => {
      if (kUsingWebRender) {
        requestAnimationFrame(() => {
          requestAnimationFrame(resolve);
        });
      } else {
        window.addEventListener("MozAfterPaint", resolve, { once: true });
      }
    });
  }

  addCallbacks(img);

  let iterationsLeft = 26;
  while (iterationsLeft > 0) {

    let discardPromise = new Promise(resolve => {
        discardCallback = resolve;
    });

    document.getElementById("container").style.display = "none";
    document.documentElement.offsetLeft; // force that style to take effect
    requestDiscard(img);

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

    await discardPromise;
    await new Promise(resolve => {requestAnimationFrame(() => { requestAnimationFrame(resolve); }); });

    let waitForFrameUpdate = new Promise(resolve => {
        frameUpdateCallback = resolve;
    });

    document.getElementById("container").style.display = "";
    document.documentElement.offsetLeft; // force that style to take effect

    await img.decode();

    await new Promise(resolve => requestAnimationFrame(resolve));

    await waitForFrameUpdate;

    ok(isItGreen(img), "should be green");

    iterationsLeft--;
    await new Promise(resolve => {requestAnimationFrame(() => { requestAnimationFrame(resolve); }); });

  }

  removeObserver(img);
  SimpleTest.finish();
}

function isItGreen(img) {
  let rect = img.getBoundingClientRect();
  let r = {left: rect.left + 5, top: rect.top + 5, width: 5, height: 5};
  let c = SpecialPowers.snapshotWindowWithOptions(window, r);
  let d = c.getContext('2d').getImageData(0,0,5,5).data;
  let isGreen = true;
  for (let i = 0; i < 5*5; i++) {
    if (d[4*i] != 0 || d[4*i + 1] != 128 || d[4*i + 2] != 0 || d[4*i + 3] != 255) {
      isGreen = false;
    }
  }
  return isGreen;
}


let scriptedObserver = undefined;
let imgLoadingContent = undefined;
function addCallbacks(anImage) {
  var observer = new ImageDecoderObserverStub();
  observer.discard = function () {
    if (discardCallback != undefined) {
      let localDiscardCallback = discardCallback;
      discardCallback = undefined;
      setTimeout(localDiscardCallback, 0);
    }
  };
  observer.frameUpdate = function () {
    if (frameUpdateCallback != undefined) {
      let localFrameUpdateCallback = frameUpdateCallback;
      frameUpdateCallback = undefined;
      setTimeout(localFrameUpdateCallback, 0);
    }
  };
  observer = SpecialPowers.wrapCallbackObject(observer);

  scriptedObserver = SpecialPowers.Cc["@mozilla.org/image/tools;1"]
                           .getService(SpecialPowers.Ci.imgITools)
                           .createScriptedObserver(observer);

  imgLoadingContent = SpecialPowers.wrap(anImage);
  imgLoadingContent.addObserver(scriptedObserver);
}

function removeObserver(anImage) {
  imgLoadingContent.removeObserver(scriptedObserver);
}

function requestDiscard(anImage) {
  var request = SpecialPowers.wrap(anImage)
      .getRequest(SpecialPowers.Ci.nsIImageLoadingContent.CURRENT_REQUEST);
  setTimeout(() => request.requestDiscard(), 0);
}

</script>
</body>
</html>
