summaryrefslogtreecommitdiffstats
path: root/image/test/mochitest/test_discardFramesAnimatedImage.html
diff options
context:
space:
mode:
Diffstat (limited to 'image/test/mochitest/test_discardFramesAnimatedImage.html')
-rw-r--r--image/test/mochitest/test_discardFramesAnimatedImage.html268
1 files changed, 268 insertions, 0 deletions
diff --git a/image/test/mochitest/test_discardFramesAnimatedImage.html b/image/test/mochitest/test_discardFramesAnimatedImage.html
new file mode 100644
index 0000000000..2e95e6203b
--- /dev/null
+++ b/image/test/mochitest/test_discardFramesAnimatedImage.html
@@ -0,0 +1,268 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=523950
+-->
+<head>
+ <title>Test that animated images can discard frames and redecode</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=523950">Mozilla Bug 523950</a>
+<p id="display"></p>
+<div id="content">
+ <div id="container">
+ <canvas id="canvas" width="100" height="100"></canvas>
+ <img id="rainbow.gif"/>
+ </div>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 523950. **/
+SimpleTest.waitForExplicitFinish();
+
+var gFinished = false;
+
+var gNumOnloads = 0;
+
+var gNumDiscards = 0;
+
+window.onload = function() {
+ // Enable minimal frame discard thresholds for the test.
+ SpecialPowers.pushPrefEnv({
+ 'set':[['image.animated.decode-on-demand.threshold-kb',0],
+ ['image.animated.decode-on-demand.batch-size',1],
+ ['image.mem.discardable',true],
+ ['image.mem.animated.discardable',true]]
+ }, runTest);
+}
+
+var gImgs = ['rainbow.gif'];
+// If we are currently counting frame updates.
+var gCountingFrameUpdates = false;
+// The number of frame update notifications for the images in gImgs that happen
+// after discarding. (The last two images are finite looping so we don't expect
+// them to get incremented but it's possible if they don't finish their
+// animation before we discard them.)
+var gNumFrameUpdates = [0];
+// The last snapshot of the image. Used to check that the image actually changes.
+var gLastSnapShot = [null];
+// Number of observed changes in the snapshot.
+var gNumSnapShotChanges = [0];
+// If we've removed the observer.
+var gRemovedObserver = [false];
+
+// rainbow.gif has 9 frames, so we choose arbitrarily 22 to include two loops.
+var kNumFrameUpdatesToExpect = 22;
+
+function runTest() {
+ var thresholdKb =
+ SpecialPowers.getIntPref('image.animated.decode-on-demand.threshold-kb');
+ var batchSize =
+ SpecialPowers.getIntPref('image.animated.decode-on-demand.batch-size');
+ var discardable =
+ SpecialPowers.getBoolPref('image.mem.discardable');
+ var animDiscardable =
+ SpecialPowers.getBoolPref('image.mem.animated.discardable');
+ if (thresholdKb > 0 || batchSize > 1 || !discardable || !animDiscardable) {
+ ok(true, "discarding frames of animated images is disabled, nothing to test");
+ SimpleTest.finish();
+ return;
+ }
+
+ setTimeout(step2, 0);
+}
+
+function step2() {
+ // Only set the src after setting the pref.
+ for (var i = 0; i < gImgs.length; i++) {
+ var elm = document.getElementById(gImgs[i]);
+ elm.src = gImgs[i];
+ elm.onload = checkIfLoaded;
+ }
+}
+
+function step3() {
+ // Draw the images to canvas to force them to be decoded.
+ for (let i = 0; i < gImgs.length; i++) {
+ drawCanvas(document.getElementById(gImgs[i]));
+ }
+
+ for (let i = 0; i < gImgs.length; i++) {
+ addCallbacks(document.getElementById(gImgs[i]), i);
+ }
+
+ setTimeout(step4, 0);
+}
+
+function step4() {
+ ok(true, "now accepting frame updates");
+ gCountingFrameUpdates = true;
+}
+
+function step5() {
+ ok(true, "discarding images");
+
+ document.getElementById("container").style.display = "none";
+ document.documentElement.offsetLeft; // force that style to take effect
+
+ // Reset our state to let us do it all again after discarding.
+ resetState();
+
+ // Draw the images to canvas to force them to be decoded.
+ for (var i = 0; i < gImgs.length; i++) {
+ requestDiscard(document.getElementById(gImgs[i]));
+ }
+
+ // the discard observers will call step6
+}
+
+function step6() {
+ // Repeat the cycle now that we discarded everything.
+ ok(gNumDiscards >= gImgs.length, "discard complete, restarting animations");
+ document.getElementById("container").style.display = "";
+
+ // Draw the images to canvas to force them to be decoded.
+ for (var i = 0; i < gImgs.length; i++) {
+ drawCanvas(document.getElementById(gImgs[i]));
+ }
+
+ setTimeout(step4, 0);
+}
+
+function checkIfLoaded() {
+ ++gNumOnloads;
+ if (gNumOnloads != gImgs.length) {
+ return;
+ }
+
+ ok(true, "got onloads");
+ setTimeout(step3, 0);
+}
+
+function resetState() {
+ gFinished = false;
+ gCountingFrameUpdates = false;
+ for (let i = 0; i < gNumFrameUpdates.length; ++i) {
+ gNumFrameUpdates[i] = 0;
+ }
+ for (let i = 0; i < gNumSnapShotChanges.length; ++i) {
+ gNumSnapShotChanges[i] = 0;
+ }
+ for (let i = 0; i < gLastSnapShot.length; ++i) {
+ gLastSnapShot[i] = null;
+ }
+}
+
+function checkIfFinished() {
+ if (gFinished) {
+ return;
+ }
+
+ for (var i = 0; i < gNumFrameUpdates.length; ++i) {
+ if (gNumFrameUpdates[i] < kNumFrameUpdatesToExpect ||
+ gNumSnapShotChanges[i] < kNumFrameUpdatesToExpect) {
+ return;
+ }
+ }
+
+ ok(true, "got expected frame updates");
+ gFinished = true;
+
+ if (gNumDiscards == 0) {
+ // If we haven't discarded any complete images, we should do so, and
+ // verify the animation again.
+ setTimeout(step5, 0);
+ return;
+ }
+
+ SimpleTest.finish();
+}
+
+// arrayIndex is the index into the arrays gNumFrameUpdates and gNumDecodes
+// to increment when a frame update notification is received.
+function addCallbacks(anImage, arrayIndex) {
+ var observer = new ImageDecoderObserverStub();
+ observer.discard = function () {
+ gNumDiscards++;
+ ok(true, "got image discard");
+ if (gNumDiscards == gImgs.length) {
+ step6();
+ }
+ };
+ observer.frameUpdate = function () {
+ if (!gCountingFrameUpdates) {
+ return;
+ }
+
+ // Do this off a setTimeout since nsImageLoadingContent uses a scriptblocker
+ // when it notifies us and calling drawWindow can call will paint observers
+ // which can dispatch a scrollport event, and events assert if dispatched
+ // when there is a scriptblocker.
+ setTimeout(function () {
+ gNumFrameUpdates[arrayIndex]++;
+
+ var r = document.getElementById(gImgs[arrayIndex]).getBoundingClientRect();
+ var snapshot = snapshotRect(window, r, "rgba(0,0,0,0)");
+ if (gLastSnapShot[arrayIndex] != null) {
+ if (snapshot.toDataURL() != gLastSnapShot[arrayIndex].toDataURL()) {
+ gNumSnapShotChanges[arrayIndex]++;
+ }
+ }
+ gLastSnapShot[arrayIndex] = snapshot;
+
+ if (gNumFrameUpdates[arrayIndex] >= kNumFrameUpdatesToExpect &&
+ gNumSnapShotChanges[arrayIndex] >= kNumFrameUpdatesToExpect &&
+ gNumDiscards >= gImgs.length) {
+ if (!gRemovedObserver[arrayIndex]) {
+ ok(true, "removing observer for " + arrayIndex);
+ gRemovedObserver[arrayIndex] = true;
+ imgLoadingContent.removeObserver(scriptedObserver);
+ }
+ }
+ if (!gFinished) {
+ // because we do this in a setTimeout we can have several in flight
+ // so don't call ok if we've already finished.
+ ok(true, "got frame update");
+ }
+ checkIfFinished();
+ }, 0);
+ };
+ observer = SpecialPowers.wrapCallbackObject(observer);
+
+ var scriptedObserver = SpecialPowers.Cc["@mozilla.org/image/tools;1"]
+ .getService(SpecialPowers.Ci.imgITools)
+ .createScriptedObserver(observer);
+
+ var imgLoadingContent = SpecialPowers.wrap(anImage);
+ imgLoadingContent.addObserver(scriptedObserver);
+}
+
+function requestDiscard(anImage) {
+ var request = SpecialPowers.wrap(anImage)
+ .getRequest(SpecialPowers.Ci.nsIImageLoadingContent.CURRENT_REQUEST);
+ setTimeout(() => request.requestDiscard(), 0);
+}
+
+function drawCanvas(anImage) {
+ var canvas = document.getElementById('canvas');
+ var context = canvas.getContext('2d');
+
+ context.clearRect(0,0,100,100);
+ var cleared = canvas.toDataURL();
+
+ context.drawImage(anImage, 0, 0);
+ ok(true, "we got through the drawImage call without an exception being thrown");
+
+ ok(cleared != canvas.toDataURL(), "drawImage drew something");
+}
+
+</script>
+</pre>
+</body>
+</html>
+