summaryrefslogtreecommitdiffstats
path: root/image/test/mochitest
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /image/test/mochitest
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'image/test/mochitest')
-rw-r--r--image/test/mochitest/.eslintrc.js5
-rw-r--r--image/test/mochitest/12M-pixels-1.pngbin0 -> 22467 bytes
-rw-r--r--image/test/mochitest/12M-pixels-2.pngbin0 -> 22467 bytes
-rw-r--r--image/test/mochitest/6M-pixels.pngbin0 -> 10147 bytes
-rw-r--r--image/test/mochitest/INT32_MIN.bmpbin0 -> 60 bytes
-rw-r--r--image/test/mochitest/animated-gif-finalframe.gifbin0 -> 72 bytes
-rw-r--r--image/test/mochitest/animated-gif.gifbin0 -> 146 bytes
-rw-r--r--image/test/mochitest/animated-gif2.gifbin0 -> 165 bytes
-rw-r--r--image/test/mochitest/animated-gif_trailing-garbage.gifbin0 -> 4030 bytes
-rw-r--r--image/test/mochitest/animated1.gifbin0 -> 4558 bytes
-rw-r--r--image/test/mochitest/animated2.gifbin0 -> 4558 bytes
-rw-r--r--image/test/mochitest/animation.svg5
-rw-r--r--image/test/mochitest/animationPolling.js469
-rw-r--r--image/test/mochitest/bad.jpgbin0 -> 2477 bytes
-rw-r--r--image/test/mochitest/big.pngbin0 -> 129497 bytes
-rw-r--r--image/test/mochitest/blue.gifbin0 -> 45 bytes
-rw-r--r--image/test/mochitest/blue.pngbin0 -> 2745 bytes
-rw-r--r--image/test/mochitest/bug1132427.gifbin0 -> 634 bytes
-rw-r--r--image/test/mochitest/bug1132427.html6
-rw-r--r--image/test/mochitest/bug1180105-waiter.sjs24
-rw-r--r--image/test/mochitest/bug1180105.sjs63
-rw-r--r--image/test/mochitest/bug1217571-iframe.html17
-rw-r--r--image/test/mochitest/bug1217571.jpgbin0 -> 2679 bytes
-rw-r--r--image/test/mochitest/bug1319025-ref.pngbin0 -> 347 bytes
-rw-r--r--image/test/mochitest/bug1319025.pngbin0 -> 422 bytes
-rw-r--r--image/test/mochitest/bug399925.gifbin0 -> 1645 bytes
-rw-r--r--image/test/mochitest/bug415761.icobin0 -> 766 bytes
-rw-r--r--image/test/mochitest/bug468160.sjs6
-rw-r--r--image/test/mochitest/bug478398_ONLY.pngbin0 -> 14139 bytes
-rw-r--r--image/test/mochitest/bug490949-iframe.html7
-rw-r--r--image/test/mochitest/bug490949.sjs33
-rw-r--r--image/test/mochitest/bug496292-1.sjs32
-rw-r--r--image/test/mochitest/bug496292-2.sjs32
-rw-r--r--image/test/mochitest/bug496292-iframe-1.html7
-rw-r--r--image/test/mochitest/bug496292-iframe-2.html7
-rw-r--r--image/test/mochitest/bug496292-iframe-ref.html7
-rw-r--r--image/test/mochitest/bug497665-iframe.html8
-rw-r--r--image/test/mochitest/bug497665.sjs34
-rw-r--r--image/test/mochitest/bug552605.sjs13
-rw-r--r--image/test/mochitest/bug657191.sjs27
-rw-r--r--image/test/mochitest/bug671906-iframe.html7
-rw-r--r--image/test/mochitest/bug671906.sjs36
-rw-r--r--image/test/mochitest/bug733553-informant.sjs15
-rw-r--r--image/test/mochitest/bug733553.sjs104
-rw-r--r--image/test/mochitest/bug767779.sjs56
-rw-r--r--image/test/mochitest/bug89419-iframe.html7
-rw-r--r--image/test/mochitest/bug89419.sjs13
-rw-r--r--image/test/mochitest/bug900200-ref.pngbin0 -> 660 bytes
-rw-r--r--image/test/mochitest/bug900200.pngbin0 -> 840 bytes
-rw-r--r--image/test/mochitest/child.html22
-rw-r--r--image/test/mochitest/chrome.ini7
-rw-r--r--image/test/mochitest/clear.gifbin0 -> 321 bytes
-rw-r--r--image/test/mochitest/clear.pngbin0 -> 622 bytes
-rw-r--r--image/test/mochitest/clear.webpbin0 -> 202 bytes
-rw-r--r--image/test/mochitest/clear2-results.gifbin0 -> 177 bytes
-rw-r--r--image/test/mochitest/clear2.gifbin0 -> 219 bytes
-rw-r--r--image/test/mochitest/clear2.webpbin0 -> 228 bytes
-rw-r--r--image/test/mochitest/damon.jpgbin0 -> 2679 bytes
-rw-r--r--image/test/mochitest/error-early.png1
-rw-r--r--image/test/mochitest/filter-final.svg9
-rw-r--r--image/test/mochitest/filter.svg9
-rw-r--r--image/test/mochitest/finite-apng.pngbin0 -> 5834 bytes
-rw-r--r--image/test/mochitest/first-frame-padding.gifbin0 -> 49 bytes
-rw-r--r--image/test/mochitest/green-background.html29
-rw-r--r--image/test/mochitest/green.pngbin0 -> 255 bytes
-rw-r--r--image/test/mochitest/grey.pngbin0 -> 256 bytes
-rw-r--r--image/test/mochitest/ico-bmp-opaque.icobin0 -> 1094 bytes
-rw-r--r--image/test/mochitest/ico-bmp-transparent.icobin0 -> 4286 bytes
-rw-r--r--image/test/mochitest/iframe.html5
-rw-r--r--image/test/mochitest/imgutils.js137
-rw-r--r--image/test/mochitest/infinite-apng.pngbin0 -> 1169 bytes
-rw-r--r--image/test/mochitest/infinite.webpbin0 -> 3742 bytes
-rw-r--r--image/test/mochitest/invalid.jpg1
-rw-r--r--image/test/mochitest/keep.gifbin0 -> 321 bytes
-rw-r--r--image/test/mochitest/keep.pngbin0 -> 622 bytes
-rw-r--r--image/test/mochitest/keep.webpbin0 -> 152 bytes
-rw-r--r--image/test/mochitest/lime-anim-100x100-2.svg6
-rw-r--r--image/test/mochitest/lime-anim-100x100.svg7
-rw-r--r--image/test/mochitest/lime-css-anim-100x100.svg19
-rw-r--r--image/test/mochitest/lime100x100.svg4
-rw-r--r--image/test/mochitest/mochitest.ini162
-rw-r--r--image/test/mochitest/mq_dynamic_svg_ref.html38
-rw-r--r--image/test/mochitest/mq_dynamic_svg_test.html61
-rw-r--r--image/test/mochitest/opaque.bmpbin0 -> 1086 bytes
-rw-r--r--image/test/mochitest/over.pngbin0 -> 525 bytes
-rw-r--r--image/test/mochitest/purple.gifbin0 -> 86 bytes
-rw-r--r--image/test/mochitest/rainbow.gifbin0 -> 1572 bytes
-rw-r--r--image/test/mochitest/red.gifbin0 -> 43 bytes
-rw-r--r--image/test/mochitest/red.pngbin0 -> 82 bytes
-rw-r--r--image/test/mochitest/ref-iframe.html6
-rw-r--r--image/test/mochitest/restore-previous.gifbin0 -> 457 bytes
-rw-r--r--image/test/mochitest/restore-previous.pngbin0 -> 622 bytes
-rw-r--r--image/test/mochitest/rillybad.jpgbin0 -> 11142 bytes
-rw-r--r--image/test/mochitest/schrep.pngbin0 -> 38767 bytes
-rw-r--r--image/test/mochitest/shaver.pngbin0 -> 52045 bytes
-rw-r--r--image/test/mochitest/short_header.gifbin0 -> 1488 bytes
-rw-r--r--image/test/mochitest/source.pngbin0 -> 525 bytes
-rw-r--r--image/test/mochitest/test_animSVGImage.html124
-rw-r--r--image/test/mochitest/test_animSVGImage2.html124
-rw-r--r--image/test/mochitest/test_animated_css_image.html165
-rw-r--r--image/test/mochitest/test_animated_gif.html50
-rw-r--r--image/test/mochitest/test_animation.html45
-rw-r--r--image/test/mochitest/test_animation2.html49
-rw-r--r--image/test/mochitest/test_animation_operators.html167
-rw-r--r--image/test/mochitest/test_background_image_anim.html44
-rw-r--r--image/test/mochitest/test_bug1132427.html94
-rw-r--r--image/test/mochitest/test_bug1180105.html46
-rw-r--r--image/test/mochitest/test_bug1217571.html43
-rw-r--r--image/test/mochitest/test_bug1325080.html37
-rw-r--r--image/test/mochitest/test_bug399925.html102
-rw-r--r--image/test/mochitest/test_bug415761.html118
-rw-r--r--image/test/mochitest/test_bug435296.html85
-rw-r--r--image/test/mochitest/test_bug466586.html58
-rw-r--r--image/test/mochitest/test_bug468160.html29
-rw-r--r--image/test/mochitest/test_bug478398.html87
-rw-r--r--image/test/mochitest/test_bug490949.html112
-rw-r--r--image/test/mochitest/test_bug496292.html130
-rw-r--r--image/test/mochitest/test_bug497665.html92
-rw-r--r--image/test/mochitest/test_bug552605-1.html56
-rw-r--r--image/test/mochitest/test_bug552605-2.html53
-rw-r--r--image/test/mochitest/test_bug553982.html39
-rw-r--r--image/test/mochitest/test_bug601470.html45
-rw-r--r--image/test/mochitest/test_bug614392.html42
-rw-r--r--image/test/mochitest/test_bug657191.html34
-rw-r--r--image/test/mochitest/test_bug671906.html71
-rw-r--r--image/test/mochitest/test_bug733553.html92
-rw-r--r--image/test/mochitest/test_bug767779.html44
-rw-r--r--image/test/mochitest/test_bug865919.html53
-rw-r--r--image/test/mochitest/test_bug89419-1.html68
-rw-r--r--image/test/mochitest/test_bug89419-2.html70
-rw-r--r--image/test/mochitest/test_bullet_animation.html56
-rw-r--r--image/test/mochitest/test_canvas_frame_animation.html24
-rw-r--r--image/test/mochitest/test_changeOfSource.html62
-rw-r--r--image/test/mochitest/test_changeOfSource2.html47
-rw-r--r--image/test/mochitest/test_discardAnimatedImage.html215
-rw-r--r--image/test/mochitest/test_discardFinishedAnimatedImage.html144
-rw-r--r--image/test/mochitest/test_discardFramesAnimatedImage.html268
-rw-r--r--image/test/mochitest/test_drawDiscardedImage.html85
-rw-r--r--image/test/mochitest/test_error_events.html67
-rw-r--r--image/test/mochitest/test_has_transparency.html169
-rw-r--r--image/test/mochitest/test_image_crossorigin_data_url.html28
-rw-r--r--image/test/mochitest/test_mq_dynamic_svg.html36
-rw-r--r--image/test/mochitest/test_net_failedtoprocess.html52
-rw-r--r--image/test/mochitest/test_removal_ondecode.html160
-rw-r--r--image/test/mochitest/test_removal_onload.html142
-rw-r--r--image/test/mochitest/test_short_gif_header.html35
-rw-r--r--image/test/mochitest/test_staticClone.html41
-rw-r--r--image/test/mochitest/test_svg_animatedGIF.html53
-rw-r--r--image/test/mochitest/test_svg_filter_animation.html42
-rw-r--r--image/test/mochitest/test_synchronized_animation.html128
-rw-r--r--image/test/mochitest/test_undisplayed_iframe.html47
-rw-r--r--image/test/mochitest/test_webcam.html68
-rw-r--r--image/test/mochitest/test_xultree_animation.xhtml67
-rw-r--r--image/test/mochitest/transparent.gifbin0 -> 355 bytes
-rw-r--r--image/test/mochitest/transparent.pngbin0 -> 419 bytes
-rw-r--r--image/test/mochitest/webcam-simulacrum.sjs51
156 files changed, 6118 insertions, 0 deletions
diff --git a/image/test/mochitest/.eslintrc.js b/image/test/mochitest/.eslintrc.js
new file mode 100644
index 0000000000..721e0938af
--- /dev/null
+++ b/image/test/mochitest/.eslintrc.js
@@ -0,0 +1,5 @@
+"use strict";
+
+module.exports = {
+ extends: ["plugin:mozilla/mochitest-test", "plugin:mozilla/chrome-test"],
+};
diff --git a/image/test/mochitest/12M-pixels-1.png b/image/test/mochitest/12M-pixels-1.png
new file mode 100644
index 0000000000..f802dd5396
--- /dev/null
+++ b/image/test/mochitest/12M-pixels-1.png
Binary files differ
diff --git a/image/test/mochitest/12M-pixels-2.png b/image/test/mochitest/12M-pixels-2.png
new file mode 100644
index 0000000000..a6d430442e
--- /dev/null
+++ b/image/test/mochitest/12M-pixels-2.png
Binary files differ
diff --git a/image/test/mochitest/6M-pixels.png b/image/test/mochitest/6M-pixels.png
new file mode 100644
index 0000000000..c813d8b569
--- /dev/null
+++ b/image/test/mochitest/6M-pixels.png
Binary files differ
diff --git a/image/test/mochitest/INT32_MIN.bmp b/image/test/mochitest/INT32_MIN.bmp
new file mode 100644
index 0000000000..d9a0016107
--- /dev/null
+++ b/image/test/mochitest/INT32_MIN.bmp
Binary files differ
diff --git a/image/test/mochitest/animated-gif-finalframe.gif b/image/test/mochitest/animated-gif-finalframe.gif
new file mode 100644
index 0000000000..4e80d31a72
--- /dev/null
+++ b/image/test/mochitest/animated-gif-finalframe.gif
Binary files differ
diff --git a/image/test/mochitest/animated-gif.gif b/image/test/mochitest/animated-gif.gif
new file mode 100644
index 0000000000..001cbfb87a
--- /dev/null
+++ b/image/test/mochitest/animated-gif.gif
Binary files differ
diff --git a/image/test/mochitest/animated-gif2.gif b/image/test/mochitest/animated-gif2.gif
new file mode 100644
index 0000000000..c66cc4b734
--- /dev/null
+++ b/image/test/mochitest/animated-gif2.gif
Binary files differ
diff --git a/image/test/mochitest/animated-gif_trailing-garbage.gif b/image/test/mochitest/animated-gif_trailing-garbage.gif
new file mode 100644
index 0000000000..02f4de2e31
--- /dev/null
+++ b/image/test/mochitest/animated-gif_trailing-garbage.gif
Binary files differ
diff --git a/image/test/mochitest/animated1.gif b/image/test/mochitest/animated1.gif
new file mode 100644
index 0000000000..2f9d8a512b
--- /dev/null
+++ b/image/test/mochitest/animated1.gif
Binary files differ
diff --git a/image/test/mochitest/animated2.gif b/image/test/mochitest/animated2.gif
new file mode 100644
index 0000000000..2f9d8a512b
--- /dev/null
+++ b/image/test/mochitest/animated2.gif
Binary files differ
diff --git a/image/test/mochitest/animation.svg b/image/test/mochitest/animation.svg
new file mode 100644
index 0000000000..2141d86791
--- /dev/null
+++ b/image/test/mochitest/animation.svg
@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <image id="anim" xlink:href="animated-gif.gif" width="40" height="40"/>
+</svg>
+
diff --git a/image/test/mochitest/animationPolling.js b/image/test/mochitest/animationPolling.js
new file mode 100644
index 0000000000..272b517f24
--- /dev/null
+++ b/image/test/mochitest/animationPolling.js
@@ -0,0 +1,469 @@
+// This file expects imgutils.js to be loaded as well.
+/* import-globals-from imgutils.js */
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+var currentTest;
+var gIsRefImageLoaded = false;
+const gShouldOutputDebugInfo = false;
+
+function pollForSuccess() {
+ if (!currentTest.isTestFinished) {
+ if (
+ !currentTest.reusingReferenceImage ||
+ (currentTest.reusingReferenceImage && gIsRefImageLoaded)
+ ) {
+ currentTest.checkImage();
+ }
+
+ setTimeout(pollForSuccess, currentTest.pollFreq);
+ }
+}
+
+function reuseImageCallback() {
+ gIsRefImageLoaded = true;
+}
+
+function failTest() {
+ if (currentTest.isTestFinished || currentTest.closeFunc) {
+ return;
+ }
+
+ ok(
+ false,
+ "timing out after " +
+ currentTest.timeout +
+ "ms. " +
+ "Animated image still doesn't look correct, after poll #" +
+ currentTest.pollCounter
+ );
+ currentTest.wereFailures = true;
+
+ if (currentTest.currentSnapshotDataURI) {
+ currentTest.outputDebugInfo(
+ "Snapshot #" + currentTest.pollCounter,
+ "snapNum" + currentTest.pollCounter,
+ currentTest.currentSnapshotDataURI
+ );
+ }
+
+ currentTest.enableDisplay(
+ document.getElementById(currentTest.debugElementId)
+ );
+
+ currentTest.cleanUpAndFinish();
+}
+
+/**
+ * Create a new AnimationTest object.
+ *
+ * @param pollFreq The amount of time (in ms) to wait between consecutive
+ * snapshots if the reference image and the test image don't match.
+ * @param timeout The total amount of time (in ms) to wait before declaring the
+ * test as failed.
+ * @param referenceElementId The id attribute of the reference image element, or
+ * the source of the image to change to, once the reference snapshot has
+ * been successfully taken. This latter option could be used if you don't
+ * want the image to become invisible at any time during the test.
+ * @param imageElementId The id attribute of the test image element.
+ * @param debugElementId The id attribute of the div where links should be
+ * appended if the test fails.
+ * @param cleanId The id attribute of the div or element to use as the 'clean'
+ * test. This element is only enabled when we are testing to verify that
+ * the reference image has been loaded. It can be undefined.
+ * @param srcAttr The location of the source of the image, for preloading. This
+ * is usually not required, but it useful for preloading reference
+ * images.
+ * @param xulTest A boolean value indicating whether or not this is a XUL test
+ * (uses hidden=true/false rather than display: none to hide/show
+ * elements).
+ * @param closeFunc A function that should be called when this test is finished.
+ * If null, then cleanUpAndFinish() will be called. This can be used to
+ * chain tests together, so they are all finished exactly once.
+ * @returns {AnimationTest}
+ */
+function AnimationTest(
+ pollFreq,
+ timeout,
+ referenceElementId,
+ imageElementId,
+ debugElementId,
+ cleanId,
+ srcAttr,
+ xulTest,
+ closeFunc
+) {
+ // We want to test the cold loading behavior, so clear cache in case an
+ // earlier test got our image in there already.
+ clearAllImageCaches();
+
+ this.wereFailures = false;
+ this.pollFreq = pollFreq;
+ this.timeout = timeout;
+ this.imageElementId = imageElementId;
+ this.referenceElementId = referenceElementId;
+
+ if (!document.getElementById(referenceElementId)) {
+ // In this case, we're assuming the user passed in a string that
+ // indicates the source of the image they want to change to,
+ // after the reference image has been taken.
+ this.reusingImageAsReference = true;
+ }
+
+ this.srcAttr = srcAttr;
+ this.debugElementId = debugElementId;
+ this.referenceSnapshot = ""; // value will be set in takeReferenceSnapshot()
+ this.pollCounter = 0;
+ this.isTestFinished = false;
+ this.numRefsTaken = 0;
+ this.blankWaitTime = 0;
+
+ this.cleanId = cleanId ? cleanId : "";
+ this.xulTest = xulTest ? xulTest : "";
+ this.closeFunc = closeFunc ? closeFunc : "";
+}
+
+AnimationTest.prototype.preloadImage = function() {
+ if (this.srcAttr) {
+ this.myImage = new Image();
+ this.myImage.onload = function() {
+ currentTest.continueTest();
+ };
+ this.myImage.src = this.srcAttr;
+ } else {
+ this.continueTest();
+ }
+};
+
+AnimationTest.prototype.outputDebugInfo = function(message, id, dataUri) {
+ if (!gShouldOutputDebugInfo) {
+ return;
+ }
+ var debugElement = document.getElementById(this.debugElementId);
+ var newDataUriElement = document.createElement("a");
+ newDataUriElement.setAttribute("id", id);
+ newDataUriElement.setAttribute("href", dataUri);
+ newDataUriElement.appendChild(document.createTextNode(message));
+ debugElement.appendChild(newDataUriElement);
+ var brElement = document.createElement("br");
+ debugElement.appendChild(brElement);
+ todo(false, "Debug (" + id + "): " + message + " " + dataUri);
+};
+
+AnimationTest.prototype.isFinished = function() {
+ return this.isTestFinished;
+};
+
+AnimationTest.prototype.takeCleanSnapshot = function() {
+ var cleanElement;
+ if (this.cleanId) {
+ cleanElement = document.getElementById(this.cleanId);
+ }
+
+ // Enable clean page comparison element
+ if (cleanElement) {
+ this.enableDisplay(cleanElement);
+ }
+
+ // Take a snapshot of the initial (clean) page
+ this.cleanSnapshot = snapshotWindow(window, false);
+
+ // Disable the clean page comparison element
+ if (cleanElement) {
+ this.disableDisplay(cleanElement);
+ }
+
+ var dataString1 = "Clean Snapshot";
+ this.outputDebugInfo(
+ dataString1,
+ "cleanSnap",
+ this.cleanSnapshot.toDataURL()
+ );
+};
+
+AnimationTest.prototype.takeBlankSnapshot = function() {
+ // Take a snapshot of the initial (essentially blank) page
+ this.blankSnapshot = snapshotWindow(window, false);
+
+ var dataString1 = "Initial Blank Snapshot";
+ this.outputDebugInfo(
+ dataString1,
+ "blank1Snap",
+ this.blankSnapshot.toDataURL()
+ );
+};
+
+/**
+ * Begin the AnimationTest. This will utilize the information provided in the
+ * constructor to invoke a mochitest on animated images. It will automatically
+ * fail if allowed to run past the timeout. This will attempt to preload an
+ * image, if applicable, and then asynchronously call continueTest(), or if not
+ * applicable, synchronously trigger a call to continueTest().
+ */
+AnimationTest.prototype.beginTest = function() {
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("untriaged");
+
+ currentTest = this;
+ this.preloadImage();
+};
+
+/**
+ * This is the second part of the test. It is triggered (eventually) from
+ * beginTest() either synchronously or asynchronously, as an image load
+ * callback.
+ */
+AnimationTest.prototype.continueTest = async function() {
+ // In case something goes wrong, fail earlier than mochitest timeout,
+ // and with more information.
+ setTimeout(failTest, this.timeout);
+
+ if (!this.reusingImageAsReference) {
+ this.disableDisplay(document.getElementById(this.imageElementId));
+ }
+
+ let tookReference = new Promise(resolve => {
+ this.takeReferenceSnapshot(resolve);
+ });
+
+ tookReference.then(() => {
+ this.setupPolledImage();
+ SimpleTest.executeSoon(pollForSuccess);
+ });
+};
+
+AnimationTest.prototype.setupPolledImage = function() {
+ // Make sure the image is visible
+ if (!this.reusingImageAsReference) {
+ this.enableDisplay(document.getElementById(this.imageElementId));
+ var currentSnapshot = snapshotWindow(window, false);
+ var result = compareSnapshots(
+ currentSnapshot,
+ this.referenceSnapshot,
+ true
+ );
+
+ this.currentSnapshotDataURI = currentSnapshot.toDataURL();
+
+ if (result[0]) {
+ // SUCCESS!
+ ok(true, "Animated image looks correct, at poll #" + this.pollCounter);
+
+ this.outputDebugInfo(
+ "Animated image",
+ "animImage",
+ this.currentSnapshotDataURI
+ );
+
+ this.outputDebugInfo(
+ "Reference image",
+ "refImage",
+ this.referenceSnapshot.toDataURL()
+ );
+
+ this.cleanUpAndFinish();
+ }
+ } else if (!gIsRefImageLoaded) {
+ this.myImage = new Image();
+ this.myImage.onload = reuseImageCallback;
+ document
+ .getElementById(this.imageElementId)
+ .setAttribute("src", this.referenceElementId);
+ }
+};
+
+AnimationTest.prototype.checkImage = function() {
+ if (this.isTestFinished) {
+ return;
+ }
+
+ this.pollCounter++;
+
+ // We need this for some tests, because we need to force the
+ // test image to be visible.
+ if (!this.reusingImageAsReference) {
+ this.enableDisplay(document.getElementById(this.imageElementId));
+ }
+
+ var currentSnapshot = snapshotWindow(window, false);
+ var result = compareSnapshots(currentSnapshot, this.referenceSnapshot, true);
+
+ this.currentSnapshotDataURI = currentSnapshot.toDataURL();
+
+ if (result[0]) {
+ // SUCCESS!
+ ok(true, "Animated image looks correct, at poll #" + this.pollCounter);
+
+ this.outputDebugInfo("Animated image", "animImage", result[1]);
+
+ this.outputDebugInfo("Reference image", "refImage", result[2]);
+
+ this.cleanUpAndFinish();
+ }
+};
+
+AnimationTest.prototype.takeReferenceSnapshot = function(resolve) {
+ this.numRefsTaken++;
+
+ // Test to make sure the reference image doesn't match a clean snapshot
+ if (!this.cleanSnapshot) {
+ this.takeCleanSnapshot();
+ }
+
+ // Used later to verify that the reference div disappeared
+ if (!this.blankSnapshot) {
+ this.takeBlankSnapshot();
+ }
+
+ if (this.reusingImageAsReference) {
+ // Show reference elem (which is actually our image), & take a snapshot
+ var referenceElem = document.getElementById(this.imageElementId);
+ this.enableDisplay(referenceElem);
+
+ this.referenceSnapshot = snapshotWindow(window, false);
+
+ let snapResult = compareSnapshots(
+ this.cleanSnapshot,
+ this.referenceSnapshot,
+ false
+ );
+ if (!snapResult[0]) {
+ if (this.blankWaitTime > 2000) {
+ // if it took longer than two seconds to load the image, we probably
+ // have a problem.
+ this.wereFailures = true;
+ ok(
+ snapResult[0],
+ "Reference snapshot shouldn't match clean (non-image) snapshot"
+ );
+ } else {
+ this.blankWaitTime += currentTest.pollFreq;
+ // let's wait a bit and see if it clears up
+ setTimeout(
+ () => this.takeReferenceSnapshot(resolve),
+ currentTest.pollFreq
+ );
+ return;
+ }
+ }
+
+ ok(
+ snapResult[0],
+ "Reference snapshot shouldn't match clean (non-image) snapshot"
+ );
+
+ let dataString = "Reference Snapshot #" + this.numRefsTaken;
+ this.outputDebugInfo(
+ dataString,
+ "refSnapId",
+ this.referenceSnapshot.toDataURL()
+ );
+ } else {
+ // Make sure the animation section is hidden
+ this.disableDisplay(document.getElementById(this.imageElementId));
+
+ // Show reference div, & take a snapshot
+ var referenceDiv = document.getElementById(this.referenceElementId);
+ this.enableDisplay(referenceDiv);
+
+ this.referenceSnapshot = snapshotWindow(window, false);
+ let snapResult = compareSnapshots(
+ this.cleanSnapshot,
+ this.referenceSnapshot,
+ false
+ );
+ if (!snapResult[0]) {
+ if (this.blankWaitTime > 2000) {
+ // if it took longer than two seconds to load the image, we probably
+ // have a problem.
+ this.wereFailures = true;
+ ok(
+ snapResult[0],
+ "Reference snapshot shouldn't match clean (non-image) snapshot"
+ );
+ } else {
+ this.blankWaitTime += 20;
+ // let's wait a bit and see if it clears up
+ setTimeout(() => this.takeReferenceSnapshot(resolve), 20);
+ return;
+ }
+ }
+
+ ok(
+ snapResult[0],
+ "Reference snapshot shouldn't match clean (non-image) snapshot"
+ );
+
+ let dataString = "Reference Snapshot #" + this.numRefsTaken;
+ this.outputDebugInfo(
+ dataString,
+ "refSnapId",
+ this.referenceSnapshot.toDataURL()
+ );
+
+ // Re-hide reference div, and take another snapshot to be sure it's gone
+ this.disableDisplay(referenceDiv);
+ this.testBlankCameBack();
+ }
+ resolve();
+};
+
+AnimationTest.prototype.enableDisplay = function(element) {
+ if (!element) {
+ return;
+ }
+
+ if (!this.xulTest) {
+ element.style.display = "";
+ } else {
+ element.setAttribute("hidden", "false");
+ }
+};
+
+AnimationTest.prototype.disableDisplay = function(element) {
+ if (!element) {
+ return;
+ }
+
+ if (!this.xulTest) {
+ element.style.display = "none";
+ } else {
+ element.setAttribute("hidden", "true");
+ }
+};
+
+AnimationTest.prototype.testBlankCameBack = function() {
+ var blankSnapshot2 = snapshotWindow(window, false);
+ var result = compareSnapshots(this.blankSnapshot, blankSnapshot2, true);
+ ok(
+ result[0],
+ "Reference image should disappear when it becomes display:none"
+ );
+
+ if (!result[0]) {
+ this.wereFailures = true;
+ var dataString = "Second Blank Snapshot";
+ this.outputDebugInfo(dataString, "blank2SnapId", result[2]);
+ }
+};
+
+AnimationTest.prototype.cleanUpAndFinish = function() {
+ // On the off chance that failTest and checkImage are triggered
+ // back-to-back, use a flag to prevent multiple calls to SimpleTest.finish.
+ if (this.isTestFinished) {
+ return;
+ }
+
+ this.isTestFinished = true;
+
+ // Call our closing function, if one exists
+ if (this.closeFunc) {
+ this.closeFunc();
+ return;
+ }
+
+ if (this.wereFailures) {
+ document.getElementById(this.debugElementId).style.display = "block";
+ }
+
+ SimpleTest.finish();
+ document.getElementById(this.debugElementId).style.display = "";
+};
diff --git a/image/test/mochitest/bad.jpg b/image/test/mochitest/bad.jpg
new file mode 100644
index 0000000000..555a416d7d
--- /dev/null
+++ b/image/test/mochitest/bad.jpg
Binary files differ
diff --git a/image/test/mochitest/big.png b/image/test/mochitest/big.png
new file mode 100644
index 0000000000..94e7eb6db2
--- /dev/null
+++ b/image/test/mochitest/big.png
Binary files differ
diff --git a/image/test/mochitest/blue.gif b/image/test/mochitest/blue.gif
new file mode 100644
index 0000000000..339f3702fb
--- /dev/null
+++ b/image/test/mochitest/blue.gif
Binary files differ
diff --git a/image/test/mochitest/blue.png b/image/test/mochitest/blue.png
new file mode 100644
index 0000000000..8df58f3a5f
--- /dev/null
+++ b/image/test/mochitest/blue.png
Binary files differ
diff --git a/image/test/mochitest/bug1132427.gif b/image/test/mochitest/bug1132427.gif
new file mode 100644
index 0000000000..39f49689a0
--- /dev/null
+++ b/image/test/mochitest/bug1132427.gif
Binary files differ
diff --git a/image/test/mochitest/bug1132427.html b/image/test/mochitest/bug1132427.html
new file mode 100644
index 0000000000..c765ce14ca
--- /dev/null
+++ b/image/test/mochitest/bug1132427.html
@@ -0,0 +1,6 @@
+<html>
+<body onload="opener.doTest();">
+ <img id="left" style="width: 201px; height: 201px;" src="bug1132427.gif">
+ <img id="right" src="bug1132427.gif">
+</body>
+</html>
diff --git a/image/test/mochitest/bug1180105-waiter.sjs b/image/test/mochitest/bug1180105-waiter.sjs
new file mode 100644
index 0000000000..4e86ae2879
--- /dev/null
+++ b/image/test/mochitest/bug1180105-waiter.sjs
@@ -0,0 +1,24 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var timer = Components.classes["@mozilla.org/timer;1"];
+var waitTimer = timer.createInstance(Components.interfaces.nsITimer);
+
+function handleRequest(request, response) {
+ response.setHeader("Content-Type", "text/html", false);
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.processAsync();
+ waitForFinish(response);
+}
+
+function waitForFinish(response) {
+ if (getSharedState("all-parts-done") === "1") {
+ response.write("done");
+ response.finish();
+ } else {
+ waitTimer.initWithCallback(function() {waitForFinish(response);}, 10,
+ Components.interfaces.nsITimer.TYPE_ONE_SHOT);
+ }
+}
diff --git a/image/test/mochitest/bug1180105.sjs b/image/test/mochitest/bug1180105.sjs
new file mode 100644
index 0000000000..e138e548ee
--- /dev/null
+++ b/image/test/mochitest/bug1180105.sjs
@@ -0,0 +1,63 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var counter = 100;
+var timer = Components.classes["@mozilla.org/timer;1"];
+var partTimer = timer.createInstance(Components.interfaces.nsITimer);
+
+function getFileAsInputStream(aFilename) {
+ var file = Components.classes["@mozilla.org/file/directory_service;1"]
+ .getService(Components.interfaces.nsIProperties)
+ .get("CurWorkD", Components.interfaces.nsIFile);
+
+ file.append("tests");
+ file.append("image");
+ file.append("test");
+ file.append("mochitest");
+ file.append(aFilename);
+
+ var fileStream = Components.classes['@mozilla.org/network/file-input-stream;1']
+ .createInstance(Components.interfaces.nsIFileInputStream);
+ fileStream.init(file, 1, 0, false);
+ return fileStream;
+}
+
+function handleRequest(request, response)
+{
+ response.setHeader("Content-Type",
+ "multipart/x-mixed-replace;boundary=BOUNDARYOMG", false);
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ // We're sending parts off in a delayed fashion, to let the tests occur.
+ response.processAsync();
+ response.write("--BOUNDARYOMG\r\n");
+ sendParts(response);
+}
+
+function sendParts(response) {
+ if (counter-- == 0) {
+ sendClose(response);
+ setSharedState("all-parts-done", "1");
+ return;
+ }
+ sendNextPart(response);
+ partTimer.initWithCallback(function() {sendParts(response);}, 1,
+ Components.interfaces.nsITimer.TYPE_ONE_SHOT);
+}
+
+function sendClose(response) {
+ response.write("--BOUNDARYOMG--\r\n");
+ response.finish();
+}
+
+function sendNextPart(response) {
+ var nextPartHead = "Content-Type: image/jpeg\r\n\r\n";
+ var inputStream = getFileAsInputStream("damon.jpg");
+ response.bodyOutputStream.write(nextPartHead, nextPartHead.length);
+ response.bodyOutputStream.writeFrom(inputStream, inputStream.available());
+ inputStream.close();
+ // Toss in the boundary, so the browser can know this part is complete
+ response.write("--BOUNDARYOMG\r\n");
+}
+
diff --git a/image/test/mochitest/bug1217571-iframe.html b/image/test/mochitest/bug1217571-iframe.html
new file mode 100644
index 0000000000..ab243d5cf5
--- /dev/null
+++ b/image/test/mochitest/bug1217571-iframe.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1217571
+-->
+<head>
+ <title>iframe for Bug 1217571</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <img src="bug1217571.jpg">
+</div>
+</body>
+</html>
diff --git a/image/test/mochitest/bug1217571.jpg b/image/test/mochitest/bug1217571.jpg
new file mode 100644
index 0000000000..917b336607
--- /dev/null
+++ b/image/test/mochitest/bug1217571.jpg
Binary files differ
diff --git a/image/test/mochitest/bug1319025-ref.png b/image/test/mochitest/bug1319025-ref.png
new file mode 100644
index 0000000000..482d027a02
--- /dev/null
+++ b/image/test/mochitest/bug1319025-ref.png
Binary files differ
diff --git a/image/test/mochitest/bug1319025.png b/image/test/mochitest/bug1319025.png
new file mode 100644
index 0000000000..8023e77879
--- /dev/null
+++ b/image/test/mochitest/bug1319025.png
Binary files differ
diff --git a/image/test/mochitest/bug399925.gif b/image/test/mochitest/bug399925.gif
new file mode 100644
index 0000000000..fc1c8f3af0
--- /dev/null
+++ b/image/test/mochitest/bug399925.gif
Binary files differ
diff --git a/image/test/mochitest/bug415761.ico b/image/test/mochitest/bug415761.ico
new file mode 100644
index 0000000000..d3f65abc23
--- /dev/null
+++ b/image/test/mochitest/bug415761.ico
Binary files differ
diff --git a/image/test/mochitest/bug468160.sjs b/image/test/mochitest/bug468160.sjs
new file mode 100644
index 0000000000..4a654216a5
--- /dev/null
+++ b/image/test/mochitest/bug468160.sjs
@@ -0,0 +1,6 @@
+function handleRequest(request, response)
+{
+ response.setStatusLine("1.1", 302, "Found");
+ response.setHeader("Location", "red.png", false);
+ response.setHeader("Cache-Control", "no-cache", false);
+}
diff --git a/image/test/mochitest/bug478398_ONLY.png b/image/test/mochitest/bug478398_ONLY.png
new file mode 100644
index 0000000000..e094ae2cf4
--- /dev/null
+++ b/image/test/mochitest/bug478398_ONLY.png
Binary files differ
diff --git a/image/test/mochitest/bug490949-iframe.html b/image/test/mochitest/bug490949-iframe.html
new file mode 100644
index 0000000000..68f74b587e
--- /dev/null
+++ b/image/test/mochitest/bug490949-iframe.html
@@ -0,0 +1,7 @@
+<html>
+<head>
+<title>Bug 490949 iframe</title>
+<body>
+<img src="bug490949.sjs" width="100" height="100" id="image" />
+</body>
+</html>
diff --git a/image/test/mochitest/bug490949.sjs b/image/test/mochitest/bug490949.sjs
new file mode 100644
index 0000000000..f306b83372
--- /dev/null
+++ b/image/test/mochitest/bug490949.sjs
@@ -0,0 +1,33 @@
+function handleRequest(request, response)
+{
+ var file = Components.classes["@mozilla.org/file/directory_service;1"]
+ .getService(Components.interfaces.nsIProperties)
+ .get("CurWorkD", Components.interfaces.nsIFile);
+
+ file.append("tests");
+ file.append("image");
+ file.append("test");
+ file.append("mochitest");
+
+ var redirectstate = "/image/test/mochitest/bug490949.sjs";
+ if (getState(redirectstate) == "") {
+ file.append('blue.png');
+ setState(redirectstate, "red");
+ } else {
+ file.append('red.png');
+ setState(redirectstate, "");
+ }
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ var fileStream = Components.classes['@mozilla.org/network/file-input-stream;1']
+ .createInstance(Components.interfaces.nsIFileInputStream);
+ fileStream.init(file, 1, 0, false);
+ var binaryStream = Components.classes['@mozilla.org/binaryinputstream;1']
+ .createInstance(Components.interfaces.nsIBinaryInputStream);
+ binaryStream.setInputStream(fileStream);
+
+ response.bodyOutputStream.writeFrom(binaryStream, binaryStream.available());
+
+ binaryStream.close();
+ fileStream.close();
+}
diff --git a/image/test/mochitest/bug496292-1.sjs b/image/test/mochitest/bug496292-1.sjs
new file mode 100644
index 0000000000..3a06b8a468
--- /dev/null
+++ b/image/test/mochitest/bug496292-1.sjs
@@ -0,0 +1,32 @@
+function handleRequest(request, response)
+{
+ var file = Components.classes["@mozilla.org/file/directory_service;1"]
+ .getService(Components.interfaces.nsIProperties)
+ .get("CurWorkD", Components.interfaces.nsIFile);
+
+ file.append("tests");
+ file.append("image");
+ file.append("test");
+ file.append("mochitest");
+
+ if (request.getHeader("Accept") == "image/webp,*/*") {
+ file.append('blue.png');
+ } else {
+ file.append('red.png');
+ }
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.setHeader("Content-Type", "image/png", false);
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ var fileStream = Components.classes['@mozilla.org/network/file-input-stream;1']
+ .createInstance(Components.interfaces.nsIFileInputStream);
+ fileStream.init(file, 1, 0, false);
+ var binaryStream = Components.classes['@mozilla.org/binaryinputstream;1']
+ .createInstance(Components.interfaces.nsIBinaryInputStream);
+ binaryStream.setInputStream(fileStream);
+
+ response.bodyOutputStream.writeFrom(binaryStream, binaryStream.available());
+
+ binaryStream.close();
+ fileStream.close();
+}
diff --git a/image/test/mochitest/bug496292-2.sjs b/image/test/mochitest/bug496292-2.sjs
new file mode 100644
index 0000000000..756ea9db2a
--- /dev/null
+++ b/image/test/mochitest/bug496292-2.sjs
@@ -0,0 +1,32 @@
+function handleRequest(request, response)
+{
+ var file = Components.classes["@mozilla.org/file/directory_service;1"]
+ .getService(Components.interfaces.nsIProperties)
+ .get("CurWorkD", Components.interfaces.nsIFile);
+
+ file.append("tests");
+ file.append("image");
+ file.append("test");
+ file.append("mochitest");
+
+ if (request.getHeader("Accept") == "image/png") {
+ file.append('blue.png');
+ } else {
+ file.append('red.png');
+ }
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.setHeader("Content-Type", "image/png", false);
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ var fileStream = Components.classes['@mozilla.org/network/file-input-stream;1']
+ .createInstance(Components.interfaces.nsIFileInputStream);
+ fileStream.init(file, 1, 0, false);
+ var binaryStream = Components.classes['@mozilla.org/binaryinputstream;1']
+ .createInstance(Components.interfaces.nsIBinaryInputStream);
+ binaryStream.setInputStream(fileStream);
+
+ response.bodyOutputStream.writeFrom(binaryStream, binaryStream.available());
+
+ binaryStream.close();
+ fileStream.close();
+}
diff --git a/image/test/mochitest/bug496292-iframe-1.html b/image/test/mochitest/bug496292-iframe-1.html
new file mode 100644
index 0000000000..00f0fbcfce
--- /dev/null
+++ b/image/test/mochitest/bug496292-iframe-1.html
@@ -0,0 +1,7 @@
+<html>
+<head>
+<title>Bug 496292 iframe 1</title>
+<body>
+<img src="bug496292-1.sjs" width="100" height="100" id="image" />
+</body>
+</html>
diff --git a/image/test/mochitest/bug496292-iframe-2.html b/image/test/mochitest/bug496292-iframe-2.html
new file mode 100644
index 0000000000..67c1ecea16
--- /dev/null
+++ b/image/test/mochitest/bug496292-iframe-2.html
@@ -0,0 +1,7 @@
+<html>
+<head>
+<title>Bug 496292 iframe 2</title>
+<body>
+<img src="bug496292-2.sjs" width="100" height="100" id="image" />
+</body>
+</html>
diff --git a/image/test/mochitest/bug496292-iframe-ref.html b/image/test/mochitest/bug496292-iframe-ref.html
new file mode 100644
index 0000000000..2e804502e6
--- /dev/null
+++ b/image/test/mochitest/bug496292-iframe-ref.html
@@ -0,0 +1,7 @@
+<html>
+<head>
+<title>Bug 496292 reference iframe</title>
+<body>
+<img src="blue.png" width="100" height="100" id="image" />
+</body>
+</html>
diff --git a/image/test/mochitest/bug497665-iframe.html b/image/test/mochitest/bug497665-iframe.html
new file mode 100644
index 0000000000..a2b098e31b
--- /dev/null
+++ b/image/test/mochitest/bug497665-iframe.html
@@ -0,0 +1,8 @@
+<html>
+<head>
+<title>Bug 497665 iframe</title>
+<body>
+<img src="bug497665.sjs" width="100" height="100" id="image1" />
+<img src="bug497665.sjs" width="100" height="100" id="image2" />
+</body>
+</html>
diff --git a/image/test/mochitest/bug497665.sjs b/image/test/mochitest/bug497665.sjs
new file mode 100644
index 0000000000..67151e25b8
--- /dev/null
+++ b/image/test/mochitest/bug497665.sjs
@@ -0,0 +1,34 @@
+function handleRequest(request, response)
+{
+ var file = Components.classes["@mozilla.org/file/directory_service;1"]
+ .getService(Components.interfaces.nsIProperties)
+ .get("CurWorkD", Components.interfaces.nsIFile);
+
+ file.append("tests");
+ file.append("image");
+ file.append("test");
+ file.append("mochitest");
+
+ var redirectstate = "/image/test/mochitest/bug497665.sjs";
+ if (getState(redirectstate) == "") {
+ file.append('blue.png');
+ setState(redirectstate, "red");
+ } else {
+ file.append('red.png');
+ setState(redirectstate, "");
+ }
+
+ response.setHeader("Cache-Control", "max-age=3600", false);
+
+ var fileStream = Components.classes['@mozilla.org/network/file-input-stream;1']
+ .createInstance(Components.interfaces.nsIFileInputStream);
+ fileStream.init(file, 1, 0, false);
+ var binaryStream = Components.classes['@mozilla.org/binaryinputstream;1']
+ .createInstance(Components.interfaces.nsIBinaryInputStream);
+ binaryStream.setInputStream(fileStream);
+
+ response.bodyOutputStream.writeFrom(binaryStream, binaryStream.available());
+
+ binaryStream.close();
+ fileStream.close();
+}
diff --git a/image/test/mochitest/bug552605.sjs b/image/test/mochitest/bug552605.sjs
new file mode 100644
index 0000000000..806a065b47
--- /dev/null
+++ b/image/test/mochitest/bug552605.sjs
@@ -0,0 +1,13 @@
+function handleRequest(request, response)
+{
+ var redirectstate = "/image/test/mochitest/bug89419.sjs";
+ response.setStatusLine("1.1", 302, "Found");
+ if (getState(redirectstate) == "") {
+ response.setHeader("Location", "red.png", false);
+ setState(redirectstate, "red");
+ } else {
+ response.setHeader("Location", "blue.png", false);
+ setState(redirectstate, "");
+ }
+ response.setHeader("Cache-Control", "no-cache", false);
+}
diff --git a/image/test/mochitest/bug657191.sjs b/image/test/mochitest/bug657191.sjs
new file mode 100644
index 0000000000..7451d98ee3
--- /dev/null
+++ b/image/test/mochitest/bug657191.sjs
@@ -0,0 +1,27 @@
+function handleRequest(request, response)
+{
+ var file = Components.classes["@mozilla.org/file/directory_service;1"]
+ .getService(Components.interfaces.nsIProperties)
+ .get("CurWorkD", Components.interfaces.nsIFile);
+
+ file.append("tests");
+ file.append("image");
+ file.append("test");
+ file.append("mochitest");
+ file.append('lime100x100.svg');
+
+ response.setStatusLine("1.1", 500, "Internal Server Error");
+ response.setHeader("Content-Type", "image/svg+xml", false);
+
+ var fileStream = Components.classes['@mozilla.org/network/file-input-stream;1']
+ .createInstance(Components.interfaces.nsIFileInputStream);
+ fileStream.init(file, 1, 0, false);
+ var binaryStream = Components.classes['@mozilla.org/binaryinputstream;1']
+ .createInstance(Components.interfaces.nsIBinaryInputStream);
+ binaryStream.setInputStream(fileStream);
+
+ response.bodyOutputStream.writeFrom(binaryStream, binaryStream.available());
+
+ binaryStream.close();
+ fileStream.close();
+}
diff --git a/image/test/mochitest/bug671906-iframe.html b/image/test/mochitest/bug671906-iframe.html
new file mode 100644
index 0000000000..87f8183a4d
--- /dev/null
+++ b/image/test/mochitest/bug671906-iframe.html
@@ -0,0 +1,7 @@
+<html>
+<head>
+<title>Bug 671906 iframe</title>
+<body>
+<img src="http://mochi.test:8888/tests/image/test/mochitest/bug671906.sjs" width="100" height="100" crossorigin="anonymous" />
+</body>
+</html>
diff --git a/image/test/mochitest/bug671906.sjs b/image/test/mochitest/bug671906.sjs
new file mode 100644
index 0000000000..f1fbc1b737
--- /dev/null
+++ b/image/test/mochitest/bug671906.sjs
@@ -0,0 +1,36 @@
+function handleRequest(request, response)
+{
+ var file = Components.classes["@mozilla.org/file/directory_service;1"]
+ .getService(Components.interfaces.nsIProperties)
+ .get("CurWorkD", Components.interfaces.nsIFile);
+
+ file.append("tests");
+ file.append("image");
+ file.append("test");
+ file.append("mochitest");
+
+ var filestate = "/image/test/mochitest/bug671906.sjs";
+ if (getState(filestate) == "") {
+ file.append('blue.png');
+ setState(filestate, "red");
+ } else {
+ file.append('red.png');
+ setState(filestate, "");
+ }
+
+ // Set the expires date to some silly time in the future so we're sure to
+ // *want* to cache this image.
+ var date = new Date();
+ date.setFullYear(date.getFullYear() + 1);
+ response.setHeader("Expires", date.toUTCString(), false);
+
+ var fileStream = Components.classes['@mozilla.org/network/file-input-stream;1']
+ .createInstance(Components.interfaces.nsIFileInputStream);
+ fileStream.init(file, 1, 0, false);
+
+ response.bodyOutputStream.writeFrom(fileStream, fileStream.available());
+
+ fileStream.close();
+
+ response.setHeader("Access-Control-Allow-Origin", "*", false);
+}
diff --git a/image/test/mochitest/bug733553-informant.sjs b/image/test/mochitest/bug733553-informant.sjs
new file mode 100644
index 0000000000..6f15a11957
--- /dev/null
+++ b/image/test/mochitest/bug733553-informant.sjs
@@ -0,0 +1,15 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function handleRequest(request, response)
+{
+ response.setHeader("Content-Type", "text/plain", false);
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ // Tells bug733553.sjs that the consumer is ready for the next part
+ let partName = request.queryString;
+ setSharedState("next-part", partName);
+ response.write("OK!");
+}
+
diff --git a/image/test/mochitest/bug733553.sjs b/image/test/mochitest/bug733553.sjs
new file mode 100644
index 0000000000..c5279f3d2d
--- /dev/null
+++ b/image/test/mochitest/bug733553.sjs
@@ -0,0 +1,104 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var bodyPartIndex = -1;
+var bodyParts = [
+ ["red.png", "image/png"],
+ ["animated-gif2.gif", "image/gif"],
+ ["red.png", "image/png"],
+ ["lime100x100.svg", "image/svg+xml"],
+ ["lime100x100.svg", "image/svg+xml"],
+ ["animated-gif2.gif", "image/gif"],
+ ["red.png", "image/png"],
+ // Mime type intentionally wrong (test for bug 907575)
+ ["shaver.png", "image/gif"],
+ ["red.png", "image/png"],
+ ["damon.jpg", "image/jpeg"],
+ ["damon.jpg", "application/octet-stream"],
+ ["damon.jpg", "image/jpeg"],
+ ["rillybad.jpg", "application/x-unknown-content-type"],
+ ["damon.jpg", "image/jpeg"],
+ ["bad.jpg", "image/jpeg"],
+ ["red.png", "image/png"],
+ ["invalid.jpg", "image/jpeg"],
+ ["animated-gif2.gif", "image/gif"]
+];
+var timer = Components.classes["@mozilla.org/timer;1"];
+var partTimer = timer.createInstance(Components.interfaces.nsITimer);
+
+function getFileAsInputStream(aFilename) {
+ var file = Components.classes["@mozilla.org/file/directory_service;1"]
+ .getService(Components.interfaces.nsIProperties)
+ .get("CurWorkD", Components.interfaces.nsIFile);
+
+ file.append("tests");
+ file.append("image");
+ file.append("test");
+ file.append("mochitest");
+ file.append(aFilename);
+
+ var fileStream = Components.classes['@mozilla.org/network/file-input-stream;1']
+ .createInstance(Components.interfaces.nsIFileInputStream);
+ fileStream.init(file, 1, 0, false);
+ return fileStream;
+}
+
+function handleRequest(request, response)
+{
+ if (!getSharedState("next-part")) {
+ setSharedState("next-part", "-1");
+ }
+ response.setHeader("Content-Type",
+ "multipart/x-mixed-replace;boundary=BOUNDARYOMG", false);
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ // We're sending parts off in a delayed fashion, to let the tests occur.
+ response.processAsync();
+ response.write("--BOUNDARYOMG\r\n");
+ sendParts(response);
+}
+
+function sendParts(response) {
+ let wait = false;
+ let nextPart = parseInt(getSharedState("next-part"), 10);
+ if (nextPart == bodyPartIndex) {
+ // Haven't been signaled yet, remain in holding pattern
+ wait = true;
+ } else {
+ bodyPartIndex = nextPart;
+ }
+ if (bodyParts.length > bodyPartIndex) {
+ let callback;
+ if (!wait) {
+ callback = getSendNextPart(response);
+ } else {
+ callback = function () { sendParts(response); };
+ }
+ partTimer.initWithCallback(callback, 1000,
+ Components.interfaces.nsITimer.TYPE_ONE_SHOT);
+ }
+ else {
+ sendClose(response);
+ }
+}
+
+function sendClose(response) {
+ response.write("--BOUNDARYOMG--\r\n");
+ response.finish();
+}
+
+function getSendNextPart(response) {
+ var part = bodyParts[bodyPartIndex];
+ var nextPartHead = "Content-Type: " + part[1] + "\r\n\r\n";
+ var inputStream = getFileAsInputStream(part[0]);
+ return function () {
+ response.bodyOutputStream.write(nextPartHead, nextPartHead.length);
+ response.bodyOutputStream.writeFrom(inputStream, inputStream.available());
+ inputStream.close();
+ // Toss in the boundary, so the browser can know this part is complete
+ response.write("--BOUNDARYOMG\r\n");
+ sendParts(response);
+ }
+}
+
diff --git a/image/test/mochitest/bug767779.sjs b/image/test/mochitest/bug767779.sjs
new file mode 100644
index 0000000000..9a7948d85d
--- /dev/null
+++ b/image/test/mochitest/bug767779.sjs
@@ -0,0 +1,56 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var timer = Components.classes["@mozilla.org/timer;1"];
+var partTimer = timer.createInstance(Components.interfaces.nsITimer);
+
+function getFileAsInputStream(aFilename) {
+ var file = Components.classes["@mozilla.org/file/directory_service;1"]
+ .getService(Components.interfaces.nsIProperties)
+ .get("CurWorkD", Components.interfaces.nsIFile);
+
+ file.append("tests");
+ file.append("image");
+ file.append("test");
+ file.append("mochitest");
+ file.append(aFilename);
+
+ var fileStream = Components.classes['@mozilla.org/network/file-input-stream;1']
+ .createInstance(Components.interfaces.nsIFileInputStream);
+ fileStream.init(file, 1, 0, false);
+ return fileStream;
+}
+
+function handleRequest(request, response)
+{
+ response.setHeader("Content-Type", "image/gif", false);
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ // We're sending data off in a delayed fashion
+ response.processAsync();
+ var inputStream = getFileAsInputStream("animated-gif_trailing-garbage.gif");
+ var available = inputStream.available(); // = 4029 bytes
+ // Send the good data at once
+ response.bodyOutputStream.writeFrom(inputStream, 285);
+ sendParts(inputStream, response);
+}
+
+function sendParts(inputStream, response) {
+ // 3744 left, send in 8 chunks of 468 each
+ partTimer.initWithCallback(getSendNextPart(inputStream, response), 500,
+ Components.interfaces.nsITimer.TYPE_ONE_SHOT);
+}
+
+function getSendNextPart(inputStream, response) {
+ return function () {
+ response.bodyOutputStream.writeFrom(inputStream, 468);
+ if (!inputStream.available()) {
+ inputStream.close();
+ response.finish();
+ } else {
+ sendParts(inputStream, response);
+ }
+ };
+}
+
diff --git a/image/test/mochitest/bug89419-iframe.html b/image/test/mochitest/bug89419-iframe.html
new file mode 100644
index 0000000000..1915315633
--- /dev/null
+++ b/image/test/mochitest/bug89419-iframe.html
@@ -0,0 +1,7 @@
+<html>
+<head>
+<title>Bug 89419 iframe</title>
+<body>
+<img src="bug89419.sjs" width="100" height="100" />
+</body>
+</html>
diff --git a/image/test/mochitest/bug89419.sjs b/image/test/mochitest/bug89419.sjs
new file mode 100644
index 0000000000..806a065b47
--- /dev/null
+++ b/image/test/mochitest/bug89419.sjs
@@ -0,0 +1,13 @@
+function handleRequest(request, response)
+{
+ var redirectstate = "/image/test/mochitest/bug89419.sjs";
+ response.setStatusLine("1.1", 302, "Found");
+ if (getState(redirectstate) == "") {
+ response.setHeader("Location", "red.png", false);
+ setState(redirectstate, "red");
+ } else {
+ response.setHeader("Location", "blue.png", false);
+ setState(redirectstate, "");
+ }
+ response.setHeader("Cache-Control", "no-cache", false);
+}
diff --git a/image/test/mochitest/bug900200-ref.png b/image/test/mochitest/bug900200-ref.png
new file mode 100644
index 0000000000..6360131325
--- /dev/null
+++ b/image/test/mochitest/bug900200-ref.png
Binary files differ
diff --git a/image/test/mochitest/bug900200.png b/image/test/mochitest/bug900200.png
new file mode 100644
index 0000000000..d7d87adce0
--- /dev/null
+++ b/image/test/mochitest/bug900200.png
Binary files differ
diff --git a/image/test/mochitest/child.html b/image/test/mochitest/child.html
new file mode 100644
index 0000000000..19f262ec3a
--- /dev/null
+++ b/image/test/mochitest/child.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<script src="imgutils.js"></script>
+<img id="img" src="animated1.gif">
+<script>
+ let observer = new ImageDecoderObserverStub();
+ observer.decodeComplete = () => {
+ parent.postMessage("decodeComplete", "*");
+ };
+ observer.loadComplete = () => {
+ parent.postMessage("loadComplete", "*");
+ };
+ observer = SpecialPowers.wrapCallbackObject(observer);
+ const gObserver = SpecialPowers.Cc["@mozilla.org/image/tools;1"]
+ .getService(SpecialPowers.Ci.imgITools)
+ .createScriptedObserver(observer);
+ const img = document.getElementById("img");
+ SpecialPowers.wrap(img).addObserver(gObserver);
+ window.addEventListener("unload", () => {
+ SpecialPowers.wrap(img).removeObserver(gObserver);
+ });
+</script>
diff --git a/image/test/mochitest/chrome.ini b/image/test/mochitest/chrome.ini
new file mode 100644
index 0000000000..b84d0af897
--- /dev/null
+++ b/image/test/mochitest/chrome.ini
@@ -0,0 +1,7 @@
+[DEFAULT]
+skip-if = os == 'android'
+
+[test_bug415761.html]
+skip-if = os != "win" || os_version == "6.2"
+support-files =
+ bug415761.ico
diff --git a/image/test/mochitest/clear.gif b/image/test/mochitest/clear.gif
new file mode 100644
index 0000000000..7ae79ba86e
--- /dev/null
+++ b/image/test/mochitest/clear.gif
Binary files differ
diff --git a/image/test/mochitest/clear.png b/image/test/mochitest/clear.png
new file mode 100644
index 0000000000..b09aecaaa0
--- /dev/null
+++ b/image/test/mochitest/clear.png
Binary files differ
diff --git a/image/test/mochitest/clear.webp b/image/test/mochitest/clear.webp
new file mode 100644
index 0000000000..6db376d6e1
--- /dev/null
+++ b/image/test/mochitest/clear.webp
Binary files differ
diff --git a/image/test/mochitest/clear2-results.gif b/image/test/mochitest/clear2-results.gif
new file mode 100644
index 0000000000..965b650253
--- /dev/null
+++ b/image/test/mochitest/clear2-results.gif
Binary files differ
diff --git a/image/test/mochitest/clear2.gif b/image/test/mochitest/clear2.gif
new file mode 100644
index 0000000000..00ad873c65
--- /dev/null
+++ b/image/test/mochitest/clear2.gif
Binary files differ
diff --git a/image/test/mochitest/clear2.webp b/image/test/mochitest/clear2.webp
new file mode 100644
index 0000000000..e4a3e2efab
--- /dev/null
+++ b/image/test/mochitest/clear2.webp
Binary files differ
diff --git a/image/test/mochitest/damon.jpg b/image/test/mochitest/damon.jpg
new file mode 100644
index 0000000000..917b336607
--- /dev/null
+++ b/image/test/mochitest/damon.jpg
Binary files differ
diff --git a/image/test/mochitest/error-early.png b/image/test/mochitest/error-early.png
new file mode 100644
index 0000000000..5df7507e2d
--- /dev/null
+++ b/image/test/mochitest/error-early.png
@@ -0,0 +1 @@
+ERROR
diff --git a/image/test/mochitest/filter-final.svg b/image/test/mochitest/filter-final.svg
new file mode 100644
index 0000000000..b2b3dca008
--- /dev/null
+++ b/image/test/mochitest/filter-final.svg
@@ -0,0 +1,9 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+
+<filter id="filter1" x="0%" y="0%" width="100%" height="100%">
+ <feImage xlink:href="animated-gif-finalframe.gif"/>
+</filter>
+<g>
+ <rect x="0" y="0" width="100%" height="100%" filter="url(#filter1)"/>
+</g>
+</svg>
diff --git a/image/test/mochitest/filter.svg b/image/test/mochitest/filter.svg
new file mode 100644
index 0000000000..e185f15b69
--- /dev/null
+++ b/image/test/mochitest/filter.svg
@@ -0,0 +1,9 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+
+<filter id="filter1" x="0%" y="0%" width="100%" height="100%">
+ <feImage xlink:href="animated-gif.gif"/>
+</filter>
+<g>
+ <rect x="0" y="0" width="100%" height="100%" filter="url(#filter1)"/>
+</g>
+</svg>
diff --git a/image/test/mochitest/finite-apng.png b/image/test/mochitest/finite-apng.png
new file mode 100644
index 0000000000..778613d851
--- /dev/null
+++ b/image/test/mochitest/finite-apng.png
Binary files differ
diff --git a/image/test/mochitest/first-frame-padding.gif b/image/test/mochitest/first-frame-padding.gif
new file mode 100644
index 0000000000..e6d7c49322
--- /dev/null
+++ b/image/test/mochitest/first-frame-padding.gif
Binary files differ
diff --git a/image/test/mochitest/green-background.html b/image/test/mochitest/green-background.html
new file mode 100644
index 0000000000..6b4518e734
--- /dev/null
+++ b/image/test/mochitest/green-background.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>Background color wrapper for clear image tests</title>
+<style>
+img {
+ background-color: rgb(0, 255, 0);
+}
+</style>
+</head>
+<body>
+<!-- non-empty alt to avoid the broken image icon -->
+<img id="image1" alt=" ">
+<script>
+// Loads an externally specified image and displays it
+// with a green background. Intended for use with tests
+// involving clear images.
+
+// Use as "green-background.html?image.png".
+
+// Get the image URL.
+var imgURL = document.location.search.substr(1);
+
+// Load it.
+var img = document.images[0];
+img.src = imgURL;
+</script>
+</body>
+</html>
diff --git a/image/test/mochitest/green.png b/image/test/mochitest/green.png
new file mode 100644
index 0000000000..7df25f33bd
--- /dev/null
+++ b/image/test/mochitest/green.png
Binary files differ
diff --git a/image/test/mochitest/grey.png b/image/test/mochitest/grey.png
new file mode 100644
index 0000000000..5c82cdeb10
--- /dev/null
+++ b/image/test/mochitest/grey.png
Binary files differ
diff --git a/image/test/mochitest/ico-bmp-opaque.ico b/image/test/mochitest/ico-bmp-opaque.ico
new file mode 100644
index 0000000000..3cf3320eae
--- /dev/null
+++ b/image/test/mochitest/ico-bmp-opaque.ico
Binary files differ
diff --git a/image/test/mochitest/ico-bmp-transparent.ico b/image/test/mochitest/ico-bmp-transparent.ico
new file mode 100644
index 0000000000..151b7cb361
--- /dev/null
+++ b/image/test/mochitest/ico-bmp-transparent.ico
Binary files differ
diff --git a/image/test/mochitest/iframe.html b/image/test/mochitest/iframe.html
new file mode 100644
index 0000000000..6d66557ef8
--- /dev/null
+++ b/image/test/mochitest/iframe.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="gray">
+ <img src="animated-gif.gif">
+</body>
+</html> \ No newline at end of file
diff --git a/image/test/mochitest/imgutils.js b/image/test/mochitest/imgutils.js
new file mode 100644
index 0000000000..b16ad1d065
--- /dev/null
+++ b/image/test/mochitest/imgutils.js
@@ -0,0 +1,137 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+// Helper file for shared image functionality
+//
+// Note that this is use by tests elsewhere in the source tree. When in doubt,
+// check mxr before removing or changing functionality.
+
+// Helper function to clear both the content and chrome image caches
+function clearAllImageCaches() {
+ var tools = SpecialPowers.Cc["@mozilla.org/image/tools;1"].getService(
+ SpecialPowers.Ci.imgITools
+ );
+ var imageCache = tools.getImgCacheForDocument(window.document);
+ imageCache.clearCache(true); // true=chrome
+ imageCache.clearCache(false); // false=content
+}
+
+// Helper function to clear the image cache of content images
+function clearImageCache() {
+ var tools = SpecialPowers.Cc["@mozilla.org/image/tools;1"].getService(
+ SpecialPowers.Ci.imgITools
+ );
+ var imageCache = tools.getImgCacheForDocument(window.document);
+ imageCache.clearCache(false); // true=chrome, false=content
+}
+
+// Helper function to determine if the frame is decoded for a given image id
+function isFrameDecoded(id) {
+ return !!(
+ getImageStatus(id) & SpecialPowers.Ci.imgIRequest.STATUS_FRAME_COMPLETE
+ );
+}
+
+// Helper function to determine if the image is loaded for a given image id
+function isImageLoaded(id) {
+ return !!(
+ getImageStatus(id) & SpecialPowers.Ci.imgIRequest.STATUS_LOAD_COMPLETE
+ );
+}
+
+// Helper function to get the status flags of an image
+function getImageStatus(id) {
+ // Get the image
+ var img = SpecialPowers.wrap(document.getElementById(id));
+
+ // Get the request
+ var request = img.getRequest(
+ SpecialPowers.Ci.nsIImageLoadingContent.CURRENT_REQUEST
+ );
+
+ // Return the status
+ return request.imageStatus;
+}
+
+// Forces a synchronous decode of an image by drawing it to a canvas. Only
+// really meaningful if the image is fully loaded first
+function forceDecode(id) {
+ // Get the image
+ var img = document.getElementById(id);
+
+ // Make a new canvas
+ var canvas = document.createElement("canvas");
+
+ // Draw the image to the canvas. This forces a synchronous decode
+ var ctx = canvas.getContext("2d");
+ ctx.drawImage(img, 0, 0);
+}
+
+// Functions to facilitate getting/setting various image-related prefs
+//
+// If you change a pref in a mochitest, Don't forget to reset it to its
+// original value!
+//
+// Null indicates no pref set
+
+const DISCARD_ENABLED_PREF = {
+ name: "discardable",
+ branch: "image.mem.",
+ type: "bool",
+};
+const DECODEONDRAW_ENABLED_PREF = {
+ name: "decodeondraw",
+ branch: "image.mem.",
+ type: "bool",
+};
+const DISCARD_TIMEOUT_PREF = {
+ name: "min_discard_timeout_ms",
+ branch: "image.mem.",
+ type: "int",
+};
+
+function setImagePref(pref, val) {
+ var prefService = SpecialPowers.Services.prefs;
+ var branch = prefService.getBranch(pref.branch);
+ if (val != null) {
+ switch (pref.type) {
+ case "bool":
+ branch.setBoolPref(pref.name, val);
+ break;
+ case "int":
+ branch.setIntPref(pref.name, val);
+ break;
+ default:
+ throw new Error("Unknown pref type");
+ }
+ } else if (branch.prefHasUserValue(pref.name)) {
+ branch.clearUserPref(pref.name);
+ }
+}
+
+function getImagePref(pref) {
+ var prefService = SpecialPowers.Services.prefs;
+ var branch = prefService.getBranch(pref.branch);
+ if (branch.prefHasUserValue(pref.name)) {
+ switch (pref.type) {
+ case "bool":
+ return branch.getBoolPref(pref.name);
+ case "int":
+ return branch.getIntPref(pref.name);
+ default:
+ throw new Error("Unknown pref type");
+ }
+ } else {
+ return null;
+ }
+}
+
+// JS implementation of imgIScriptedNotificationObserver with stubs for all of its methods.
+function ImageDecoderObserverStub() {
+ this.sizeAvailable = function sizeAvailable(aRequest) {};
+ this.frameComplete = function frameComplete(aRequest) {};
+ this.decodeComplete = function decodeComplete(aRequest) {};
+ this.loadComplete = function loadComplete(aRequest) {};
+ this.frameUpdate = function frameUpdate(aRequest) {};
+ this.discard = function discard(aRequest) {};
+ this.isAnimated = function isAnimated(aRequest) {};
+ this.hasTransparency = function hasTransparency(aRequest) {};
+}
diff --git a/image/test/mochitest/infinite-apng.png b/image/test/mochitest/infinite-apng.png
new file mode 100644
index 0000000000..637dafbc2b
--- /dev/null
+++ b/image/test/mochitest/infinite-apng.png
Binary files differ
diff --git a/image/test/mochitest/infinite.webp b/image/test/mochitest/infinite.webp
new file mode 100644
index 0000000000..4219e179b0
--- /dev/null
+++ b/image/test/mochitest/infinite.webp
Binary files differ
diff --git a/image/test/mochitest/invalid.jpg b/image/test/mochitest/invalid.jpg
new file mode 100644
index 0000000000..c677a81e29
--- /dev/null
+++ b/image/test/mochitest/invalid.jpg
@@ -0,0 +1 @@
+notajpg
diff --git a/image/test/mochitest/keep.gif b/image/test/mochitest/keep.gif
new file mode 100644
index 0000000000..e967d6a6dc
--- /dev/null
+++ b/image/test/mochitest/keep.gif
Binary files differ
diff --git a/image/test/mochitest/keep.png b/image/test/mochitest/keep.png
new file mode 100644
index 0000000000..aa3ff74450
--- /dev/null
+++ b/image/test/mochitest/keep.png
Binary files differ
diff --git a/image/test/mochitest/keep.webp b/image/test/mochitest/keep.webp
new file mode 100644
index 0000000000..342982be06
--- /dev/null
+++ b/image/test/mochitest/keep.webp
Binary files differ
diff --git a/image/test/mochitest/lime-anim-100x100-2.svg b/image/test/mochitest/lime-anim-100x100-2.svg
new file mode 100644
index 0000000000..d19d3b0e7e
--- /dev/null
+++ b/image/test/mochitest/lime-anim-100x100-2.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
+ width="100" height="100">
+ <rect width="100%" height="100%" fill="red">
+ <animate attributeName="fill" to="lime" dur="0.1" fill="freeze"/>
+ </rect>
+</svg>
diff --git a/image/test/mochitest/lime-anim-100x100.svg b/image/test/mochitest/lime-anim-100x100.svg
new file mode 100644
index 0000000000..c6584047d0
--- /dev/null
+++ b/image/test/mochitest/lime-anim-100x100.svg
@@ -0,0 +1,7 @@
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
+ width="100" height="100">
+ <rect width="100%" height="100%" fill="red"/>
+ <rect x="-600" width="100%" height="100%" fill="lime">
+ <animate attributeName="x" by="600" dur="0.1" fill="freeze"/>
+ </rect>
+</svg>
diff --git a/image/test/mochitest/lime-css-anim-100x100.svg b/image/test/mochitest/lime-css-anim-100x100.svg
new file mode 100644
index 0000000000..3edbd3eaaf
--- /dev/null
+++ b/image/test/mochitest/lime-css-anim-100x100.svg
@@ -0,0 +1,19 @@
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
+ width="100" height="100">
+ <defs>
+ <style>
+ #myRect {
+ animation-duration: 0.1s;
+ animation-name: fade;
+ animation-fill-mode: forwards;
+ }
+
+ @keyframes fade {
+ 0% { fill-opacity: 0 }
+ 100% { fill-opacity: 1 }
+ }
+ </style>
+ </defs>
+ <rect width="100%" height="100%" fill="red"/>
+ <rect id="myRect" width="100%" height="100%" fill="lime" fill-opacity="0"/>
+</svg>
diff --git a/image/test/mochitest/lime100x100.svg b/image/test/mochitest/lime100x100.svg
new file mode 100644
index 0000000000..8bdec62c1f
--- /dev/null
+++ b/image/test/mochitest/lime100x100.svg
@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
+ width="100" height="100">
+ <rect width="100%" height="100%" fill="lime"/>
+</svg>
diff --git a/image/test/mochitest/mochitest.ini b/image/test/mochitest/mochitest.ini
new file mode 100644
index 0000000000..ad9e18a119
--- /dev/null
+++ b/image/test/mochitest/mochitest.ini
@@ -0,0 +1,162 @@
+[DEFAULT]
+support-files =
+ INT32_MIN.bmp
+ animated1.gif
+ animated2.gif
+ animated-gif.gif
+ animated-gif2.gif
+ animated-gif_trailing-garbage.gif
+ animated-gif-finalframe.gif
+ animation.svg
+ animationPolling.js
+ bad.jpg
+ big.png
+ blue.gif
+ blue.png
+ bug399925.gif
+ bug468160.sjs
+ bug478398_ONLY.png
+ bug490949-iframe.html
+ bug490949.sjs
+ bug496292-1.sjs
+ bug496292-2.sjs
+ bug496292-iframe-1.html
+ bug496292-iframe-2.html
+ bug496292-iframe-ref.html
+ bug497665-iframe.html
+ bug497665.sjs
+ bug552605.sjs
+ bug657191.sjs
+ bug671906-iframe.html
+ bug671906.sjs
+ bug733553-informant.sjs
+ bug733553.sjs
+ bug767779.sjs
+ bug89419-iframe.html
+ bug89419.sjs
+ bug900200.png
+ bug900200-ref.png
+ bug1132427.html
+ bug1132427.gif
+ bug1180105.sjs
+ bug1180105-waiter.sjs
+ bug1217571-iframe.html
+ bug1217571.jpg
+ bug1319025.png
+ bug1319025-ref.png
+ clear.gif
+ clear.png
+ clear.webp
+ clear2.gif
+ clear2.webp
+ clear2-results.gif
+ damon.jpg
+ error-early.png
+ filter-final.svg
+ filter.svg
+ finite-apng.png
+ first-frame-padding.gif
+ green.png
+ green-background.html
+ grey.png
+ ico-bmp-opaque.ico
+ ico-bmp-transparent.ico
+ iframe.html
+ imgutils.js
+ infinite.webp
+ infinite-apng.png
+ invalid.jpg
+ keep.gif
+ keep.png
+ keep.webp
+ lime100x100.svg
+ lime-anim-100x100.svg
+ lime-anim-100x100-2.svg
+ lime-css-anim-100x100.svg
+ mq_dynamic_svg_test.html
+ mq_dynamic_svg_ref.html
+ opaque.bmp
+ purple.gif
+ rainbow.gif
+ red.gif
+ red.png
+ ref-iframe.html
+ restore-previous.gif
+ restore-previous.png
+ rillybad.jpg
+ schrep.png
+ shaver.png
+ short_header.gif
+ source.png
+ transparent.gif
+ transparent.png
+ over.png
+ webcam-simulacrum.sjs
+ 6M-pixels.png
+ 12M-pixels-1.png
+ 12M-pixels-2.png
+
+[test_animated_css_image.html]
+[test_animated_gif.html]
+support-files = child.html
+[test_animation.html]
+[test_canvas_frame_animation.html]
+[test_animation_operators.html]
+[test_animation2.html]
+[test_animSVGImage.html]
+skip-if = os == 'android' || os == 'win' || (os == 'mac' && os_version == '10.14') # Bug 1370784, macosx due to bug 1549058
+[test_animSVGImage2.html]
+skip-if = (webrender && os == 'win') || (webrender && os == 'linux') #Bug 1354561
+[test_background_image_anim.html]
+[test_bug399925.html]
+[test_bug435296.html]
+skip-if = true # disabled - See bug 578591
+[test_bug466586.html]
+[test_bug468160.html]
+[test_bug478398.html]
+skip-if = true # disabled - See bug 579139
+[test_bug490949.html]
+[test_bug496292.html]
+skip-if = verify
+[test_bug497665.html]
+[test_bug552605-1.html]
+[test_bug552605-2.html]
+[test_bug553982.html]
+[test_bug601470.html]
+[test_bug614392.html]
+[test_bug657191.html]
+[test_bug671906.html]
+[test_bug733553.html]
+skip-if = verify
+[test_bug767779.html]
+[test_bug865919.html]
+[test_bug89419-1.html]
+[test_bug89419-2.html]
+[test_bug1132427.html]
+[test_bug1180105.html]
+[test_bug1217571.html]
+[test_bug1325080.html]
+[test_bullet_animation.html]
+[test_changeOfSource.html]
+[test_changeOfSource2.html]
+[test_discardAnimatedImage.html]
+[test_discardFinishedAnimatedImage.html]
+[test_discardFramesAnimatedImage.html]
+[test_drawDiscardedImage.html]
+[test_error_events.html]
+[test_image_crossorigin_data_url.html]
+[test_has_transparency.html]
+[test_mq_dynamic_svg.html]
+[test_net_failedtoprocess.html]
+skip-if = verify
+[test_removal_ondecode.html]
+[test_removal_onload.html]
+[test_short_gif_header.html]
+[test_staticClone.html]
+[test_svg_animatedGIF.html]
+[test_svg_filter_animation.html]
+[test_synchronized_animation.html]
+disabled = bug 1295501
+[test_undisplayed_iframe.html]
+[test_webcam.html]
+[test_xultree_animation.xhtml]
diff --git a/image/test/mochitest/mq_dynamic_svg_ref.html b/image/test/mochitest/mq_dynamic_svg_ref.html
new file mode 100644
index 0000000000..bbd4a3e205
--- /dev/null
+++ b/image/test/mochitest/mq_dynamic_svg_ref.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+<meta charset=utf-8>
+<style>
+iframe { border: none; }
+</style>
+<p>inline</p>
+<svg width="50" height="50">
+ <rect width="50" height="50" fill="green"/>
+</svg>
+<p>iframe</p>
+<iframe src='data:image/svg+xml,
+ <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50">
+ <rect width="50" height="50" fill="green"/>
+ </svg>
+' width=50 height=50></iframe>
+<p>img</p>
+<img src='data:image/svg+xml,
+ <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50">
+ <rect width="50" height="50" fill="green"/>
+ </svg>
+' width=50 height=50>
+<p>background-image</p>
+<div style='background-image: url("data:image/svg+xml,\
+ <svg xmlns=&apos;http://www.w3.org/2000/svg&apos; width=&apos;50&apos; height=&apos;50&apos;>\
+ <rect width=&apos;50&apos; height=&apos;50&apos; fill=&apos;green&apos;/>\
+ </svg>\
+"); width: 50px; height: 50px;'></div>
+<p>img with nested document</p>
+<img src='data:image/svg+xml,
+ <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50">
+ <image href="data:image/svg+xml,
+ &amp;lt;svg xmlns=&amp;quot;http://www.w3.org/2000/svg&amp;quot; width=&amp;quot;50&amp;quot; height=&amp;quot;50&amp;quot;>
+ &amp;lt;rect width=&amp;quot;50&amp;quot; height=&amp;quot;50&amp;quot; fill=&amp;quot;green&amp;quot;/>
+ &amp;lt;/svg>
+ " width="50" height="50"/>
+ </svg>
+' width=50 height=50>
diff --git a/image/test/mochitest/mq_dynamic_svg_test.html b/image/test/mochitest/mq_dynamic_svg_test.html
new file mode 100644
index 0000000000..8acb6d3331
--- /dev/null
+++ b/image/test/mochitest/mq_dynamic_svg_test.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+<meta charset=utf-8>
+<style>
+@media (prefers-color-scheme: dark) {
+ rect { fill: green; }
+}
+iframe { border: none; }
+</style>
+<p>inline</p>
+<svg width="50" height="50">
+ <rect width="50" height="50" fill="red"/>
+</svg>
+<p>iframe</p>
+<iframe src='data:image/svg+xml,
+ <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50">
+ <style>
+ @media (prefers-color-scheme: dark) {
+ rect { fill: green; }
+ }
+ </style>
+ <rect width="50" height="50" fill="red"/>
+ </svg>
+' width=50 height=50></iframe>
+<p>img</p>
+<img src='data:image/svg+xml,
+ <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50">
+ <style>
+ @media (prefers-color-scheme: dark) {
+ rect { fill: green; }
+ }
+ </style>
+ <rect width="50" height="50" fill="red"/>
+ </svg>
+' width=50 height=50>
+<p>background-image</p>
+<div style='background-image: url("data:image/svg+xml,\
+ <svg xmlns=&apos;http://www.w3.org/2000/svg&apos; width=&apos;50&apos; height=&apos;50&apos;>\
+ <style>\
+ @media (prefers-color-scheme: dark) {\
+ rect { fill: green; }\
+ }\
+ </style>\
+ <rect width=&apos;50&apos; height=&apos;50&apos; fill=&apos;red&apos;/>\
+ </svg>\
+"); width: 50px; height: 50px;'></div>
+<p>img with nested document</p>
+<img src='data:image/svg+xml,
+ <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50">
+ <image href="data:image/svg+xml,
+ &amp;lt;svg xmlns=&amp;quot;http://www.w3.org/2000/svg&amp;quot; width=&amp;quot;50&amp;quot; height=&amp;quot;50&amp;quot;>
+ &amp;lt;style>
+ @media (prefers-color-scheme: dark) {
+ rect { fill: green; }
+ }
+ &amp;lt;/style>
+ &amp;lt;rect width=&amp;quot;50&amp;quot; height=&amp;quot;50&amp;quot; fill=&amp;quot;red&amp;quot;/>
+ &amp;lt;/svg>
+ " width="50" height="50"/>
+ </svg>
+' width=50 height=50>
diff --git a/image/test/mochitest/opaque.bmp b/image/test/mochitest/opaque.bmp
new file mode 100644
index 0000000000..63d3f1c058
--- /dev/null
+++ b/image/test/mochitest/opaque.bmp
Binary files differ
diff --git a/image/test/mochitest/over.png b/image/test/mochitest/over.png
new file mode 100644
index 0000000000..9e957182f7
--- /dev/null
+++ b/image/test/mochitest/over.png
Binary files differ
diff --git a/image/test/mochitest/purple.gif b/image/test/mochitest/purple.gif
new file mode 100644
index 0000000000..79826af205
--- /dev/null
+++ b/image/test/mochitest/purple.gif
Binary files differ
diff --git a/image/test/mochitest/rainbow.gif b/image/test/mochitest/rainbow.gif
new file mode 100644
index 0000000000..a247a80df0
--- /dev/null
+++ b/image/test/mochitest/rainbow.gif
Binary files differ
diff --git a/image/test/mochitest/red.gif b/image/test/mochitest/red.gif
new file mode 100644
index 0000000000..d3c32bae25
--- /dev/null
+++ b/image/test/mochitest/red.gif
Binary files differ
diff --git a/image/test/mochitest/red.png b/image/test/mochitest/red.png
new file mode 100644
index 0000000000..aa9ce25263
--- /dev/null
+++ b/image/test/mochitest/red.png
Binary files differ
diff --git a/image/test/mochitest/ref-iframe.html b/image/test/mochitest/ref-iframe.html
new file mode 100644
index 0000000000..585772c8a9
--- /dev/null
+++ b/image/test/mochitest/ref-iframe.html
@@ -0,0 +1,6 @@
+<html>
+<body bgcolor="gray">
+ <div id="referenceImage"
+ style="height: 40px; width: 40px; background: #2aff00"></div>
+</body>
+</html>
diff --git a/image/test/mochitest/restore-previous.gif b/image/test/mochitest/restore-previous.gif
new file mode 100644
index 0000000000..15ba9ddc48
--- /dev/null
+++ b/image/test/mochitest/restore-previous.gif
Binary files differ
diff --git a/image/test/mochitest/restore-previous.png b/image/test/mochitest/restore-previous.png
new file mode 100644
index 0000000000..09dee63820
--- /dev/null
+++ b/image/test/mochitest/restore-previous.png
Binary files differ
diff --git a/image/test/mochitest/rillybad.jpg b/image/test/mochitest/rillybad.jpg
new file mode 100644
index 0000000000..e2fb1d303f
--- /dev/null
+++ b/image/test/mochitest/rillybad.jpg
Binary files differ
diff --git a/image/test/mochitest/schrep.png b/image/test/mochitest/schrep.png
new file mode 100644
index 0000000000..bcb406387d
--- /dev/null
+++ b/image/test/mochitest/schrep.png
Binary files differ
diff --git a/image/test/mochitest/shaver.png b/image/test/mochitest/shaver.png
new file mode 100644
index 0000000000..ab0b6c7b40
--- /dev/null
+++ b/image/test/mochitest/shaver.png
Binary files differ
diff --git a/image/test/mochitest/short_header.gif b/image/test/mochitest/short_header.gif
new file mode 100644
index 0000000000..70af95ac6d
--- /dev/null
+++ b/image/test/mochitest/short_header.gif
Binary files differ
diff --git a/image/test/mochitest/source.png b/image/test/mochitest/source.png
new file mode 100644
index 0000000000..df1c76dae5
--- /dev/null
+++ b/image/test/mochitest/source.png
Binary files differ
diff --git a/image/test/mochitest/test_animSVGImage.html b/image/test/mochitest/test_animSVGImage.html
new file mode 100644
index 0000000000..a405cdd46b
--- /dev/null
+++ b/image/test/mochitest/test_animSVGImage.html
@@ -0,0 +1,124 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=610419
+-->
+<head>
+ <title>Test for Bug 610419</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <script type="application/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=610419">Mozilla Bug 610419</a>
+<p id="display"></p>
+<div id="content">
+ <div id="referenceDiv" style="height: 100px; width: 100px;
+ display: none; background: lime"></div>
+ <img>
+</div>
+<pre id="test">
+<script type="application/javascript">
+/** Test for Bug 610419**/
+
+SimpleTest.requestFlakyTimeout("Pre-existing timeouts when converting from mochitest-chrome");
+SimpleTest.requestCompleteLog();
+SimpleTest.waitForExplicitFinish();
+
+const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
+
+const gImg = document.getElementsByTagName("img")[0];
+
+var gMyDecoderObserver; // value will be set in main()
+var gReferenceSnapshot; // value will be set in takeReferenceSnapshot()
+var gPollCounter = 0;
+var gIsTestFinished = false;
+var gSVGImages = [
+ "lime-anim-100x100.svg", // SMIL animation
+ "lime-css-anim-100x100.svg" // CSS animation
+]
+var gSVGCurrentImage = 0;
+
+function takeReferenceSnapshot() {
+ // Take a snapshot of the initial (essentially blank) page
+ let blankSnapshot = snapshotWindow(window, false);
+
+ // Show reference div, & take a snapshot
+ let referenceDiv = document.getElementById("referenceDiv");
+ referenceDiv.style.display = "block";
+ gReferenceSnapshot = snapshotWindow(window, false);
+ ok(compareSnapshots(blankSnapshot, gReferenceSnapshot, false)[0],
+ "reference snapshot shouldn't match blank page snapshot");
+
+ // Re-hide reference div, and take another snapshot to be sure it's gone
+ referenceDiv.style.display = "none";
+ let blankSnapshot2 = snapshotWindow(window, false);
+ ok(compareSnapshots(blankSnapshot, blankSnapshot2, true)[0],
+ "reference div should disappear when it becomes display:none");
+}
+
+function loadNextImageAndPoll()
+{
+ setTimeout(myPoll, 1);
+ // kick off image-loading! myPoll handles the rest.
+ gImg.setAttribute("src", gSVGImages[gSVGCurrentImage]);
+}
+
+function myPoll() {
+ gPollCounter++;
+ ok(true, "myPoll called");
+ let currentSnapshot = snapshotWindow(window, false);
+ if (compareSnapshots(currentSnapshot, gReferenceSnapshot, true)[0]) {
+ // SUCCESS!
+ ok(true, "Animated image looks correct, " +
+ "at call #" + gPollCounter + " to myPoll");
+
+ if (++gSVGCurrentImage > gSVGImages.length) {
+ cleanUpAndFinish();
+ } else {
+ loadNextImageAndPoll();
+ }
+ }
+ else {
+ setTimeout(myPoll, 20);
+ }
+}
+
+function failTest() {
+ ok(false, "timing out after " + FAILURE_TIMEOUT + "ms. " +
+ "Animated image still doesn't look correct, " +
+ "after call #" + gPollCounter + " to myPoll");
+ cleanUpAndFinish();
+}
+
+function cleanUpAndFinish() {
+ // On the off chance that failTest and myPoll are triggered
+ // back-to-back, use a flag to prevent multiple calls to SimpleTest.finish.
+ if (gIsTestFinished) {
+ return;
+ }
+ SimpleTest.finish();
+ gIsTestFinished = true;
+}
+
+function main() {
+ takeReferenceSnapshot();
+
+ // We want to test the cold loading behavior, so clear cache in case an
+ // earlier test got our image in there already.
+ clearAllImageCaches();
+
+ loadNextImageAndPoll();
+
+ // In case something goes wrong, fail earlier than mochitest timeout,
+ // and with more information.
+ setTimeout(failTest, FAILURE_TIMEOUT);
+}
+
+window.onload = main;
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_animSVGImage2.html b/image/test/mochitest/test_animSVGImage2.html
new file mode 100644
index 0000000000..0f3ae046c5
--- /dev/null
+++ b/image/test/mochitest/test_animSVGImage2.html
@@ -0,0 +1,124 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=907503
+-->
+<head>
+ <title>Test for Bug 907503</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <script type="application/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=907503">Mozilla Bug 907503</a>
+<p id="display"></p>
+<div id="content">
+ <div id="referenceDiv" style="height: 100px; width: 100px;
+ display: none; background: lime"></div>
+ <img>
+</div>
+<pre id="test">
+<script type="application/javascript">
+/** Test for Bug 907503**/
+
+SimpleTest.requestFlakyTimeout("Early failure timeout");
+SimpleTest.waitForExplicitFinish();
+
+const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
+
+const Cc = SpecialPowers.Cc;
+const Ci = SpecialPowers.Ci;
+const gImg = document.getElementsByTagName("img")[0];
+
+var gMyDecoderObserver; // value will be set in main()
+var gReferenceSnapshot; // value will be set in takeReferenceSnapshot()
+var gOnFrameUpdateCounter = 0;
+var gIsTestFinished = false;
+
+
+function takeReferenceSnapshot() {
+ // Take a snapshot of the initial (essentially blank) page
+ let blankSnapshot = snapshotWindow(window, false);
+
+ // Show reference div, & take a snapshot
+ let referenceDiv = document.getElementById("referenceDiv");
+ referenceDiv.style.display = "block";
+ gReferenceSnapshot = snapshotWindow(window, false);
+ ok(compareSnapshots(blankSnapshot, gReferenceSnapshot, false)[0],
+ "reference snapshot shouldn't match blank page snapshot");
+
+ // Re-hide reference div, and take another snapshot to be sure it's gone
+ referenceDiv.style.display = "none";
+ let blankSnapshot2 = snapshotWindow(window, false);
+ ok(compareSnapshots(blankSnapshot, blankSnapshot2, true)[0],
+ "reference div should disappear when it becomes display:none");
+}
+
+function myOnFrameUpdate(aRequest) {
+ if (gIsTestFinished) {
+ return;
+ }
+ gOnFrameUpdateCounter++;
+ ok(true, "myOnFrameUpdate called");
+ let currentSnapshot = snapshotWindow(window, false);
+ if (compareSnapshots(currentSnapshot, gReferenceSnapshot, true)[0]) {
+ // SUCCESS!
+ ok(true, "Animated image looks correct, " +
+ "at call #" + gOnFrameUpdateCounter + " to myOnFrameUpdate");
+ cleanUpAndFinish();
+ }
+}
+
+function failTest() {
+ if (gIsTestFinished) {
+ return;
+ }
+ ok(false, "timing out after " + FAILURE_TIMEOUT + "ms. " +
+ "Animated image still doesn't look correct, " +
+ "after call #" + gOnFrameUpdateCounter + " to myOnFrameUpdate");
+ cleanUpAndFinish();
+}
+
+function cleanUpAndFinish() {
+ // On the off chance that failTest and myOnFrameUpdate are triggered
+ // back-to-back, use a flag to prevent multiple calls to SimpleTest.finish.
+ if (gIsTestFinished) {
+ return;
+ }
+ let imgLoadingContent = SpecialPowers.wrap(gImg);
+ imgLoadingContent.removeObserver(gMyDecoderObserver);
+ SimpleTest.finish();
+ gIsTestFinished = true;
+}
+
+function main() {
+ takeReferenceSnapshot();
+
+ // Create, customize & attach decoder observer
+ var observer = new ImageDecoderObserverStub();
+ observer.frameUpdate = myOnFrameUpdate;
+ gMyDecoderObserver =
+ Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
+ .createScriptedObserver(SpecialPowers.wrapCallbackObject(observer));
+ let imgLoadingContent = SpecialPowers.wrap(gImg);
+ imgLoadingContent.addObserver(gMyDecoderObserver);
+
+ // We want to test the cold loading behavior, so clear cache in case an
+ // earlier test got our image in there already.
+ clearAllImageCaches();
+
+ // kick off image-loading! myOnFrameUpdate handles the rest.
+ gImg.setAttribute("src", "lime-anim-100x100-2.svg");
+
+ // In case something goes wrong, fail earlier than mochitest timeout,
+ // and with more information.
+ setTimeout(failTest, FAILURE_TIMEOUT);
+}
+
+window.onload = main;
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_animated_css_image.html b/image/test/mochitest/test_animated_css_image.html
new file mode 100644
index 0000000000..c9c77ff444
--- /dev/null
+++ b/image/test/mochitest/test_animated_css_image.html
@@ -0,0 +1,165 @@
+<!doctype html>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+<!--
+ scrolling=no is just paranoia to ensure that we don't get invalidations
+ due to scrollbars
+-->
+<iframe scrolling="no" id="iframe"></iframe>
+<script>
+SimpleTest.waitForExplicitFinish();
+
+// We hit an optimized path in WebRender that doesn't cause a repaint on the
+// main thread:
+//
+// https://searchfox.org/mozilla-central/rev/b7f3977978922d44c7d92ae01c0d4cc2baca7bc2/layout/style/ImageLoader.cpp#553
+//
+// So our assertions and polling need to be a bit weaker on WR.
+const kUsingWebRender = SpecialPowers.DOMWindowUtils.layerManagerType === "WebRender";
+
+let iframe = document.getElementById("iframe");
+let blankSnapshot;
+
+async function assertAnimates(html, getExpectedRepaintedElement) {
+ const kExpectEqual = true;
+ const kNumRetries = kUsingWebRender ? 600 : 30;
+
+ info("testing: " + html);
+
+ {
+ let load = new Promise(resolve => {
+ iframe.addEventListener("load", resolve, { once: true });
+ });
+ iframe.srcdoc = html;
+ await load;
+ }
+
+ // This ensures the MozAfterPaint events come through as expected.
+ await SimpleTest.promiseFocus(iframe.contentWindow);
+
+ let initial = await snapshotWindow(iframe.contentWindow);
+
+ let repaintedElement = getExpectedRepaintedElement(iframe.contentDocument);
+ if (!kUsingWebRender) {
+ // Ensure the painted state is clear before we start polling.
+ SpecialPowers.DOMWindowUtils.checkAndClearPaintedState(repaintedElement);
+ }
+
+ {
+ let [equal, s1 /* , s2, differentPixels, maxDiff */] = compareSnapshots(initial, blankSnapshot, kExpectEqual);
+ ok(!equal, "Initial snapshot shouldn't be blank");
+ info(s1);
+ }
+
+ let foundDifferent = false;
+ let foundInitialAgain = false;
+ for (let i = 0; i < kNumRetries; ++i) {
+ let current = await snapshotWindow(iframe.contentWindow);
+ let [equal, /* s1 */, s2 /* , differentPixels, maxDiff */ ] = compareSnapshots(initial, current, kExpectEqual);
+ if (!foundDifferent && !equal) {
+ ok(true, `Found different image after ${i} retries`);
+ ok(kUsingWebRender || SpecialPowers.DOMWindowUtils.checkAndClearPaintedState(repaintedElement), "Should've repainted the expected element");
+ info(s2);
+ 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`);
+ ok(kUsingWebRender || SpecialPowers.DOMWindowUtils.checkAndClearPaintedState(repaintedElement), "Should've repainted the expected element");
+ foundInitialAgain = true;
+ break;
+ }
+
+ await new Promise(resolve => {
+ if (kUsingWebRender) {
+ requestAnimationFrame(() => {
+ requestAnimationFrame(resolve);
+ });
+ } else {
+ iframe.contentWindow.addEventListener("MozAfterPaint", resolve, { once: true });
+ }
+ });
+ }
+
+ ok(foundDifferent && foundInitialAgain, `Should've found a different snapshot and then an equal one, after ${kNumRetries} retries`);
+}
+
+const kTests = [
+ // Sanity test: background-image on a regular element.
+ {
+ html: `
+ <!doctype html>
+ <style>
+ div {
+ width: 100px;
+ height: 100px;
+ background-image: url(animated1.gif);
+ }
+ </style>
+ <div></div>
+ `,
+ element(doc) {
+ return doc.querySelector("div");
+ },
+ },
+
+ // bug 1627585: content: url()
+ {
+ html: `
+ <!doctype html>
+ <style>
+ div::before {
+ content: url(animated1.gif);
+ }
+ </style>
+ <div></div>
+ `,
+ element(doc) {
+ return doc.querySelector("div");
+ },
+ },
+
+ // bug 1627585: content: url() (on an element directly)
+ {
+ html: `
+ <!doctype html>
+ <style>
+ div {
+ content: url(animated1.gif);
+ }
+ </style>
+ <div></div>
+ `,
+ element(doc) {
+ return doc.querySelector("div");
+ },
+ },
+
+ // bug 1625571: background propagated to canvas.
+ {
+ html: `
+ <!doctype html>
+ <style>
+ body {
+ background-image: url(animated1.gif);
+ }
+ </style>
+ `,
+ element(doc) {
+ return doc.documentElement;
+ },
+ }
+];
+
+onload = async function() {
+ // First snapshot the blank window.
+ blankSnapshot = await snapshotWindow(iframe.contentWindow);
+
+ for (let { html, element } of kTests)
+ await assertAnimates(html, element);
+
+ SimpleTest.finish();
+}
+</script>
diff --git a/image/test/mochitest/test_animated_gif.html b/image/test/mochitest/test_animated_gif.html
new file mode 100644
index 0000000000..814749b7f0
--- /dev/null
+++ b/image/test/mochitest/test_animated_gif.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Images outside of display port are not decoded</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="imgutils.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<div id="scroller" style="height: 300px; overflow: scroll;">
+ <div style="width: 100%; height: 12000px;"></div>
+ <iframe id="iframe"></iframe>
+ <div style="width: 100%; height: 5000px;"></div>
+</div>
+<pre id="test"></pre>
+<script>
+ add_task(async () => {
+ window.addEventListener("message", event => {
+ isnot(event.data, "decodeComplete",
+ "decodeComplete should never be received");
+ });
+
+ await new Promise(resolve => {
+ window.addEventListener("message", event => {
+ if (event.data == "loadComplete") {
+ ok(true, "Got loadComplete");
+ resolve();
+ }
+ }, { once: true });
+
+ const iframe = document.getElementById("iframe");
+ iframe.src = "http://example.org/tests/image/test/mochitest/child.html";
+ });
+
+ const start = Date.now();
+
+ // Waits a second;
+ await SimpleTest.promiseWaitForCondition(() => {
+ return 1000 < (Date.now() - start);
+ });
+
+ ok(true, "decodeComplete didn't receive within a second");
+ });
+
+</script>
+</body>
+</html>
diff --git a/image/test/mochitest/test_animation.html b/image/test/mochitest/test_animation.html
new file mode 100644
index 0000000000..725cd93e85
--- /dev/null
+++ b/image/test/mochitest/test_animation.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=666446
+-->
+<head>
+ <title>Test for Bug 666446 - General Animated GIF Test</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <script type="application/javascript" src="imgutils.js"></script>
+ <script type="application/javascript" src="animationPolling.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=666446">
+Mozilla Bug 666446: lots of animated gifs swamp us with paint events
+</a>
+<p id="display"></p>
+
+<div id="content">
+ <div id="referenceDiv" style="height: 40px; width: 40px;
+ display: none; background: #2aff00"></div>
+ <div id="animatedImage">
+ <img id="animatedGif" src="animated-gif.gif" style="display: none;">
+ <div id="text-descr"></div>
+ </div>
+ <div id="debug" style="display:none">
+ </div>
+</div>
+<pre id="test">
+<script type="text/javascript">
+const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
+
+function main()
+{
+ var animTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceDiv',
+ 'animatedGif', 'debug');
+ animTest.beginTest();
+}
+
+window.onload = main;
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_animation2.html b/image/test/mochitest/test_animation2.html
new file mode 100644
index 0000000000..1b4e0f94c7
--- /dev/null
+++ b/image/test/mochitest/test_animation2.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=705580
+-->
+<head>
+ <title>Test for Bug 705580 - General Animated GIF Test 2</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <script type="application/javascript" src="imgutils.js"></script>
+ <script type="application/javascript" src="animationPolling.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=705580">
+Mozilla Bug 705580: Test animated GIFs that are converted to ImageLayers
+</a>
+<p id="display"></p>
+
+<div id="content">
+ <!--
+ Use will-change: opacity to force a ContainerLayer, and the img as the sole item in the PaintedLayer. It should then
+ be promoted to an ImageLayer.
+ -->
+ <div id="referenceDiv" style="height: 40px; width: 40px;
+ display: none; background: #2aff00;"></div>
+ <div id="animatedImage" style="will-change: opacity;">
+ <img id="animatedGif" src="animated-gif.gif" style="display: none;">
+ <div id="text-descr"></div>
+ </div>
+ <div id="debug" style="display:none">
+ </div>
+</div>
+<pre id="test">
+<script type="text/javascript">
+const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
+
+function main()
+{
+ var animTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceDiv',
+ 'animatedGif', 'debug');
+ animTest.beginTest();
+}
+
+window.onload = main;
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_animation_operators.html b/image/test/mochitest/test_animation_operators.html
new file mode 100644
index 0000000000..31f1108362
--- /dev/null
+++ b/image/test/mochitest/test_animation_operators.html
@@ -0,0 +1,167 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=936720
+-->
+<head>
+ <title>Test for Bug 936720</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.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=936720">Mozilla Bug 936720</a>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 936720 **/
+
+// Because there is no event telling us when an animated image finishes
+// animating, tests for the operators used by animated GIFs and PNGs
+// require that we poll until we get the correct result. A fixed timeout
+// can easily result in intermittent failures on tests running in VMs.
+
+// (Note that we do _not_ poll the reference, so it must not be animated.)
+
+var gTests = [
+ // IMPORTANT NOTE: For these tests, the test and reference are not
+ // snapshotted in the same way. The REFERENCE (second file) is
+ // assumed to be complete when loaded, but we poll the TEST
+ // (first file) until the test passes.
+
+ // Tests of the allowed disposal operators for GIF, APNG and WebP: keep, clear,
+ // and restore previous.
+ "== green-background.html?clear.gif green.png",
+ "== green-background.html?clear.png green.png",
+ "== green-background.html?clear.webp green.png",
+ "== keep.gif green.png",
+ "== keep.png green.png",
+ "== keep.webp green.png",
+ "== restore-previous.gif green.png",
+ "== restore-previous.png green.png",
+
+ // Tests of the blending/compositing operators that only APNG supports.
+ "== over.png grey.png",
+ "!= source.png grey.png",
+ "== bug900200.png bug900200-ref.png",
+ "== bug1319025.png bug1319025-ref.png",
+
+ // Test of subframe updates.
+ "== clear2.gif clear2-results.gif",
+ "== clear2.webp clear2-results.gif",
+];
+
+// Maintain a reference count of how many things we're waiting for until
+// we can say the tests are done.
+var gDelayCount = 0;
+function AddFinishDependency()
+ { ++gDelayCount; }
+function RemoveFinishDependency()
+ { if (--gDelayCount == 0) SimpleTest.finish(); }
+
+// We record the maximum number of times we had to look at a test before
+// it switched to the passing state (though we assume it's 10 to start
+// rather than 0 so that we have a reasonable default). Then we make a
+// test "time out" if it takes more than gTimeoutFactor times that
+// amount of time. This allows us to report a test failure rather than
+// making a test failure just show up as a timeout.
+var gMaxPassingTries = 10;
+var gTimeoutFactor = 10;
+
+function takeSnapshot(iframe_element)
+{
+ return snapshotWindow(iframe_element.contentWindow, false);
+}
+
+function passes(op, shot1, shot2)
+{
+ var values = compareSnapshots(shot1, shot2, op == "==");
+ return values[0];
+}
+
+function startTest(i)
+{
+ var testLine = gTests[i];
+ var splitData = testLine.split(" ");
+ var testData =
+ { op: splitData[0], test: splitData[1], reference: splitData[2] };
+ var tries = 0;
+
+ // Maintain state specific to this test in the closure exposed to all
+ // the functions nested inside this one.
+
+ function startIframe(url)
+ {
+ var element = document.createElement("iframe");
+ element.addEventListener("load", handleLoad);
+ // Smaller than normal reftests, but enough for these.
+ element.setAttribute("style", "width: 100px; height: 100px");
+ element.setAttribute("frameborder", "0");
+ element.setAttribute("scrolling", "no");
+ element.src = url;
+ document.body.appendChild(element);
+ function handleLoad(event)
+ {
+ iframe.loaded = true;
+ if (iframe == reference) {
+ reference.snapshot = takeSnapshot(element);
+ }
+ var other = (iframe == test) ? reference : test;
+ if (other.loaded) {
+ setTimeout(checkTest, 100);
+ }
+ }
+ function checkTest()
+ {
+ var test_snapshot = takeSnapshot(test.element);
+ if (passes(testData.op, test_snapshot, reference.snapshot)) {
+ if (tries > gMaxPassingTries) {
+ gMaxPassingTries = tries;
+ }
+ report(true);
+ } else {
+ ++tries;
+ if (tries > gMaxPassingTries * gTimeoutFactor) {
+ info("Giving up after " + tries + " tries, " +
+ "maxp=" + gMaxPassingTries +
+ "fact=" + gTimeoutFactor);
+ report(false);
+ } else {
+ // The animation might not have finished. Try again in 100ms.
+ setTimeout(checkTest, 100);
+ }
+ }
+ }
+ function report(result)
+ {
+ ok(result, "(" + i + ") " +
+ testData.op + " " + testData.test + " " + testData.reference);
+ RemoveFinishDependency();
+ }
+ var iframe = { element, loaded: false };
+
+ return iframe;
+ }
+
+ AddFinishDependency();
+ var test = startIframe(testData.test);
+ var reference = startIframe(testData.reference);
+}
+
+function runTests()
+{
+ // Run the tests.
+ for (var i = 0; i < gTests.length; ++i) {
+ startTest(i);
+ }
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+
+SpecialPowers.pushPrefEnv({"set": [["image.webp.enabled", true]]}, runTests);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_background_image_anim.html b/image/test/mochitest/test_background_image_anim.html
new file mode 100644
index 0000000000..f1aeb6288b
--- /dev/null
+++ b/image/test/mochitest/test_background_image_anim.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=666446
+-->
+<head>
+ <title>Test for Bug 666446 - Animated Background Images</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <script type="application/javascript" src="imgutils.js"></script>
+ <script type="application/javascript" src="animationPolling.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=666446">
+Mozilla Bug 666446: lots of animated gifs swamp us with paint events
+</a>
+<p id="display"></p>
+<div id="content">
+ <div id="referenceDiv" style="height: 140px; width: 140px;
+ display: none; background: #2aff00"></div>
+ <div id="bgImage" style="height: 140px; width: 140px; background-image: url(animated-gif.gif); display: none;">
+ </div>
+</div>
+<div id="debug" style="display:none"></div>
+<pre id="test">
+<script type="text/javascript">
+
+/** Test for Bug 666446 nsImageLoader/RasterImage**/
+
+const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
+
+function main() {
+ var animTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceDiv',
+ 'bgImage', 'debug');
+ animTest.beginTest();
+}
+
+window.onload = main;
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_bug1132427.html b/image/test/mochitest/test_bug1132427.html
new file mode 100644
index 0000000000..0ee2872fea
--- /dev/null
+++ b/image/test/mochitest/test_bug1132427.html
@@ -0,0 +1,94 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for scrolling selection into view</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+// We open a window which contains two copies of the same gif. One at a scaled size, one at the
+// natural image size. We rely on the bug only showing up in the scaled image. The gif has three
+// frames and a delay of 100ms. The first is all white. The second has a very small update area
+// in the upper left, it changes the pixels to slightly off white. The third changes all the
+// pixels to blue. When the bug appears we only update the upper left pixels when looping around
+// from the last frame to the first frame. We compare a middle pixel of the two images to make
+// sure that they are the same at 100ms for a second. If the bug appears then the middle pixel
+// on the scaled image will always be blue and so should not match the middle pixel on the
+// unscaled image which should be white two thirds of the time. If the timers fire at bad times
+// and only fire when both frames are displaying blue we won't be able to detect this bug and the
+// test will pass without testing anything important, but that's not a big deal. That should be
+// rare enough, and the next time the test is run will should do proper testing.
+
+SimpleTest.requestFlakyTimeout("Pre-existing timeouts when converting from mochitest-chrome");
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(openWindow);
+
+var win = null;
+
+function openWindow() {
+ win = window.open("bug1132427.html",
+ "", "scrollbars=yes,toolbar,menubar,width=600,height=800");
+ win.focus();
+}
+
+function doTest() {
+ setTimeout(continueTest, 1000);
+}
+
+function checkPixel(canvas, context, x1, y1, x2, y2) {
+ var pix = context.getImageData(0, 0, canvas.width, canvas.height).data;
+ for (var i = 0; i < 4; i++) {
+ is(pix[4 * (y1 * canvas.width + x1) + i], pix[4 * (y2 * canvas.width + x2) + i], "pixels should match");
+ }
+}
+
+var iterationsLeft = 10;
+
+function continueTest() {
+ // we need to drawWindow the chrome window so we can get a dump of the retained widget layers
+ // if we have to repaint to fulfill this drawWindow request then it will be impossible to
+ // observe the bug
+ // XXX(kmag): This test has not had access to a chrome window since the dawn
+ // of e10s. I'm not sure how accurate the above comment was even before that
+ // point, but it certainly is not accurate now.
+ var topWin = SpecialPowers.wrap(win).top;
+
+ var el = window.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+ el.width = topWin.innerWidth;
+ el.height = topWin.innerHeight;
+ var ctx = el.getContext("2d");
+ // pass the correct flags so we don't have to flush the retained layers
+ SpecialPowers.wrap(ctx).drawWindow(topWin, 0, 0, topWin.innerWidth, topWin.innerHeight, "rgba(0,0,0,0)",
+ ctx.DRAWWINDOW_USE_WIDGET_LAYERS | ctx.DRAWWINDOW_DRAW_VIEW | ctx.DRAWWINDOW_DRAW_CARET);
+
+ var leftbox = win.document.getElementById("left").getBoundingClientRect();
+ var rightbox = win.document.getElementById("right").getBoundingClientRect();
+ // this is actually chrome on left and right, but in practice we have none so it doesn't matter
+ var chromeleft = win.outerWidth - win.innerWidth;
+ // this is actually chrome on top and bottom, but bottom chrome is usually small to none and we have
+ // 100px to spare in hitting the middle of the image elements (they are 200x200)
+ var chrometop = win.outerHeight - win.innerHeight;
+
+ // compare the middle of the two image elements
+ checkPixel(el, ctx, chromeleft + leftbox.left + Math.floor(leftbox.width/2), chrometop + leftbox.top + Math.floor(leftbox.height/2),
+ chromeleft + rightbox.left + Math.floor(rightbox.width/2), chrometop + rightbox.top + Math.floor(rightbox.height/2));
+
+ iterationsLeft--;
+ if (iterationsLeft > 0) {
+ // now test 100ms later, we should have the next frame of the gif then
+ setTimeout(continueTest, 100);
+ } else {
+ win.close();
+ SimpleTest.finish();
+ }
+}
+</script>
+</pre>
+</body>
+
+</html>
diff --git a/image/test/mochitest/test_bug1180105.html b/image/test/mochitest/test_bug1180105.html
new file mode 100644
index 0000000000..579c8db760
--- /dev/null
+++ b/image/test/mochitest/test_bug1180105.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1180105
+-->
+<head>
+ <title>Test for Bug 1180105</title>
+ <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="initializeOnload()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1180105">Mozilla Bug 1180105</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+const WAITER_URL = "bug1180105-waiter.sjs";
+
+function initializeOnload() {
+ var firstimg = document.createElement('img');
+ firstimg.src = "bug1180105.sjs";
+ document.getElementById('content').appendChild(firstimg);
+
+ waitForFinish();
+}
+
+function waitForFinish() {
+ var loader = document.getElementById("loader");
+ loader.src = WAITER_URL;
+ loader.onload = function() {
+ var img = document.getElementsByTagName('img')[0];
+ ok(img.width > 0, "Image should be loaded by now");
+ SimpleTest.finish();
+ };
+}
+
+</script>
+</pre>
+<div id="content">>
+<iframe id="loader"></iframe>
+</div>
+</body>
+</html>
diff --git a/image/test/mochitest/test_bug1217571.html b/image/test/mochitest/test_bug1217571.html
new file mode 100644
index 0000000000..f81fc7c51d
--- /dev/null
+++ b/image/test/mochitest/test_bug1217571.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1217571
+-->
+<head>
+ <title>Test for Bug 1217571</title>
+ <script src="/tests/SimpleTest/SimpleTest.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=1217571">Mozilla Bug 1217571</a>
+<p id="display"></p>
+<iframe src="bug1217571-iframe.html"></iframe>
+<iframe src="bug1217571-iframe.html"></iframe>
+<pre id="test">
+<script type="application/javascript">
+/** Test for Bug 614392**/
+
+SimpleTest.waitForExplicitFinish();
+
+window.onload = function() {
+ // Each iframe loads the same image. Both images should share the same
+ // container from the image cache. Check that this holds true.
+ var iframes = document.getElementsByTagName("iframe");
+ var imgs = Array.from(iframes, function (f) {
+ return SpecialPowers.wrap(f.contentDocument.getElementsByTagName("img")[0]);
+ });
+ var containers = imgs.map(function (img) {
+ return img.getRequest(SpecialPowers.Ci.nsIImageLoadingContent.CURRENT_REQUEST)
+ .image;
+ });
+
+ ok(SpecialPowers.compare(containers[0], containers[1]),
+ "containers for identical images in different iframes should be identical");
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_bug1325080.html b/image/test/mochitest/test_bug1325080.html
new file mode 100644
index 0000000000..91dea11173
--- /dev/null
+++ b/image/test/mochitest/test_bug1325080.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1325080
+-->
+<head>
+ <title>Test for Bug 1325080</title>
+ <script src="/tests/SimpleTest/SimpleTest.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=1325080">Mozilla Bug 1325080</a>
+<pre id="test">
+<script type="application/javascript">
+/** Test for Bug 1325080 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function createImage() {
+ // This function's code comes from the Acid3 test #72
+ document.open();
+ document.write('<!DOCTYPE html><head><style>img { height: 10px; }</style></head><body><img src="data:image/gif;base64,R0lGODlhAQABAID%2FAMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw%3D%3D" alt="alt-text"></body>');
+ document.close();
+}
+
+window.onload = function() {
+ createImage();
+ SimpleTest.executeSoon(() => {
+ ok(document.images[0].height == 10, "Style should set height of image.");
+ SimpleTest.finish();
+ });
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_bug399925.html b/image/test/mochitest/test_bug399925.html
new file mode 100644
index 0000000000..ae45479377
--- /dev/null
+++ b/image/test/mochitest/test_bug399925.html
@@ -0,0 +1,102 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=399925
+-->
+<head>
+ <title>Test for Bug 399925</title>
+ <script src="/tests/SimpleTest/SimpleTest.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=399925">Mozilla Bug 399925</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<canvas id="canvas" width="100" height="100"> </canvas>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 399925. **/
+var triggerDiscardingManually = false;
+var pngResults = [];
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+
+window.onload = function() {
+ // It'd be nice to reduce the discard timer here, but unfortunately we only
+ // read that pref on startup. We instead manually trigger discarding on
+ // platforms where the discard timer is too long (which we'll somewhat
+ // arbitrarily define as 'longer than 60 seconds').
+ var expirationMs =
+ SpecialPowers.getIntPref('image.mem.surfacecache.min_expiration_ms');
+ if (expirationMs > 60000) {
+ ok(true, 'Triggering discarding manually because SurfaceCache expiration ' +
+ 'is ' + expirationMs + ' ms');
+ triggerDiscardingManually = true;
+ } else {
+ ok(true, 'Using normal discarding because SurfaceCache expiration ' +
+ 'is ' + expirationMs + ' ms');
+ }
+
+ // Enable discarding for the test.
+ SpecialPowers.pushPrefEnv({
+ 'set':[['image.mem.discardable',true]]
+ }, runTest);
+}
+
+function runTest() {
+ var image = new Image();
+ image.setAttribute("id", "gif");
+
+ // 1. Draw the canvas once on loadComplete
+ // 2. Redraw the canvas and compare the results right on discard
+ addCallbacks(image, drawCanvas, function() {
+ drawCanvas();
+ is(pngResults[0], pngResults[1], "got different rendered results");
+ SimpleTest.finish();
+ });
+
+ image.src = "bug399925.gif";
+ document.getElementById("content").appendChild(image);
+
+ if (triggerDiscardingManually) {
+ var request = SpecialPowers.wrap(image)
+ .getRequest(SpecialPowers.Ci.nsIImageLoadingContent.CURRENT_REQUEST);
+ setTimeout(() => request.requestDiscard(), 1000);
+ }
+}
+
+function addCallbacks(anImage, loadCompleteCallback, discardCallback) {
+ var observer = new ImageDecoderObserverStub();
+ observer.discard = function () {
+ imgLoadingContent.removeObserver(scriptedObserver);
+ discardCallback();
+ }
+ observer.loadComplete = loadCompleteCallback;
+ 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 drawCanvas() {
+ var canvas = document.getElementById('canvas');
+ var context = canvas.getContext('2d');
+ var gif = document.getElementById('gif');
+
+ context.drawImage(gif, 0, 0);
+ ok(true, "we got through the drawImage call without an exception being thrown");
+ pngResults.push(canvas.toDataURL());
+}
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/image/test/mochitest/test_bug415761.html b/image/test/mochitest/test_bug415761.html
new file mode 100644
index 0000000000..788e6097b6
--- /dev/null
+++ b/image/test/mochitest/test_bug415761.html
@@ -0,0 +1,118 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for icon filenames</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+// We want to make sure that moz-icon URIs with non-ascii characters work. To that
+// end, we compare the rendering of an icon without non-ascii characters to that
+// of one that does include such characters.
+
+// First, obtain the file URI to the ourselves:
+var chromeURI = location.href;
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var io = Services.io;
+chromeURI = io.newURI(chromeURI);
+var chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"]
+ .getService(Ci.nsIChromeRegistry);
+var fileURI = chromeReg.convertChromeURL(chromeURI);
+fileURI.QueryInterface(Ci.nsIFileURL);
+var self = fileURI.file;
+
+// Check if the ref or test icon are still hanging around from a previous test
+var testDest = self.parent;
+var refDest = self.parent;
+testDest.append("\u263a.ico");
+refDest.append("bug415761-ref.ico");
+if (testDest.exists()) {
+ testDest.remove(false);
+}
+if (refDest.exists()) {
+ refDest.remove(false);
+}
+
+// Copy the source icon so that we have two identical icons with, one with
+// non-ascii characters in its name.
+var src = self.parent;
+src.append("bug415761.ico");
+src.copyTo(null, testDest.leafName);
+src.copyTo(null, refDest.leafName);
+
+// Now load both icons in an Image() with a moz-icon URI
+var testImage = new Image();
+var refImage = new Image();
+
+var loadedImages = 0;
+testImage.onload = refImage.onload = function() {
+ loadedImages++;
+ if (loadedImages == 2) {
+ finishTest();
+ }
+};
+testImage.onerror = refImage.onerror = function() {
+ testImage.onload = refImage.onload = function() {};
+
+ ok(false, "Icon did not load successfully");
+ SimpleTest.finish();
+};
+
+function finishTest() {
+ ok(true, "Both icons loaded successfully");
+ // Render the reference to a canvas
+ var refCanvas = document.createElement("canvas");
+ refCanvas.setAttribute("height", 32);
+ refCanvas.setAttribute("width", 32);
+ refCanvas.getContext('2d').drawImage(refImage, 0, 0, 32, 32);
+
+ // A blank canvas to compare to to make sure we don't draw nothing.
+ var blankCanvas = document.createElement("canvas");
+ blankCanvas.setAttribute("height", 32);
+ blankCanvas.setAttribute("width", 32);
+
+ // Assert that they should be the different.
+ if (!navigator.userAgent.includes("Windows NT 6.1")) {
+ // Fails on Windows 7 for some reason.
+ assertSnapshots(blankCanvas, refCanvas, false, 0, "blank", "reference icon");
+ }
+
+ // Render the icon with a non-ascii character in its name to a canvas
+ var testCanvas = document.createElement("canvas");
+ testCanvas.setAttribute("height", 32);
+ testCanvas.setAttribute("width", 32);
+ testCanvas.getContext('2d').drawImage(testImage, 0, 0, 32, 32);
+
+ // Assert that they should be the same.
+ assertSnapshots(testCanvas, refCanvas, true, 0, "icon", "reference icon");
+ SimpleTest.finish();
+};
+
+var testURI = io.newFileURI(testDest).spec;
+var refURI = io.newFileURI(refDest).spec;
+testImage.src = "moz-icon:" + testURI;
+refImage.src = "moz-icon:" + refURI;
+
+SimpleTest.registerCleanupFunction(function() {
+ // Remove the copied files if they exist.
+ if (testDest.exists()) {
+ testDest.remove(false);
+ }
+ if (refDest.exists()) {
+ refDest.remove(false);
+ }
+});
+
+</script>
+</pre>
+</body>
+
+</html>
+
diff --git a/image/test/mochitest/test_bug435296.html b/image/test/mochitest/test_bug435296.html
new file mode 100644
index 0000000000..1610410b16
--- /dev/null
+++ b/image/test/mochitest/test_bug435296.html
@@ -0,0 +1,85 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=435296
+-->
+<head>
+ <title>Test for Bug 435296</title>
+ <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/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=435296">Mozilla Bug 435296</a>
+<img id="testimage" style="display: none;">
+<pre id="test">
+<script type="application/javascript">
+
+// Boilerplate
+SimpleTest.waitForExplicitFinish();
+
+// Assert that discarding isn't enabled, which might make this test go orange.
+ok(!getImagePref(DISCARD_ENABLED_PREF), "discarding should NOT be enabled here");
+
+// We want to make sure d-o-d is enabled, since that's what we're testing
+var oldDODPref = getImagePref(DECODEONDRAW_ENABLED_PREF);
+setImagePref(DECODEONDRAW_ENABLED_PREF, true);
+
+// We're relying on very particular behavior for certain images - clear the
+// image cache.
+clearImageCache();
+
+// In order to work around the effects introduced in bug 512435, we only load
+// the image after window onload fires
+function windowLoadHandler()
+{
+ // Set the source and an onload handler
+ var image = document.getElementById("testimage");
+ image.src = "schrep.png";
+ image.onload = imageLoadHandler;
+}
+
+function imageLoadHandler()
+{
+ // The image is hidden, so it should not be decoded
+ ok(!isFrameDecoded("testimage"), "image should not be decoded");
+
+ // Make the image visible
+ var image = document.getElementById("testimage");
+ image.style.display = "inline";
+
+ // Wait for the image to decode
+ setTimeout(function() {
+ tryToFinish();
+ }, 500);
+}
+
+function tryToFinish()
+{
+ // If it hasn't happened yet, wait longer. If it never happens, this test
+ // will timeout after 300 seconds...
+ if (!isFrameDecoded("testimage")) {
+ setTimeout(function() {
+ tryToFinish();
+ }, 500);
+ return;
+ }
+
+ // By definition, the image is decoded here. Give ourselves a pat on the back.
+ ok(isFrameDecoded("testimage"), "image should be decoded");
+
+ // Restore the decode-on-draw pref
+ setImagePref(DECODEONDRAW_ENABLED_PREF, oldDODPref);
+
+ // All done
+ SimpleTest.finish();
+}
+
+// Set our onload handler, making sure we have focus
+window.onload = SimpleTest.waitForFocus(windowLoadHandler);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_bug466586.html b/image/test/mochitest/test_bug466586.html
new file mode 100644
index 0000000000..fb900dc2b3
--- /dev/null
+++ b/image/test/mochitest/test_bug466586.html
@@ -0,0 +1,58 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=466586
+-->
+<head>
+ <title>Test for Bug 466586</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<body onload="loadSmall();">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=89419">Mozilla Bug 466586</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <img id="big" src="big.png"/>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+var jsBig = new Image();
+
+// We have loaded the large png with id "big". We want to test if it will be
+// kicked out of the cache and thus have to be reloaded, but to ensure that, we
+// need to get the cache to look at what is there. So we load another image,
+// this one small.
+function loadSmall()
+{
+ // Trivial check, for reference.
+ is(document.getElementById("big").width, 3000,
+ "HTML 'big' image width after page onload()");
+
+ var small = new Image();
+ small.onload = smallLoaded;
+ small.src = "red.png";
+}
+
+function smallLoaded()
+{
+ jsBig.src = document.getElementById("big").src;
+ // Check that it is not needed to wait for onload().
+ is(jsBig.width, 3000, "JS 'big' image width before its onload()");
+ // Check again after onload(), for reference.
+ jsBig.onload = jsBigLoaded;
+}
+
+function jsBigLoaded()
+{
+ is(jsBig.width, 3000, "JS 'big' image width after its onload()");
+
+ SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_bug468160.html b/image/test/mochitest/test_bug468160.html
new file mode 100644
index 0000000000..cb33454e1b
--- /dev/null
+++ b/image/test/mochitest/test_bug468160.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=468160
+-->
+<head>
+ <title>Test for Bug 468160</title>
+ <script src="/tests/SimpleTest/SimpleTest.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=468160">Mozilla Bug 468160</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<!-- bug468160.sjs does a simple redirect to a PNG image. As long as this
+ doesn't leak, this test passes. -->
+
+<object data="bug468160.sjs"></object>
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+ok(true, "test passed");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_bug478398.html b/image/test/mochitest/test_bug478398.html
new file mode 100644
index 0000000000..45ab6acc2f
--- /dev/null
+++ b/image/test/mochitest/test_bug478398.html
@@ -0,0 +1,87 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=478398
+-->
+<head>
+ <title>Test for Bug 478398</title>
+ <script src="/tests/SimpleTest/SimpleTest.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=478398">Mozilla Bug 478398</a>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 399925. **/
+var oldTimeoutPref;
+var oldDiscardPref;
+SimpleTest.waitForExplicitFinish();
+window.onload = stage1;
+var imageFilename = "bug478398_ONLY.png";
+
+function stage1()
+{
+ // Get the current pref values
+ oldTimeoutPref = getImagePref(DISCARD_TIMEOUT_PREF);
+ oldDiscardPref = getImagePref(DISCARD_ENABLED_PREF);
+
+ // We're testing discarding here
+ setImagePref(DISCARD_ENABLED_PREF, true);
+
+ // Sets the discard timer to 500 ms (max timeout = 2*500ms = 1s)
+ setImagePref(DISCARD_TIMEOUT_PREF, 500);
+
+ // Create the image _after_ setting the discard timer pref
+ // This image was carefully constructed to make it a "big win" for discarding,
+ // so any reasonable heuristic should still discard it.
+ var image = new Image();
+ image.setAttribute("id", "testimage");
+ image.style.display = "none";
+ image.src = imageFilename;
+
+ // Put the image into the document
+ document.body.appendChild(image);
+
+ // Wait for load, then do stage2
+ image.onload = stage2;
+}
+
+function stage2()
+{
+ // Make sure we're loaded
+ ok(isImageLoaded("testimage"), "image should be loaded");
+
+ // We're loaded - force a synchronous decode
+ forceDecode("testimage");
+
+ // We should be decoded
+ ok(isFrameDecoded("testimage"), "image should be decoded");
+
+ // Wait 1.5 seconds, then finish the test
+ setTimeout(function() {
+ finishTest();;
+ }, 1500);
+
+}
+
+function finishTest()
+{
+ // The image should be discarded by now
+ ok(!isFrameDecoded("testimage"), "image should have been discarded!");
+
+ // Reset the prefs
+ setImagePref(DISCARD_TIMEOUT_PREF, oldTimeoutPref);
+ setImagePref(DISCARD_ENABLED_PREF, oldDiscardPref);
+
+ // Finish the test
+ SimpleTest.finish();
+}
+
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/image/test/mochitest/test_bug490949.html b/image/test/mochitest/test_bug490949.html
new file mode 100644
index 0000000000..8f1b70c8f4
--- /dev/null
+++ b/image/test/mochitest/test_bug490949.html
@@ -0,0 +1,112 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=490949
+-->
+<head>
+ <title>Test for Bug 490949</title>
+ <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.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=490949">Mozilla Bug 490949</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<canvas id="canvas" width="100" height="100"> </canvas>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+var canvas = document.getElementById('canvas');
+var first, second, third;
+
+var RemoteCanvas = function() {
+ this.url = "bug490949-iframe.html";
+};
+
+RemoteCanvas.CANVAS_WIDTH = 100;
+RemoteCanvas.CANVAS_HEIGHT = 100;
+
+RemoteCanvas.prototype.load = function(cb) {
+ this.cb = cb;
+
+ var windowWidth = window.innerWidth - 25;
+ var iframe;
+ iframe = document.createElement("iframe");
+ iframe.id = "test-iframe";
+ iframe.height = "10px";
+ iframe.width = windowWidth + "px";
+ iframe.style.visibility = "hidden";
+ iframe.src = this.url;
+ // Here is where the magic happens... add a listener to the
+ // frame's onload event - it will call handleEvent
+ iframe.addEventListener("load", this, true);
+ // append to the end of the page
+ window.document.body.appendChild(iframe);
+};
+
+RemoteCanvas.prototype.reload = function(cb, force) {
+ this.cb = cb;
+ window.frames[0].location.reload(force);
+}
+
+RemoteCanvas.prototype.handleEvent = function() {
+ // Look back up the iframe by id
+ var ldrFrame = document.getElementById("test-iframe");
+ // Get a reference to the window object you need for the
+ // SpecialPowers.snapshotRect method
+ var remoteWindow = ldrFrame.contentWindow;
+
+ // Draw canvas
+ canvas.style.width = RemoteCanvas.CANVAS_WIDTH + "px";
+ canvas.style.height = RemoteCanvas.CANVAS_HEIGHT + "px";
+ canvas.width = RemoteCanvas.CANVAS_WIDTH;
+ canvas.height = RemoteCanvas.CANVAS_HEIGHT;
+ var windowWidth = window.innerWidth - 25;
+ var windowHeight = window.innerHeight;
+
+ var rect = { left: 0, top: 0, width: windowWidth, height: windowHeight };
+ var snapshot = SpecialPowers.snapshotRect(remoteWindow, rect, "rgb(0,0,0)");
+
+ var ctx = canvas.getContext("2d");
+ ctx.clearRect(0, 0,
+ RemoteCanvas.CANVAS_WIDTH,
+ RemoteCanvas.CANVAS_HEIGHT);
+ ctx.save();
+ ctx.scale(RemoteCanvas.CANVAS_WIDTH / windowWidth,
+ RemoteCanvas.CANVAS_HEIGHT / windowHeight);
+ ctx.drawImage(snapshot, 0, 0);
+ ctx.restore();
+ this.cb();
+};
+
+function checkFirst()
+{
+ first = canvas.toDataURL();
+ remoteCanvas.reload(checkForceReload, true);
+}
+
+function checkForceReload()
+{
+ second = canvas.toDataURL();
+ ok(first != second, "We got the wrong image.");
+ remoteCanvas.reload(checkLazyReload, false);
+}
+
+function checkLazyReload()
+{
+ third = canvas.toDataURL();
+ ok(second != third, "We got the wrong image.");
+ SimpleTest.finish();
+}
+
+var remoteCanvas = new RemoteCanvas();
+remoteCanvas.load(checkFirst);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_bug496292.html b/image/test/mochitest/test_bug496292.html
new file mode 100644
index 0000000000..7f91539bf3
--- /dev/null
+++ b/image/test/mochitest/test_bug496292.html
@@ -0,0 +1,130 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=496292
+-->
+<head>
+ <title>Test for Bug 496292</title>
+ <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.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=496292">Mozilla Bug 496292</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<canvas id="canvas" width="100" height="100"> </canvas>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+var canvas = document.getElementById('canvas');
+var first, second, third, ref;
+
+var RemoteCanvas = function(url) {
+ this.url = url;
+};
+
+RemoteCanvas.CANVAS_WIDTH = 100;
+RemoteCanvas.CANVAS_HEIGHT = 100;
+
+RemoteCanvas.prototype.load = function(cb) {
+ this.cb = cb;
+
+ var windowWidth = window.innerWidth - 25;
+ var iframe;
+ iframe = document.createElement("iframe");
+ iframe.id = "test-iframe-" + this.url;
+ iframe.height = "10px";
+ iframe.width = windowWidth + "px";
+ iframe.style.visibility = "hidden";
+ iframe.src = this.url;
+ // Here is where the magic happens... add a listener to the
+ // frame's onload event - it will call handleEvent
+ iframe.addEventListener("load", this, true);
+ // append to the end of the page
+ window.document.body.appendChild(iframe);
+};
+
+RemoteCanvas.prototype.reload = function(cb, force) {
+ this.cb = cb;
+ window.frames[0].location.reload(force);
+}
+
+RemoteCanvas.prototype.handleEvent = function() {
+ // Look back up the iframe by id
+ var ldrFrame = document.getElementById("test-iframe-" + this.url);
+ // Get a reference to the window object you need for the canvas
+ // SpecialPowers.snapshotRect method
+ var remoteWindow = ldrFrame.contentWindow;
+
+ // Draw canvas
+ canvas.style.width = RemoteCanvas.CANVAS_WIDTH + "px";
+ canvas.style.height = RemoteCanvas.CANVAS_HEIGHT + "px";
+ canvas.width = RemoteCanvas.CANVAS_WIDTH;
+ canvas.height = RemoteCanvas.CANVAS_HEIGHT;
+ var windowWidth = window.innerWidth - 25;
+ var windowHeight = window.innerHeight;
+
+ var rect = { left: 0, top: 0, width: windowWidth, height: windowHeight };
+ var snapshot = SpecialPowers.snapshotRect(remoteWindow, rect, "rgb(0,0,0)");
+
+ var ctx = canvas.getContext("2d");
+ ctx.clearRect(0, 0,
+ RemoteCanvas.CANVAS_WIDTH,
+ RemoteCanvas.CANVAS_HEIGHT);
+ ctx.save();
+ ctx.scale(RemoteCanvas.CANVAS_WIDTH / windowWidth,
+ RemoteCanvas.CANVAS_HEIGHT / windowHeight);
+ ctx.drawImage(snapshot, 0, 0);
+ ctx.restore();
+ this.cb();
+};
+
+function loadFirst()
+{
+ ref = canvas.toDataURL();
+
+ var remoteCanvas = new RemoteCanvas("bug496292-iframe-1.html");
+ remoteCanvas.load(checkFirst);
+}
+
+function checkFirst()
+{
+ first = canvas.toDataURL();
+ is(first, ref, "The default Accept header used by image loader is correct");
+
+ SpecialPowers.setCharPref("image.http.accept", "image/png");
+ SpecialPowers.pushPrefEnv({"set": [["image.http.accept", "image/png"]]}, function() {
+ var remoteCanvas = new RemoteCanvas("bug496292-iframe-2.html");
+ remoteCanvas.load(checkSecond);
+ });
+}
+
+function checkSecond()
+{
+ second = canvas.toDataURL();
+ is(second, ref, "The modified Accept header used by image loader is correct");
+
+ SpecialPowers.pushPrefEnv({"clear": [["image.http.accept"]]}, function() {
+ var remoteCanvas = new RemoteCanvas("bug496292-iframe-1.html");
+ remoteCanvas.load(checkThird);
+ });
+}
+
+function checkThird() {
+ third = canvas.toDataURL();
+ is(third, ref, "The Accept header used by image loader should be correctly reset");
+
+ SimpleTest.finish();
+}
+
+var refCanvas = new RemoteCanvas("bug496292-iframe-ref.html");
+refCanvas.load(loadFirst);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_bug497665.html b/image/test/mochitest/test_bug497665.html
new file mode 100644
index 0000000000..f14a767b7d
--- /dev/null
+++ b/image/test/mochitest/test_bug497665.html
@@ -0,0 +1,92 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=497665
+-->
+<head>
+ <title>Test for Bug 497665</title>
+ <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.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=497665">Mozilla Bug 497665</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+var image1first, image2first, image1second, image2second, image1third, image2third;
+
+SimpleTest.waitForExplicitFinish();
+
+function checkFirst()
+{
+ var iframeelem = document.getElementById('test-iframe');
+ var canvas = document.getElementById('canvas');
+ var ctx = canvas.getContext('2d');
+
+ var firstimg = iframeelem.contentDocument.getElementById('image1');
+ var secondimg = iframeelem.contentDocument.getElementById('image2');
+ ctx.drawImage(firstimg, 0, 0);
+ image1first = canvas.toDataURL();
+ ctx.drawImage(secondimg, 0, 0);
+ image2first = canvas.toDataURL();
+
+ ok(image1first == image2first, "We got different images, but shouldn't have.");
+
+ iframeelem.onload = checkForceReload;
+ iframeelem.contentWindow.location.reload(true);
+}
+
+function checkForceReload()
+{
+ var iframeelem = document.getElementById('test-iframe');
+ var canvas = document.getElementById('canvas');
+ var ctx = canvas.getContext('2d');
+
+ var firstimg = iframeelem.contentDocument.getElementById('image1');
+ var secondimg = iframeelem.contentDocument.getElementById('image2');
+ ctx.drawImage(firstimg, 0, 0);
+ image1second = canvas.toDataURL();
+ ctx.drawImage(secondimg, 0, 0);
+ image2second = canvas.toDataURL();
+
+ ok(image1second == image2second, "We got different images after a force-reload, but shouldn't have.");
+
+ // Sanity check that we actually reloaded.
+ ok(image1first != image1second, "We got the same images after a force-reload.");
+
+ iframeelem.onload = checkReload;
+ iframeelem.contentWindow.location.reload(false);
+}
+
+function checkReload()
+{
+ var iframeelem = document.getElementById('test-iframe');
+ var canvas = document.getElementById('canvas');
+ var ctx = canvas.getContext('2d');
+
+ var firstimg = iframeelem.contentDocument.getElementById('image1');
+ var secondimg = iframeelem.contentDocument.getElementById('image2');
+ ctx.drawImage(firstimg, 0, 0);
+ image1third = canvas.toDataURL();
+ ctx.drawImage(secondimg, 0, 0);
+ image2third = canvas.toDataURL();
+
+ ok(image1third == image2third, "We got different images after a reload, but shouldn't have.");
+
+ // Sanity check that we actually reloaded properly.
+ ok(image1second != image1third, "We got the same images after a reload.");
+ ok(image1first == image1third, "We didn't loop back to the first image.");
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+<div id="content"> <!-- style="display: none" -->
+<canvas id="canvas" width="100" height="100"> </canvas>
+<iframe id="test-iframe" src="bug497665-iframe.html" onload="checkFirst()"></iframe>
+</div>
+</body>
+</html>
diff --git a/image/test/mochitest/test_bug552605-1.html b/image/test/mochitest/test_bug552605-1.html
new file mode 100644
index 0000000000..271b3599ce
--- /dev/null
+++ b/image/test/mochitest/test_bug552605-1.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=552605
+-->
+<head>
+ <title>Test for Bug 552605</title>
+ <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.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=552605">Mozilla Bug 552605</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+var first, second;
+
+SimpleTest.waitForExplicitFinish();
+
+function checkFirst()
+{
+ var testimage = document.getElementById('test-image');
+ first = document.createElement('canvas')
+ var ctx = first.getContext('2d');
+ ctx.drawImage(testimage, 0, 0);
+
+ var newimg = new Image();
+ newimg.onload = checkSecond;
+ newimg.src = "bug552605.sjs";
+ document.body.appendChild(newimg);
+}
+
+function checkSecond()
+{
+ var testimage = document.getElementById('test-image');
+ second = document.createElement('canvas')
+ var ctx = second.getContext('2d');
+ ctx.drawImage(testimage, 0, 0);
+
+ // Check that the images are the same, since they're in the same document.
+ var values = compareSnapshots(first, second, true);
+ ok(values[0], "Image should be the same for all loads.");
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+<div id="content"> <!-- style="display: none" -->
+<img src="bug552605.sjs" onload="checkFirst()" id="test-image"></iframe>
+</div>
+</body>
+</html>
diff --git a/image/test/mochitest/test_bug552605-2.html b/image/test/mochitest/test_bug552605-2.html
new file mode 100644
index 0000000000..7869a9e481
--- /dev/null
+++ b/image/test/mochitest/test_bug552605-2.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=552605
+-->
+<head>
+ <title>Test for Bug 552605</title>
+ <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.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=552605">Mozilla Bug 552605</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+var count = 0;
+
+SimpleTest.waitForExplicitFinish();
+
+function check()
+{
+ count++;
+ if (count != 2)
+ return;
+
+ var image1 = document.getElementById('test-image1');
+ var image2 = document.getElementById('test-image2');
+ var first = document.createElement('canvas')
+ var ctx = first.getContext('2d');
+ ctx.drawImage(image1, 0, 0);
+
+ var second = document.createElement('canvas');
+ ctx = second.getContext('2d');
+ ctx.drawImage(image2, 0, 0);
+
+ // Check that the images are the same, since they're in the same document.
+ var values = compareSnapshots(first, second, true);
+ ok(values[0], "Image should be the same for all loads.");
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+<div id="content"> <!-- style="display: none" -->
+<img src="bug552605.sjs" onload="check()" id="test-image1"></iframe>
+<img src="bug552605.sjs" onload="check()" id="test-image2"></iframe>
+</div>
+</body>
+</html>
diff --git a/image/test/mochitest/test_bug553982.html b/image/test/mochitest/test_bug553982.html
new file mode 100644
index 0000000000..f7fe58a0af
--- /dev/null
+++ b/image/test/mochitest/test_bug553982.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=553982
+-->
+<head>
+ <title>Test for Bug 553982</title>
+ <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.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=553982">Mozilla Bug 553982</a>
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+var success = false;
+
+// This should fire
+function doError() {
+ success = true;
+}
+
+// This should not fire
+function doLoad() {
+ ok(false, "onload should not fire");
+}
+
+window.onload = function() {
+ ok(success, "onerror should fire");
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+<img src="data:text/html," onerror="doError()" onload="doLoad()" />
+</body>
+</html>
diff --git a/image/test/mochitest/test_bug601470.html b/image/test/mochitest/test_bug601470.html
new file mode 100644
index 0000000000..fdf2d074f8
--- /dev/null
+++ b/image/test/mochitest/test_bug601470.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=601470
+-->
+<head>
+ <title>Test for Bug 601470</title>
+ <script src="/tests/SimpleTest/SimpleTest.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=601470">Mozilla Bug 601470</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <img src="lime100x100.svg" style="width: 100px; height: 100px;">
+ <img src="damon.jpg" style="width: 100px; height: 100px;">
+</div>
+<pre id="test">
+<script type="application/javascript">
+/** Test for Bug 601470 **/
+
+SimpleTest.waitForExplicitFinish();
+
+window.onload = function() {
+ var mgr = SpecialPowers.Cc["@mozilla.org/memory-reporter-manager;1"]
+ .getService(SpecialPowers.Ci.nsIMemoryReporterManager);
+
+ var amount = 0;
+ var handleReport = function(aProcess, aPath, aKind, aUnits, aAmount, aDesc) {
+ amount += aAmount;
+ }
+
+ var finished = function() {
+ ok(amount > 0, "we should be using a nonzero amount of memory");
+ ok(true, "yay, didn't crash!");
+ SimpleTest.finish();
+ }
+
+ mgr.getReports(handleReport, null, finished, null, /* anonymize = */ false);
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_bug614392.html b/image/test/mochitest/test_bug614392.html
new file mode 100644
index 0000000000..94585ba35e
--- /dev/null
+++ b/image/test/mochitest/test_bug614392.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=614392
+-->
+<head>
+ <title>Test for Bug 614392</title>
+ <script src="/tests/SimpleTest/SimpleTest.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=614392">Mozilla Bug 614392</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <img src="damon.jpg">
+</div>
+<pre id="test">
+<script type="application/javascript">
+/** Test for Bug 614392**/
+
+SimpleTest.waitForExplicitFinish();
+
+window.onload = function() {
+ var img = SpecialPowers.wrap(document.getElementsByTagName("img")[0]);
+ var container = img
+ .getRequest(SpecialPowers.Ci.nsIImageLoadingContent.CURRENT_REQUEST)
+ .image;
+
+ container.animationMode =
+ SpecialPowers.Ci.imgIContainer.kDontAnimMode;
+
+ is(container.animationMode,
+ SpecialPowers.Ci.imgIContainer.kDontAnimMode,
+ "yay, our animationMode tweak took effect (and we didn't crash!)");
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_bug657191.html b/image/test/mochitest/test_bug657191.html
new file mode 100644
index 0000000000..17fcae6c8a
--- /dev/null
+++ b/image/test/mochitest/test_bug657191.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=657191
+-->
+<head>
+ <title>Test for Bug 657191</title>
+ <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.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=657191">Mozilla Bug 657191</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<!-- bug657191.sjs returns an SVG image with HTTP error code 500.
+ As long as this doesn't crash, this test passes. -->
+ <img src="bug657191.sjs">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 657191 **/
+SimpleTest.waitForExplicitFinish();
+
+window.onload = function() {
+ ok(true, "test passed");
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_bug671906.html b/image/test/mochitest/test_bug671906.html
new file mode 100644
index 0000000000..82f70f19d6
--- /dev/null
+++ b/image/test/mochitest/test_bug671906.html
@@ -0,0 +1,71 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=671906
+-->
+<head>
+ <title>Test for Bug 671906</title>
+ <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.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=671906">Mozilla Bug 671906</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+var first, second, third;
+var correct, val1, val2;
+
+SimpleTest.waitForExplicitFinish();
+
+async function snapshotFirst()
+{
+ var iframeelem = document.getElementById('test-iframe');
+ first = await SpecialPowers.wrap(snapshotWindow(iframeelem, false));
+
+ iframeelem.onload = snapshotSecond;
+ iframeelem.src = "http://example.com/tests/image/test/mochitest/bug671906-iframe.html";
+}
+
+async function snapshotSecond()
+{
+ var iframeelem = document.getElementById('test-iframe');
+ second = await SpecialPowers.wrap(snapshotWindow(iframeelem, false));
+
+ // We must have loaded the image again, because the principals for the
+ // loading document are different.
+ [correct, val1, val2] = compareSnapshots(first, second, false);
+ ok(correct, "Image should have changed after changing the iframe's src.");
+
+ iframeelem.onload = snapshotThird;
+ iframeelem.src = "http://mochi.test:8888/tests/image/test/mochitest/bug671906-iframe.html";
+}
+
+async function snapshotThird()
+{
+ var iframeelem = document.getElementById('test-iframe');
+ third = await SpecialPowers.wrap(snapshotWindow(iframeelem, false));
+
+ // We must have loaded the image again, because the principals for the
+ // loading document are different.
+ [correct, val1, val2] = compareSnapshots(second, third, false);
+ ok(correct, "Image should have changed after changing the iframe's src.");
+
+ // We must have looped back to the first image, because the sjs only sends
+ // one of two images.
+ [correct, val1, val2] = compareSnapshots(first, third, true);
+ ok(correct, "Image should be the same on the third load.");
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+<div id="content"> <!-- style="display: none" -->
+<iframe id="test-iframe" src="http://mochi.test:8888/tests/image/test/mochitest/bug671906-iframe.html" onload="snapshotFirst()"></iframe>
+</div>
+</body>
+</html>
diff --git a/image/test/mochitest/test_bug733553.html b/image/test/mochitest/test_bug733553.html
new file mode 100644
index 0000000000..6d7ed81019
--- /dev/null
+++ b/image/test/mochitest/test_bug733553.html
@@ -0,0 +1,92 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=733553
+-->
+<head>
+ <title>Test for Bug 733553</title>
+ <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="initializeOnload()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=733553">Mozilla Bug 733553</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+var testIndex = -1;
+var testParts = [
+ [1, "red.png"],
+ [40, "animated-gif2.gif"],
+ [1, "red.png"],
+ [100, "lime100x100.svg"],
+ [100, "lime100x100.svg"],
+ [40, "animated-gif2.gif"],
+ [1, "red.png"],
+ // Note a failure of the next 'red.png' may indicate failure of resniffing on
+ // this part ('shaver.png'); see bug 907575.
+ [177, "shaver.png"],
+ [1, "red.png"],
+ [80, "damon.jpg"],
+ [80, "damon.jpg"],
+ [80, "damon.jpg"],
+ // An invalid image (from bug 787899) that is further delivered with a
+ // "special" bad MIME type that indicates that the necko
+ // multipart/x-mixed-replace parser wasn't able to parse it.
+ // We use a width of 80 because MultipartImage will just drop rillybad.jpg
+ // and re-present damon.jpg.
+ [80, "rillybad.jpg"],
+ [80, "damon.jpg"],
+ // Similarly, we'll drop bad.jpg, so we use damon.jpg's width.
+ [80, "bad.jpg"],
+ [1, "red.png"],
+ // We also drop invalid.jpg, so we use red.png's width.
+ [1, "invalid.jpg"],
+ [40, "animated-gif2.gif"]
+];
+
+// We'll append the part number to this, and tell the informant
+const BASE_URL = "bug733553-informant.sjs?";
+
+function initializeOnload() {
+ var firstimg = document.createElement('img');
+ firstimg.addEventListener("load", imageLoad);
+ firstimg.addEventListener("error", imageLoad);
+ firstimg.alt = "";
+ firstimg.src = "bug733553.sjs";
+ document.getElementById('content').appendChild(firstimg);
+
+ // Really ready for first, but who's counting
+ readyForNext();
+}
+
+function readyForNext() {
+ var loader = document.getElementById("loader");
+ loader.src = BASE_URL + ++testIndex;
+}
+
+function imageLoad(aEvent) {
+ var [width, fileName] = testParts[testIndex];
+ is(aEvent.target.width, width,
+ "Test " + testIndex + " " + fileName + " width correct");
+
+ // Always call readyForNext here, as it's the closest we have to a cleanup
+ readyForNext();
+ if (testParts.length == testIndex) {
+ var firstimg = document.getElementsByTagName('img')[0];
+ firstimg.removeEventListener("load", imageLoad);
+ firstimg.removeEventListener("error", imageLoad);
+ SimpleTest.finish();
+ }
+}
+
+</script>
+</pre>
+<div id="content"> <!-- style="display: none" -->
+<iframe id="loader"></iframe>
+</div>
+</body>
+</html>
diff --git a/image/test/mochitest/test_bug767779.html b/image/test/mochitest/test_bug767779.html
new file mode 100644
index 0000000000..ae3fe492cf
--- /dev/null
+++ b/image/test/mochitest/test_bug767779.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=767779
+-->
+<head>
+ <title>Test for Bug 767779</title>
+ <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <script type="application/javascript" src="imgutils.js"></script>
+ <script type="application/javascript" src="animationPolling.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=767779">Mozilla Bug 767779</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+const FAILURE_TIMEOUT = 30000; // Fail early after 30 seconds
+
+function main()
+{
+ // referenceDiv's size and color correspond to the last frame of the GIF
+ var animTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceDiv',
+ 'animatedGif', 'debug');
+ animTest.beginTest();
+}
+
+window.onload = main;
+</script>
+</pre>
+<div id="content"> <!-- style="display: none" -->
+ <div id="referenceDiv" style="height: 40px; width: 40px;
+ display: none; background: #18ff00;"></div>
+ <div id="animatedImage">
+ <img id="animatedGif" src="bug767779.sjs" style="display: none;"/>
+ <div id="text-descr"></div>
+ </div>
+ <div id="debug" style="display:none">
+ </div>
+</div>
+</body>
+</html>
diff --git a/image/test/mochitest/test_bug865919.html b/image/test/mochitest/test_bug865919.html
new file mode 100644
index 0000000000..46686c67d7
--- /dev/null
+++ b/image/test/mochitest/test_bug865919.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=865919
+-->
+<head>
+ <meta charset="UTF-8">
+ <title>Test for Bug 865919</title>
+ <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script type="text/javascript">
+"use strict";
+
+/* Test for Bug 865919:
+ * BMP with height of INT32_MIN should fail to decode.
+ */
+
+SimpleTest.waitForExplicitFinish();
+
+var req = new XMLHttpRequest();
+req.onload = function() { CallbackAssert(true, 'Request for file succeeded.'); };
+req.onerror = function() { CallbackAssert(false, 'Request for file failed! Failed to test non-existent file.'); };
+req.open('GET', 'INT32_MIN.bmp');
+req.send(null);
+
+var outstandingCallbacks = 2;
+
+function CallbackAssert(assertVal, failText) {
+ ok(assertVal, failText);
+
+ outstandingCallbacks--;
+ ok(outstandingCallbacks >= 0, '`outstandingCallbacks` should be non-negative.');
+ if (outstandingCallbacks)
+ return;
+
+ // No outstanding callbacks remain, so we're done.
+ SimpleTest.finish();
+}
+
+</script>
+
+<div id='content'>
+ <img src='INT32_MIN.bmp'
+ onerror='CallbackAssert(true, "Got expected onerror for INT32_MIN.bmp")'
+ onload='CallbackAssert(false, "Got unexpected onload for INT32_MIN.bmp")'>
+</div>
+
+</body>
+</html>
+
diff --git a/image/test/mochitest/test_bug89419-1.html b/image/test/mochitest/test_bug89419-1.html
new file mode 100644
index 0000000000..3364dacbfb
--- /dev/null
+++ b/image/test/mochitest/test_bug89419-1.html
@@ -0,0 +1,68 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=89419
+-->
+<head>
+ <title>Test for Bug 89419</title>
+ <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.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=89419">Mozilla Bug 89419</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+var first, second, third;
+var correct, val1, val2;
+
+SimpleTest.waitForExplicitFinish();
+
+function checkFirst()
+{
+ var iframeelem = document.getElementById('test-iframe');
+ first = snapshotWindow(iframeelem.contentWindow, false);
+
+ iframeelem.onload = checkSecond;
+ iframeelem.contentWindow.location.reload(false);
+}
+
+function checkSecond()
+{
+ var iframeelem = document.getElementById('test-iframe');
+ second = snapshotWindow(iframeelem.contentWindow, false);
+
+ // Check that we actually reloaded.
+ [correct, val1, val2] = compareSnapshots(first, second, false);
+ ok(correct, "Image should have changed after the first reload.");
+
+ iframeelem.onload = checkThird;
+ iframeelem.contentWindow.location.reload(false);
+}
+
+function checkThird()
+{
+ var iframeelem = document.getElementById('test-iframe');
+ third = snapshotWindow(iframeelem.contentWindow, false);
+
+ // Check that we actually reloaded.
+ [correct, val1, val2] = compareSnapshots(second, third, false);
+ ok(correct, "Image should have changed after the second reload.");
+
+ // Make sure we looped back to the first image.
+ [correct, val1, val2] = compareSnapshots(first, third, true);
+ ok(correct, "Third image should match first image.");
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+<div id="content"> <!-- style="display: none" -->
+<iframe id="test-iframe" src="bug89419-iframe.html" onload="checkFirst()"></iframe>
+</div>
+</body>
+</html>
diff --git a/image/test/mochitest/test_bug89419-2.html b/image/test/mochitest/test_bug89419-2.html
new file mode 100644
index 0000000000..25861b9179
--- /dev/null
+++ b/image/test/mochitest/test_bug89419-2.html
@@ -0,0 +1,70 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=89419
+-->
+<head>
+ <title>Test for Bug 89419</title>
+ <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.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=89419">Mozilla Bug 89419</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+var first, second, third;
+var correct, val1, val2;
+
+SimpleTest.waitForExplicitFinish();
+
+function checkFirst()
+{
+ var iframeelem = document.getElementById('test-iframe');
+ first = snapshotWindow(iframeelem.contentWindow, false);
+
+ iframeelem.onload = checkSecond;
+ // eslint-disable-next-line no-self-assign
+ iframeelem.contentWindow.location.href = iframeelem.contentWindow.location.href;
+}
+
+function checkSecond()
+{
+ var iframeelem = document.getElementById('test-iframe');
+ second = snapshotWindow(iframeelem.contentWindow, false);
+
+ // Check that we actually reloaded.
+ [correct, val1, val2] = compareSnapshots(first, second, false);
+ ok(correct, "Image should have changed after the first reload.");
+
+ iframeelem.onload = checkThird;
+ // eslint-disable-next-line no-self-assign
+ iframeelem.contentWindow.location.href = iframeelem.contentWindow.location.href;
+}
+
+function checkThird()
+{
+ var iframeelem = document.getElementById('test-iframe');
+ third = snapshotWindow(iframeelem.contentWindow, false);
+
+ // Check that we actually reloaded.
+ [correct, val1, val2] = compareSnapshots(second, third, false);
+ ok(correct, "Image should have changed after the second reload.");
+
+ // Make sure we looped back to the first image.
+ [correct, val1, val2] = compareSnapshots(first, third, true);
+ ok(correct, "Third image should match first image.");
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+<div id="content"> <!-- style="display: none" -->
+<iframe id="test-iframe" src="bug89419-iframe.html" onload="checkFirst()"></iframe>
+</div>
+</body>
+</html>
diff --git a/image/test/mochitest/test_bullet_animation.html b/image/test/mochitest/test_bullet_animation.html
new file mode 100644
index 0000000000..1d97512fdb
--- /dev/null
+++ b/image/test/mochitest/test_bullet_animation.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=666446
+-->
+<head>
+ <title>Test for Bug 666446 - Animated Bullets</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <script type="application/javascript" src="imgutils.js"></script>
+ <script type="application/javascript" src="animationPolling.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=666446">
+Mozilla Bug 666446: lots of animated gifs swamp us with paint events
+</a>
+<p id="display"></p>
+
+<div id="content">
+ <div id="cleanDiv" style="display: none;">
+ <ul>
+ <li>Test 1</li>
+ </ul>
+ </div>
+ <div id="referenceDiv" style="display: none;">
+ <ul>
+ <li style="list-style-image: url(animated-gif-finalframe.gif);">Test 1</li>
+ </ul>
+ </div>
+ <div id="animatedImage" style="display: none;">
+ <ul>
+ <li style="list-style-image: url(animated-gif.gif);">Test 1</li>
+ </ul>
+ </div>
+ <div id="text-descr"></div>
+ <div id="debug" style="display:none">
+ </div>
+</div>
+<pre id="test">
+<script type="text/javascript">
+const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
+
+function main()
+{
+ var animTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceDiv',
+ 'animatedImage', 'debug', 'cleanDiv',
+ 'animated-gif-finalframe.gif');
+ animTest.beginTest();
+}
+
+window.onload = main;
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_canvas_frame_animation.html b/image/test/mochitest/test_canvas_frame_animation.html
new file mode 100644
index 0000000000..c2820b3a66
--- /dev/null
+++ b/image/test/mochitest/test_canvas_frame_animation.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<title>Test for bug 1619245 - animated image as canvas background</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+<script src="imgutils.js"></script>
+<script src="animationPolling.js"></script>
+<!-- Intentionally not including SimpleTest.css as that sets the background of the root -->
+<body id="body" style="background-image: url(animated-gif.gif); display: none; overflow: hidden;">
+ <div id="reference" style="background-image: url(animated-gif-finalframe.gif); display: none; width: 100vw; height: 100vh;"></div>
+ <div id="clean" style="display: none; width: 100vw; height: 100vh;"></div>
+ <div id="debug" style="display: none"></div>
+</body>
+<script>
+const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
+
+window.onload = function() {
+ // first move the reference outside of the <body>, so that the test can properly show it.
+ document.documentElement.appendChild(document.getElementById("reference"));
+ document.documentElement.appendChild(document.getElementById("debug"));
+ document.documentElement.appendChild(document.getElementById("clean"));
+ var animTest = new AnimationTest(20, FAILURE_TIMEOUT, 'reference', 'body', 'debug', 'clean', 'animated-gif.gif');
+ animTest.beginTest();
+}
+</script>
diff --git a/image/test/mochitest/test_changeOfSource.html b/image/test/mochitest/test_changeOfSource.html
new file mode 100644
index 0000000000..f557a3a140
--- /dev/null
+++ b/image/test/mochitest/test_changeOfSource.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=666446
+-->
+<head>
+ <title>Test for Bug 666446 - Change of Source (1st Version)</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <script type="application/javascript" src="imgutils.js"></script>
+ <script type="application/javascript" src="animationPolling.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=666446">
+Mozilla Bug 666446: lots of animated gifs swamp us with paint events
+</a>
+<p id="display"></p>
+
+<div id="content">
+ <div id="referenceDiv" style="height: 40px; width: 40px;
+ display: none; background: #2aff00;">
+ </div>
+ <div id="animatedImage">
+ <img id='animatedGif' src="animated-gif.gif" style="display: none;">
+ </div>
+ <div id="text-descr"></div>
+ <div id="debug" style="display:none">
+ </div>
+</div>
+<pre id="test">
+<script type="text/javascript">
+const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
+
+var gAnimTest;
+var gIntervalId;
+
+function initSecondTest() {
+ document.getElementById('debug').style.display = 'none';
+ document.getElementById('referenceDiv').style.background = "#9600ff";
+ document.getElementById('animatedGif').setAttribute('src',
+ 'animated-gif2.gif');
+ document.getElementById('animatedGif').style.display = 'none';
+ var secondTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceDiv',
+ 'animatedGif', 'debug', '', '', false);
+ secondTest.beginTest();
+}
+
+function main()
+{
+ gAnimTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceDiv',
+ 'animatedGif', 'debug', '', '', false,
+ initSecondTest);
+ gAnimTest.beginTest();
+
+}
+
+window.onload = main;
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_changeOfSource2.html b/image/test/mochitest/test_changeOfSource2.html
new file mode 100644
index 0000000000..e3db345470
--- /dev/null
+++ b/image/test/mochitest/test_changeOfSource2.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=666446
+-->
+<head>
+ <title>Test for Bug 691792 - Change of Source (2nd Version)</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <script type="application/javascript" src="imgutils.js"></script>
+ <script type="application/javascript" src="animationPolling.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=691792">
+Mozilla Bug 691792: Change of src attribute for animated gifs no longer works as expected
+</a>
+<p id="display"></p>
+
+<div id="content">
+ <div id="animatedImage">
+ <img id='animatedGif' src="purple.gif" style="display: none;">
+ </div>
+ <div id="text-descr"></div>
+ <div id="debug" style="display:none">
+ </div>
+</div>
+<pre id="test">
+<script type="text/javascript">
+const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
+
+var gAnimTest;
+var gIntervalId;
+
+function main()
+{
+ gAnimTest = new AnimationTest(20, FAILURE_TIMEOUT, 'animated-gif2.gif',
+ 'animatedGif', 'debug', '', 'animated-gif2.gif',
+ false);
+ gAnimTest.beginTest();
+}
+
+window.onload = main;
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_discardAnimatedImage.html b/image/test/mochitest/test_discardAnimatedImage.html
new file mode 100644
index 0000000000..d74190c78d
--- /dev/null
+++ b/image/test/mochitest/test_discardAnimatedImage.html
@@ -0,0 +1,215 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=686905
+-->
+<head>
+ <title>Test that animated images can be discarded</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=686905">Mozilla Bug 686905</a>
+<p id="display"></p>
+<div id="content">
+ <div id="container">
+ <canvas id="canvas" width="100" height="100"></canvas>
+ <img id="infinitepng" src="infinite-apng.png">
+ <img id="infinitegif" src="animated1.gif">
+ <img id="infinitewebp" src="infinite.webp">
+ <img id="finitepng" src="restore-previous.png">
+ <img id="finitegif" src="animated-gif.gif">
+ <img id="finitewebp" src="keep.webp">
+ </div>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 686905. **/
+SimpleTest.waitForExplicitFinish();
+
+var gFinished = false;
+
+var gNumDiscards = 0;
+
+window.onload = function() {
+ // Enable discarding for the test.
+ SpecialPowers.pushPrefEnv({
+ 'set':[['image.mem.discardable',true],
+ ['image.webp.enabled',true]]
+ }, runTest);
+}
+
+var gImgs = ['infinitepng', 'infinitegif', 'infinitewebp',
+ 'finitepng', 'finitegif', 'finitewebp'];
+// 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, 0, 0, 0, 0, 0];
+// The last snapshot of the image. Used to check that the image actually changes.
+var gLastSnapShot = [null, null, null, null, null, null];
+// Number of observed changes in the snapshot.
+var gNumSnapShotChanges = [0, 0, 0, 0, 0, 0];
+// If we've removed the observer.
+var gRemovedObserver = [false, false, false, false, false, false];
+
+// 2 would probably be a good enough test, we arbitrarily choose 4.
+var kNumFrameUpdatesToExpect = 4;
+
+function runTest() {
+ var animatedDiscardable =
+ SpecialPowers.getBoolPref('image.mem.animated.discardable');
+ if (!animatedDiscardable) {
+ ok(true, "discarding of animated images is disabled, nothing to test");
+ SimpleTest.finish();
+ return;
+ }
+
+ setTimeout(step2, 0);
+}
+
+function step2() {
+ // 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(step3, 0);
+}
+
+function step3() {
+ document.getElementById("container").style.display = "none";
+ document.documentElement.offsetLeft; // force that style to take effect
+
+ for (var i = 0; i < gImgs.length; i++) {
+ requestDiscard(document.getElementById(gImgs[i]));
+ }
+
+ // the discard observers will call step4
+}
+
+function step4() {
+ gCountingFrameUpdates = true;
+ document.getElementById("container").style.display = "";
+
+ // Draw the images to canvas to force them to be decoded again.
+ for (var i = 0; i < gImgs.length; i++) {
+ drawCanvas(document.getElementById(gImgs[i]));
+ }
+}
+
+function checkIfFinished() {
+ if (gFinished) {
+ return;
+ }
+
+ if ((gNumFrameUpdates[0] >= kNumFrameUpdatesToExpect) &&
+ (gNumFrameUpdates[1] >= kNumFrameUpdatesToExpect) &&
+ (gNumFrameUpdates[2] >= kNumFrameUpdatesToExpect) &&
+ (gNumSnapShotChanges[0] >= kNumFrameUpdatesToExpect) &&
+ (gNumSnapShotChanges[1] >= kNumFrameUpdatesToExpect) &&
+ (gNumSnapShotChanges[2] >= kNumFrameUpdatesToExpect)) {
+ ok(true, "got expected frame updates");
+ gFinished = true;
+ 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 (arrayIndex >= 3) {
+ // The last two images are finite, so we don't expect any frame updates,
+ // this image is done the test, so remove the observer.
+ if (!gRemovedObserver[arrayIndex]) {
+ gRemovedObserver[arrayIndex] = true;
+ imgLoadingContent.removeObserver(scriptedObserver);
+ }
+ }
+ if (gNumDiscards == gImgs.length) {
+ step4();
+ }
+ };
+ 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) {
+ if (!gRemovedObserver[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>
+
diff --git a/image/test/mochitest/test_discardFinishedAnimatedImage.html b/image/test/mochitest/test_discardFinishedAnimatedImage.html
new file mode 100644
index 0000000000..ebaa89e705
--- /dev/null
+++ b/image/test/mochitest/test_discardFinishedAnimatedImage.html
@@ -0,0 +1,144 @@
+<!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 === "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>
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>
+
diff --git a/image/test/mochitest/test_drawDiscardedImage.html b/image/test/mochitest/test_drawDiscardedImage.html
new file mode 100644
index 0000000000..f1a2dde89c
--- /dev/null
+++ b/image/test/mochitest/test_drawDiscardedImage.html
@@ -0,0 +1,85 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=731419
+-->
+<head>
+ <title>Test for Bug 731419 - Draw an ostensibly discarded image to a canvas</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<body>
+
+<!--
+ Load an image in an iframe, then draw that image to a canvas. Then set the
+ iframe to display:none (after bug 731419, this causes the image's decoded
+ data to be discarded) and draw the image to a canvas again. We should draw
+ the same image data both times.
+-->
+
+<script>
+
+SimpleTest.waitForExplicitFinish();
+
+var data1;
+
+function drawImage()
+{
+ var canvas = document.getElementById('canvas');
+ var ctx = canvas.getContext('2d');
+ var iframeDoc = document.getElementById('iframe').contentDocument;
+
+ ctx.clearRect(0, 0, canvas.height, canvas.width);
+ ctx.drawImage(iframeDoc.getElementById('image'), 0, 0);
+ return canvas.toDataURL();
+}
+
+function iframeLoad()
+{
+ data1 = drawImage();
+ document.getElementById('iframe').style.display = 'none';
+
+ // Spin the event loop a few times to give the image in the display:none
+ // iframe a chance to be discarded.
+ SimpleTest.executeSoon(function() {
+ SimpleTest.executeSoon(function() {
+ SimpleTest.executeSoon(function() {
+ step2();
+ });
+ });
+ });
+}
+
+function step2()
+{
+ is(drawImage(), data1, "Same image before and after iframe display:none");
+ SimpleTest.finish();
+}
+
+</script>
+
+<canvas id='canvas'></canvas>
+
+<iframe id='iframe' onload='iframeLoad()' srcdoc='<img id="image"
+src="data:image/png;base64,
+iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAADRElEQVQ4EQXBTWgcZQCA4ff7ZmZ3
+NpvNZLObTWpCuoZGIxWJplAKbVUKavUiHrQHaRG1XrV4SNuD4MFcRDwUoR4qEq2gFUlBEWmtppYi
+acSmMakxtfkx/5tNdmd35/8bn0cAzJ7IXwKGH/q8NDF48vy+7vk/3tzVXDs8nj9cAAiDcD70gwVi
+vvvr4tsjAAAAAmD2RD4GOL34wge21XHsnHWh9/aUjX1pC4C1UpXrP08zN7vMvvujPx3P/PD+0VH3
+BoAcTspXAbK9iuGe78+csy70ZnsVvh+xWQ8p1QI8dNK7CiT9CmeO28/4ZsuVX9/IvQwgmzLaU9LS
+AGh/3KJ5jw6A6ynyL7Xx7UCORiwQGRN0g7C4m4FX9poNV35681ShU6ZbxKDRLJVuZQl9RdSQRB4c
+OtDGoQNtPGHBuh0SaAa+ZvLjHYt8fwfZrpTl2cFp2ZwVDyQzSgLgVIndGN/tIP/c61y/WWb14gaV
+asTWioPSDabnfCqVkK7BHKHtPK0n06oFGQHgewJtbw8AujGNkYTNpTJxbYfaygqR0piYkaRkhMya
+eI2oX9dTQRIFmtrmz7EGpS9vESZjAN7tfo/UL2PouoZwbfxIo9jaoLWlzI7jEPmhLjVEbXs5IPAE
+jx5M0Z5RZDJwqjCENFN8XBtmOP0FXq1O6NR5snsRtsv4C+voCdHQpcfVtTn/xUKXTrMlyfck6BCC
+a02fkDZDqirF5JVrRA8ewagu8NbADN6az9btMoTqjnasKDTHjp5PSM3I5DQy7UliZbCz7bCwFDD/
+b52h3BCviVHOHv2bvmydyvwOM5MSmch9Ji4/SxMNcaNJTw707zdJmBqeo+G5BuO/V6AzQ5Oo01MI
+KBaTOOis3rPZrKeqrbn2hwXA10fY7zvicqeZKPQ8YpKxJCgIpEQXisBVhG6MYcQ0pGJp2XWnSpx8
+52o0ogF8c5/ltMlGIlYHo0qQrq9HxHWFvx3RqCoCFzwn4L+tiIVV5Y5MhWc/mlDnATQAgMkynbMb
+opoN4z2hUAlPBdpO6FNp+JTtkPVaHE7NYX94K/xqrBT/BvwDIAAAgALQAfT1aWJwtyYea9VEXoAo
+RfHGYhTfvRfF48BdYB3YAPgfnOuE39kFlREAAAAASUVORK5CYII=">'></iframe>
+
+</body>
+</html>
+
diff --git a/image/test/mochitest/test_error_events.html b/image/test/mochitest/test_error_events.html
new file mode 100644
index 0000000000..89ed9da528
--- /dev/null
+++ b/image/test/mochitest/test_error_events.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=715308
+-->
+<head>
+ <title>Test for Bug 715308 comment 93</title>
+ <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<!-- Test for Bug 715308 comment 93:
+
+ - For a valid image, onload is fired and onerror is never fired.
+ - For an image with errors, onerror is fired, but onload is never fired.
+ - For any image, either onload or onerror is fired, but never both.
+
+ -->
+<script type="text/javascript">
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+
+var numCallbacks = 0;
+
+function image_error(name)
+{
+ numCallbacks++;
+ ok(name == 'error-early', "Got onerror for " + name);
+}
+
+function image_load(name)
+{
+ numCallbacks++;
+ ok(name == 'shaver', "Got onload for " + name);
+}
+
+function page_load()
+{
+ ok(numCallbacks == 2, 'Got page load before all onload/onerror callbacks?');
+
+ // Spin the event loop a few times to let image_error run if it's going to,
+ // then finish the test.
+ SimpleTest.executeSoon(function() {
+ SimpleTest.executeSoon(function() {
+ SimpleTest.executeSoon(function() {
+ SimpleTest.finish();
+ });
+ });
+ });
+}
+
+addEventListener('load', page_load);
+
+</script>
+
+<div id="content">
+ <img src='shaver.png' onerror='image_error("shaver")' onload='image_load("shaver")'>
+ <img src='error-early.png' onerror='image_error("error-early")' onload='image_load("error-early")'>
+</div>
+
+</pre>
+</body>
+</html>
+
diff --git a/image/test/mochitest/test_has_transparency.html b/image/test/mochitest/test_has_transparency.html
new file mode 100644
index 0000000000..6461034635
--- /dev/null
+++ b/image/test/mochitest/test_has_transparency.html
@@ -0,0 +1,169 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1089880
+-->
+<head>
+ <title>Test for Bug 1089880</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <script type="application/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=1089880">Mozilla Bug 1089880</a>
+<p id="display"></p>
+<div id="content">
+</div>
+<pre id="test">
+<script type="application/javascript">
+/** Test for Bug 1089880 **/
+
+SimpleTest.requestFlakyTimeout("Early failure timeout");
+SimpleTest.waitForExplicitFinish();
+
+const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
+
+const Cc = SpecialPowers.Cc;
+const Ci = SpecialPowers.Ci;
+const gContent = document.getElementById("content");
+
+var gCanvas;
+var gCanvasCtx;
+var gImg;
+var gMyDecoderObserver;
+var gIsTestFinished = false;
+var gFiles;
+var gCurrentFileIsTransparent = false;
+var gHasTransparencyWasCalled = false;
+
+function* testFiles() {
+ // [A, B] where 'A' is the image and 'B' is whether it's transparent.
+
+ // PNGs and GIFs may be transparent or not.
+ yield ["red.png", false];
+ yield ["transparent.png", true];
+ yield ["red.gif", false];
+ yield ["transparent.gif", true];
+
+ // GIFs with padding on the first frame are always transparent.
+ yield ["first-frame-padding.gif", true];
+
+ // JPEGs are never transparent.
+ yield ["damon.jpg", false];
+
+ // Most BMPs are not transparent. (The TestMetadata GTest, which will
+ // eventually replace this test totally, has coverage for the kinds that can be
+ // transparent.)
+ yield ["opaque.bmp", false];
+
+ // ICO files which contain BMPs have an additional type of transparency - the
+ // AND mask - that warrants separate testing. (Although, after bug 1201796,
+ // all ICOs are considered transparent.)
+ yield ["ico-bmp-opaque.ico", true];
+ yield ["ico-bmp-transparent.ico", true];
+
+ // SVGs are always transparent.
+ yield ["lime100x100.svg", true];
+}
+
+function loadNext() {
+ var currentFile = "";
+ gHasTransparencyWasCalled = false;
+ let {done, value} = gFiles.next();
+ if (done) {
+ // We ran out of test files.
+ cleanUpAndFinish();
+ return;
+ }
+ [currentFile, gCurrentFileIsTransparent] = value;
+ gImg.setAttribute("src", currentFile);
+}
+
+function onHasTransparency(aRequest) {
+ gHasTransparencyWasCalled = true;
+}
+
+function onDecodeComplete(aRequest) {
+ if (!gCurrentFileIsTransparent) {
+ ok(!gHasTransparencyWasCalled,
+ "onHasTransparency was not called for non-transparent file " + gImg.src);
+ } else {
+ ok(gHasTransparencyWasCalled,
+ "onHasTransparency was called for transparent file " + gImg.src);
+ }
+ loadNext();
+}
+
+function onError() {
+ if (gIsTestFinished) {
+ return;
+ }
+ ok(false, "Should successfully load " + gImg.src);
+ loadNext();
+}
+
+function onLoad() {
+ if (gIsTestFinished) {
+ return;
+ }
+ ok(true, "Should successfully load " + gImg.src);
+
+ // Force decoding of the image.
+ SimpleTest.executeSoon(function() {
+ gCanvasCtx.drawImage(gImg, 0, 0);
+ });
+}
+
+function failTest() {
+ ok(false, "timing out after " + FAILURE_TIMEOUT + "ms. " +
+ "currently displaying " + gImg.src);
+ cleanUpAndFinish();
+}
+
+function cleanUpAndFinish() {
+ if (gIsTestFinished) {
+ return;
+ }
+ gIsTestFinished = true;
+ let imgLoadingContent = SpecialPowers.wrap(gImg);
+ imgLoadingContent.removeObserver(gMyDecoderObserver);
+ SimpleTest.finish();
+}
+
+function main() {
+ gFiles = testFiles();
+ gCanvas = document.createElement('canvas');
+ gCanvasCtx = gCanvas.getContext('2d');
+ gImg = new Image();
+ gImg.onload = onLoad;
+ gImg.onerror = onError;
+
+ // Create, customize & attach decoder observer.
+ var observer = new ImageDecoderObserverStub();
+ observer.hasTransparency = onHasTransparency;
+ observer.decodeComplete = onDecodeComplete;
+ gMyDecoderObserver =
+ Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
+ .createScriptedObserver(SpecialPowers.wrapCallbackObject(observer));
+ let imgLoadingContent = SpecialPowers.wrap(gImg);
+ imgLoadingContent.addObserver(gMyDecoderObserver);
+
+ // We want to test the cold loading behavior, so clear cache in case an
+ // earlier test got our image in there already.
+ clearAllImageCaches();
+
+ // Load the first image.
+ loadNext();
+
+ // In case something goes wrong, fail earlier than mochitest timeout,
+ // and with more information.
+ setTimeout(failTest, FAILURE_TIMEOUT);
+}
+
+window.onload = main;
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_image_crossorigin_data_url.html b/image/test/mochitest/test_image_crossorigin_data_url.html
new file mode 100644
index 0000000000..4465b5aab4
--- /dev/null
+++ b/image/test/mochitest/test_image_crossorigin_data_url.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test for handling of 'crossorigin' attribute on CSS link with data: URL</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="someuniqueidhere"></div>
+<img id="testimg" crossorigin src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR42mP4z8AAAAMBAQD3A0FDAAAAAElFTkSuQmCC">
+<script>
+ /* global async_test, assert_equals */
+ var t = async_test("img@crossorigin with data: src");
+ window.addEventListener("load", t.step_func_done(function() {
+ var img = document.getElementById("testimg");
+ assert_equals(img.naturalWidth, 1, "Should have 1px width");
+ assert_equals(img.naturalHeight, 1, "Should have 1px height");
+ var c = document.createElement("canvas");
+ c.width = c.height = 1;
+ var ctx = c.getContext("2d");
+ ctx.drawImage(img, 0, 0);
+ var data = ctx.getImageData(0, 0, 1, 1);
+ assert_equals(data.width, 1, "Should have 1px data width");
+ assert_equals(data.height, 1, "Should have 1px data height");
+ assert_equals(data.data[0], 255, "Should have lots of red");
+ assert_equals(data.data[1], 0, "Should have no green");
+ assert_equals(data.data[2], 0, "Should have no blue");
+ assert_equals(data.data[3], 255, "Should have no translucency");
+ }));
+</script>
diff --git a/image/test/mochitest/test_mq_dynamic_svg.html b/image/test/mochitest/test_mq_dynamic_svg.html
new file mode 100644
index 0000000000..70f613fcc5
--- /dev/null
+++ b/image/test/mochitest/test_mq_dynamic_svg.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Dynamic changes to prefers-color-scheme affecting SVG images</title>
+<link rel=stylesheet type=text/css href=/tests/SimpleTest/test.css>
+<script src=/tests/SimpleTest/SimpleTest.js></script>
+<script src=/tests/SimpleTest/WindowSnapshot.js></script>
+<body onload=run()>
+<iframe id=f1 width=300 height=600></iframe>
+<iframe id=f2 width=300 height=600></iframe>
+<script>
+SimpleTest.waitForExplicitFinish();
+
+// Not strictly needed, but keeps eslint happy.
+let f1 = window.f1;
+let f2 = window.f2;
+
+function snapshotsEqual() {
+ let s1 = snapshotWindow(f1.contentWindow);
+ let s2 = snapshotWindow(f2.contentWindow);
+ return compareSnapshots(s1, s2, true)[0];
+}
+
+async function run() {
+ let loadedFrame1 = new Promise(resolve => f1.onload = resolve);
+ let loadedFrame2 = new Promise(resolve => f2.onload = resolve);
+ await SpecialPowers.pushPrefEnv({ set: [["ui.systemUsesDarkTheme", 0]] });
+ f1.src = "mq_dynamic_svg_test.html";
+ f2.src = "mq_dynamic_svg_ref.html";
+ await loadedFrame1;
+ await loadedFrame2;
+ ok(!snapshotsEqual(), "In light mode snapshot comparison should be false");
+ await SpecialPowers.pushPrefEnv({ set: [["ui.systemUsesDarkTheme", 1]] });
+ ok(snapshotsEqual(), "In dark mode snapshot comparison should be true");
+ SimpleTest.finish();
+}
+</script>
diff --git a/image/test/mochitest/test_net_failedtoprocess.html b/image/test/mochitest/test_net_failedtoprocess.html
new file mode 100644
index 0000000000..470f3f5b31
--- /dev/null
+++ b/image/test/mochitest/test_net_failedtoprocess.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test that a image decoding error producs a net:failed-to-process-uri-content
+observer event with the nsIURI of the failed image as the subject
+-->
+<head>
+ <title>Test for image net:failed-to-process-uri-content</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<pre id="test">
+</pre>
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+const Ci = SpecialPowers.Ci;
+const Cc = SpecialPowers.Cc;
+var obs = Cc["@mozilla.org/observer-service;1"].getService();
+obs = obs.QueryInterface(Ci.nsIObserverService);
+
+var observer = {
+ /* eslint-disable-next-line mozilla/use-chromeutils-generateqi */
+ QueryInterface (aIID) {
+ if (aIID.equals(Ci.nsISupports) ||
+ aIID.equals(Ci.nsIObserver))
+ return this;
+ throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
+ },
+
+ observe(subject, topic, data) {
+ ok(topic == "net:failed-to-process-uri-content", "wrong topic");
+ subject = subject.QueryInterface(Ci.nsIURI);
+ is(subject.asciiSpec, `${location.origin}/tests/image/test/mochitest/invalid.jpg`, "wrong subject");
+
+ obs.removeObserver(this, "net:failed-to-process-uri-content");
+
+ SimpleTest.finish();
+ }
+};
+
+obs.addObserver(SpecialPowers.wrapCallbackObject(observer), "net:failed-to-process-uri-content");
+
+document.write('<img src="damon.jpg">');
+document.write('<img src="invalid.jpg">');
+
+</script>
+</body>
+</html>
diff --git a/image/test/mochitest/test_removal_ondecode.html b/image/test/mochitest/test_removal_ondecode.html
new file mode 100644
index 0000000000..4ce7555757
--- /dev/null
+++ b/image/test/mochitest/test_removal_ondecode.html
@@ -0,0 +1,160 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=841579
+-->
+<head>
+ <title>Test for Bug 841579</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <script type="application/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=841579">Mozilla Bug 841579</a>
+<p id="display"></p>
+<div id="content">
+</div>
+<pre id="test">
+<script type="application/javascript">
+/** Test for Bug 841579**/
+
+SimpleTest.requestFlakyTimeout("Early failure timeout");
+SimpleTest.waitForExplicitFinish();
+
+const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
+
+const Cc = SpecialPowers.Cc;
+const Ci = SpecialPowers.Ci;
+const gContent = document.getElementById("content");
+
+var gImg;
+var gMyDecoderObserver;
+var gIsTestFinished = false;
+var gFiles;
+var gNotifications = 0;
+var gLoads = 0;
+var gRemovals = 0;
+var gExpected = 5;
+
+function* fileToLoad() {
+ yield "red.png";
+ yield "invalid.jpg";
+ yield "lime100x100.svg";
+ yield "bad.jpg";
+ yield "rillybad.jpg";
+}
+
+function onSizeAvailable(aRequest) {
+ ok(true, "AfterLoad.onSizeAvailable called for " + gImg.src);
+}
+function onLoadComplete(aRequest) {
+ ok(gExpected > gLoads, "AfterLoad.onLoadComplete called for " + gImg.src);
+ gLoads++;
+
+ // We aren't going to get a decode complete event if the metadata decoding
+ // failed (i.e. for invalid.jpg). By definition we should have the size or
+ // an error once we get a load complete event, so check if the size is valid
+ // and if not, trigger a decode complete event manually.
+ var hasSize = false;
+ try {
+ hasSize = aRequest.image.width > 0 && aRequest.image.height > 0;
+ } catch(e) {}
+
+ if (hasSize) {
+ maybeAdvance();
+ } else {
+ onDecodeComplete(aRequest);
+ }
+}
+
+function onDecodeComplete(aRequest) {
+ ok(gExpected > gRemovals, "AfterLoad.onDecodeComplete called for " + gImg.src);
+ SimpleTest.executeSoon(function() {
+ try {
+ gContent.removeChild(gImg);
+ }
+ catch (e) {}
+ gRemovals++;
+ maybeAdvance();
+ });
+}
+
+function failTest() {
+ ok(false, "timing out after " + FAILURE_TIMEOUT + "ms. " +
+ "currently displaying " + gImg.src);
+ cleanUpAndFinish();
+}
+
+function onNotification()
+{
+ ok(gExpected > gNotifications, "AfterLoad.onNotification called for " + gImg.src);
+ gNotifications++;
+ maybeAdvance();
+}
+
+function maybeAdvance()
+{
+ if (gNotifications != gLoads || gNotifications != gRemovals) {
+ return;
+ }
+
+ let {done, value} = gFiles.next();
+ if (done) {
+ cleanUpAndFinish();
+ return;
+ }
+ gImg.src = value;
+ gContent.appendChild(gImg);
+}
+
+function cleanUpAndFinish() {
+ // On the off chance that failTest and myOnStopFrame are triggered
+ // back-to-back, use a flag to prevent multiple calls to SimpleTest.finish.
+ if (gIsTestFinished) {
+ return;
+ }
+ let imgLoadingContent = SpecialPowers.wrap(gImg);
+ imgLoadingContent.removeObserver(gMyDecoderObserver);
+ // TODO - this isn't the case until post-bug 716140's refactorings
+ // ok(gNotifications == gLoads, "Should be notified the same number of times as loads");
+ SimpleTest.finish();
+ gIsTestFinished = true;
+}
+
+function main() {
+ gFiles = fileToLoad();
+ gImg = new Image();
+ gImg.onload = onNotification;
+ gImg.onerror = onNotification;
+
+ // Create, customize & attach decoder observer
+ var observer = new ImageDecoderObserverStub();
+ observer.sizeAvailable = onSizeAvailable;
+ observer.loadComplete = onLoadComplete;
+ observer.decodeComplete = onDecodeComplete;
+ gMyDecoderObserver =
+ Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
+ .createScriptedObserver(SpecialPowers.wrapCallbackObject(observer));
+ let imgLoadingContent = SpecialPowers.wrap(gImg);
+ imgLoadingContent.addObserver(gMyDecoderObserver);
+
+ // We want to test the cold loading behavior, so clear cache in case an
+ // earlier test got our image in there already.
+ clearAllImageCaches();
+
+ // kick off image-loading! myOnStopFrame handles the rest.
+ gImg.setAttribute("src", gFiles.next().value);
+ gContent.appendChild(gImg);
+
+ // In case something goes wrong, fail earlier than mochitest timeout,
+ // and with more information.
+ setTimeout(failTest, FAILURE_TIMEOUT);
+}
+
+window.onload = main;
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_removal_onload.html b/image/test/mochitest/test_removal_onload.html
new file mode 100644
index 0000000000..0a060542f5
--- /dev/null
+++ b/image/test/mochitest/test_removal_onload.html
@@ -0,0 +1,142 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=841579
+-->
+<head>
+ <title>Test for Bug 841579</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <script type="application/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=841579">Mozilla Bug 841579</a>
+<p id="display"></p>
+<div id="content">
+</div>
+<pre id="test">
+<script type="application/javascript">
+/** Test for Bug 841579**/
+
+SimpleTest.requestFlakyTimeout("Early failure timeout");
+SimpleTest.waitForExplicitFinish();
+
+const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
+
+const Cc = SpecialPowers.Cc;
+const Ci = SpecialPowers.Ci;
+const gContent = document.getElementById("content");
+
+var gImg;
+var gMyDecoderObserver;
+var gIsTestFinished = false;
+var gFiles;
+var gNotifications = 0;
+var gLoads = 0;
+var gRemovals = 0;
+var gExpected = 5;
+
+function* fileToLoad() {
+ yield "red.png";
+ yield "invalid.jpg";
+ yield "lime100x100.svg";
+ yield "bad.jpg";
+ yield "rillybad.jpg";
+}
+
+function onSizeAvailable(aRequest) {
+ ok(true, "AfterLoad.onSizeAvailable called for " + gImg.src);
+}
+function onLoadComplete(aRequest) {
+ ok(gExpected > gLoads, "AfterLoad.onLoadComplete called for " + gImg.src);
+ gLoads++;
+ SimpleTest.executeSoon(function() {
+ try {
+ gContent.removeChild(gImg);
+ }
+ catch (e) {}
+ gRemovals++;
+ maybeAdvance();
+ });
+}
+function onDecodeComplete(aRequest) {
+ ok(true, "AfterLoad.onDecodeComplete called for " + gImg.src);
+}
+
+function failTest() {
+ ok(false, "timing out after " + FAILURE_TIMEOUT + "ms. " +
+ "currently displaying " + gImg.src);
+ cleanUpAndFinish();
+}
+
+function onNotification()
+{
+ gNotifications++;
+ maybeAdvance();
+}
+
+function maybeAdvance()
+{
+ if (gRemovals != gNotifications) {
+ return;
+ }
+
+ let {done, value} = gFiles.next();
+ if (done) {
+ cleanUpAndFinish();
+ return;
+ }
+ gImg.src = value;
+ gContent.appendChild(gImg);
+}
+
+function cleanUpAndFinish() {
+ // On the off chance that failTest and myOnStopFrame are triggered
+ // back-to-back, use a flag to prevent multiple calls to SimpleTest.finish.
+ if (gIsTestFinished) {
+ return;
+ }
+ let imgLoadingContent = SpecialPowers.wrap(gImg);
+ imgLoadingContent.removeObserver(gMyDecoderObserver);
+ // TODO: this isn't the case until post-bug 716140's refactorings
+ // ok(gNotifications == gLoads, "Should be notified the same number of times as loads");
+ SimpleTest.finish();
+ gIsTestFinished = true;
+}
+
+function main() {
+ gFiles = fileToLoad();
+ gImg = new Image();
+ gImg.onload = onNotification;
+ gImg.onerror = onNotification;
+
+ // Create, customize & attach decoder observer
+ var observer = new ImageDecoderObserverStub();
+ observer.sizeAvailable = onSizeAvailable;
+ observer.loadComplete = onLoadComplete;
+ observer.decodeComplete = onDecodeComplete;
+ gMyDecoderObserver =
+ Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
+ .createScriptedObserver(SpecialPowers.wrapCallbackObject(observer));
+ let imgLoadingContent = SpecialPowers.wrap(gImg);
+ imgLoadingContent.addObserver(gMyDecoderObserver);
+
+ // We want to test the cold loading behavior, so clear cache in case an
+ // earlier test got our image in there already.
+ clearAllImageCaches();
+
+ // kick off image-loading! myOnStopFrame handles the rest.
+ gImg.setAttribute("src", gFiles.next());
+
+ // In case something goes wrong, fail earlier than mochitest timeout,
+ // and with more information.
+ setTimeout(failTest, FAILURE_TIMEOUT);
+}
+
+window.onload = main;
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_short_gif_header.html b/image/test/mochitest/test_short_gif_header.html
new file mode 100644
index 0000000000..f8294825b5
--- /dev/null
+++ b/image/test/mochitest/test_short_gif_header.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=844684
+-->
+<head>
+ <title>Test for Bug 844684</title>
+ <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.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=844684">Mozilla Bug 844684</a>
+<div id="content">
+<img id="testcontent" onload="success()" onerror="failure()">
+</div>
+<pre id="test">
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+document.getElementById('testcontent').src = "short_header.gif";
+
+function success() {
+ ok(true, "Image loaded.");
+ SimpleTest.finish();
+}
+
+function failure() {
+ ok(false, "Image didn't load.");
+ SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_staticClone.html b/image/test/mochitest/test_staticClone.html
new file mode 100644
index 0000000000..7400e064c6
--- /dev/null
+++ b/image/test/mochitest/test_staticClone.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=878037
+-->
+<head>
+ <title>Test for Bug 878037</title>
+ <script src="/tests/SimpleTest/SimpleTest.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=878037">Mozilla Bug 878037</a>
+<p id="display"></p>
+<div>
+ <!-- transparent-animation.gif from the gif reftests. -->
+ <img id="animated" src="data:image/gif;base64,R0lGODlhZABkAIABAP8AAP///yH5BAkBAAEALAAAAABLAGQAAAK8jI+py+0Po5y02ouz3rz7D4biSJbmiabqyrbuC8fyTNf2jef6zvf+DwwKh8Si8YhMKpchgPMJjUqnVOipis1ir9qul+sNV8HistVkTj/JajG7/UXDy+95tm4fy/NdPF/q93dWIqgVWAhwWKgoyPjnyAeZJ2lHOWcJh9mmqcaZ5mkGSreHOCXqRloadRrGGkeoapoa6+TaN0tra4gbq3vHq+q7BVwqrMeEnKy8zNzs/AwdLT1NXW19jZ1tUgAAIfkECQEAAQAsAAAAADQAZAAAArCMj6nL7Q+jnLTai7PevPsPhuJIluaJpurKtu4Lx/JM1/aN5/rO9/7vAAiHxKLxiCRCkswmc+mMSqHSapJqzSof2u4Q67WCw1MuOTs+N9Pqq7kdZcON8vk2aF+/88g6358HaCc4Rwhn2IaopnjGSOYYBukl2UWpZYm2x0enuXnX4NnXGQqAKTYaalqlWoZH+snwWsQah+pJ64Sr5ypbCvQLHCw8TFxsfIycrLzM3PxQAAAh+QQJAQABACwAAAAAGwBkAAACUIyPqcvtD6OctNqLs968+w+G4kiW5omm6sq27gTE8kzX9o3n+s73/g8MCofEovGITCqXzKbzCY1Kp9Sq9YrNarfcrvdrfYnH5LL5jE6r16sCADs=">
+</div>
+<pre id="test">
+<script type="application/javascript">
+/** Test for Bug 878037**/
+
+SimpleTest.waitForExplicitFinish();
+
+window.onload = function() {
+ var img = document.getElementById("animated");
+ var content = SpecialPowers.wrap(img);
+
+ var request = content.getRequest(SpecialPowers.Ci.nsIImageLoadingContent.CURRENT_REQUEST);
+
+ var staticReq = request.getStaticRequest();
+ var clone = staticReq.clone(null);
+
+ ok(clone, "hooray, we didn't crash!");
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_svg_animatedGIF.html b/image/test/mochitest/test_svg_animatedGIF.html
new file mode 100644
index 0000000000..04077676b7
--- /dev/null
+++ b/image/test/mochitest/test_svg_animatedGIF.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=666446
+-->
+<head>
+ <title>Test for Bug 666446 - Animated Raster Images inside of SVG Frames</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <script type="application/javascript" src="imgutils.js"></script>
+ <script type="application/javascript" src="animationPolling.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<!-- Make sure embed element is snapped to an exact pixel. -->
+<div class="bug-header" style="height: 100px;">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=666446">
+ Mozilla Bug 666446: lots of animated gifs swamp us with paint events
+ </a>
+</div>
+
+<p id="display"></p>
+<div id="content">
+ <div id="referenceDiv" style="height: 40px; width: 40px;
+ display: none; background: #2aff00"></div>
+ <!--
+ We use <embed> here instead of <img> because the <img> tag utilizes
+ the VectorImage class for SVG, whereas in this test, we are testing
+ RasterImage.
+ -->
+ <embed id="embeddedSVG" src="animation.svg" type="image/svg+xml"
+ style="height: 40px; width: 40px; display: none;"/>
+</div>
+<div id="debug" style="display:none"></div>
+<pre id="test">
+<script type="text/javascript">
+
+/** Test for Bug 666446 SVGImageFrame/RasterImage**/
+
+const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
+
+function main() {
+ var animTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceDiv',
+ 'embeddedSVG', 'debug', '');
+ animTest.beginTest();
+}
+
+window.onload = main;
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_svg_filter_animation.html b/image/test/mochitest/test_svg_filter_animation.html
new file mode 100644
index 0000000000..b72aaa68c1
--- /dev/null
+++ b/image/test/mochitest/test_svg_filter_animation.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=666446
+-->
+<head>
+ <title>Test for Bug 666446 - Animated Images within SVG Filters</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <script type="application/javascript" src="imgutils.js"></script>
+ <script type="application/javascript" src="animationPolling.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=666446">
+Mozilla Bug 666446: lots of animated gifs swamp us with paint events
+</a>
+<p id="display"></p>
+<div id="content">
+ <embed id="referenceImage" src="filter-final.svg" type="image/svg+xml" style="display: none;"/>
+ <embed id="embeddedSVGFilt" src="filter.svg" type="image/svg+xml" style="display: none;"/>
+</div>
+<div id="debug" style="display:none"></div>
+<pre id="test">
+<script type="text/javascript">
+
+/** Test for Bug 666446 nsSVGFEImageElement/RasterImage**/
+
+const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
+
+function main() {
+ var animTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceImage',
+ 'embeddedSVGFilt', 'debug', '');
+ animTest.beginTest();
+}
+
+window.onload = main;
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_synchronized_animation.html b/image/test/mochitest/test_synchronized_animation.html
new file mode 100644
index 0000000000..3c2a874be1
--- /dev/null
+++ b/image/test/mochitest/test_synchronized_animation.html
@@ -0,0 +1,128 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=867758
+-->
+<head>
+ <title>Test for Bug 867758</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <script type="application/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=867758">Mozilla Bug 867758</a>
+<p id="display"></p>
+<div id="content">
+</div>
+<pre id="test">
+<script type="application/javascript">
+/** Test for Bug 867758**/
+
+SimpleTest.requestFlakyTimeout("Early failure timeout");
+SimpleTest.waitForExplicitFinish();
+
+const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
+
+const Cc = SpecialPowers.Cc;
+const Ci = SpecialPowers.Ci;
+const gContent = document.getElementById("content");
+
+var gDispatched = false;
+var gRanEvent = false;
+var gObserver;
+var gImg1;
+var gImg2;
+var gFirstImageLoaded = false;
+var gOuter;
+var gFinished = false;
+var gFirstRequest = null;
+
+function cleanUpAndFinish() {
+ if (gFinished) {
+ return;
+ }
+ var imgLoadingContent = SpecialPowers.wrap(gImg1);
+ imgLoadingContent.removeObserver(gOuter);
+
+ imgLoadingContent = SpecialPowers.wrap(gImg2);
+ imgLoadingContent.removeObserver(gOuter);
+
+ SimpleTest.finish();
+
+ gFinished = true;
+}
+
+function frameUpdate(aRequest) {
+ if (!gDispatched) {
+ Promise.resolve().then(function() {
+ gRanEvent = true;
+ });
+ gDispatched = true;
+ gFirstRequest = aRequest;
+ } else if (aRequest != gFirstRequest) {
+ ok(!gRanEvent, "Should not have run event before all frame update events occurred!");
+ cleanUpAndFinish();
+ }
+}
+
+function failTest() {
+ ok(false, "timing out after " + FAILURE_TIMEOUT + "ms. ");
+ cleanUpAndFinish();
+}
+
+function waitForLoadAndTest(image) {
+ return () => {
+ // Draw the image into a canvas to ensure it's decoded.
+ var canvas = document.createElement('canvas');
+ var context = canvas.getContext('2d');
+ context.drawImage(image, 0, 0);
+
+ // Attach the observer.
+ var imgLoadingContent = SpecialPowers.wrap(image);
+ imgLoadingContent.addObserver(gOuter);
+
+ // If the other image already loaded, add both images to the document, which
+ // begins the real test.
+ if (gFirstImageLoaded) {
+ gContent.appendChild(gImg1);
+ gContent.appendChild(gImg2);
+ } else {
+ gFirstImageLoaded = true;
+ }
+ };
+}
+
+function main() {
+ gImg1 = new Image();
+ gImg2 = new Image();
+
+ // Create and customize decoder observer
+ var obs = new ImageDecoderObserverStub();
+ obs.frameUpdate = frameUpdate;
+
+ gOuter = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools).createScriptedObserver(SpecialPowers.wrapCallbackObject(obs));
+
+ // We want to test the cold loading behavior, so clear cache in case an
+ // earlier test got our image in there already.
+ clearAllImageCaches();
+
+ // These are two copies of the same image; hence, they have the same frame rate.
+ gImg1.src = "animated1.gif";
+ gImg2.src = "animated2.gif";
+
+ // Wait for each image to load.
+ gImg1.addEventListener('load', waitForLoadAndTest(gImg1));
+ gImg2.addEventListener('load', waitForLoadAndTest(gImg2));
+
+ // In case something goes wrong, fail earlier than mochitest timeout,
+ // and with more information.
+ setTimeout(failTest, FAILURE_TIMEOUT);
+}
+
+window.onload = main;
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_undisplayed_iframe.html b/image/test/mochitest/test_undisplayed_iframe.html
new file mode 100644
index 0000000000..695e4e5448
--- /dev/null
+++ b/image/test/mochitest/test_undisplayed_iframe.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=666446
+-->
+<head>
+<title>Test for Bug 666446 - Test for Animated Gif within IFRAME</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+<script type="application/javascript" src="imgutils.js"></script>
+<script type="application/javascript" src="animationPolling.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=666446">
+ Mozilla Bug 666446: lots of animated gifs swamp us with paint events</a>
+ <p id="display"></p>
+
+ <div id="content">
+ <div id="referenceDiv" style="display:none;">
+ <iframe id="referenceIFrame" src="ref-iframe.html" width="50%" height="100">
+ Browser does not support iframes.
+ </iframe>
+ </div>
+ <div id="animatedImage">
+ <iframe id="imageIFrame" src="iframe.html" width="50%" height="100" style="display: none;">
+ Browser does not support iframes.
+ </iframe>
+ </div>
+ <div id="debug" style="display: none"></div>
+ </div>
+ <pre id="test">
+<script type="text/javascript">
+const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
+
+function main()
+{
+ var animTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceDiv',
+ 'imageIFrame', 'debug');
+ animTest.beginTest();
+}
+
+window.onload = main;
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_webcam.html b/image/test/mochitest/test_webcam.html
new file mode 100644
index 0000000000..8d6bf6b490
--- /dev/null
+++ b/image/test/mochitest/test_webcam.html
@@ -0,0 +1,68 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=641748
+
+webcam-simulacrum.mgif is a hand-edited file containing red.gif and blue.gif,
+concatenated together with the relevant headers for
+multipart/x-mixed-replace. Specifically, with the headers in
+webcam-simulacrum.mjpg^headers^, the web browser will get the following:
+
+HTTP 200 OK
+Content-Type: multipart/x-mixed-replace;boundary=BOUNDARYOMG
+
+\-\-BOUNDARYOMG\r\n
+Content-Type: image/gif\r\n
+\r\n
+<contents of red.gif> (no newline)
+\-\-BOUNDARYOMG\r\n
+Content-Type: image/gif\r\n
+\r\n
+<contents of blue.gif> (no newline)
+\-\-BOUNDARYOMG\-\-\r\n
+
+(The boundary is arbitrary, and just has to be defined as something that
+won't be in the text of the contents themselves. \-\-$(boundary)\r\n means
+"Here is the beginning of a boundary," and \-\-$(boundary)\-\- means "All done
+sending you parts.")
+-->
+<head>
+ <title>Test for Bug 641748 - WebCam Simulacrum</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <script type="application/javascript" src="imgutils.js"></script>
+ <script type="application/javascript" src="animationPolling.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=641748">
+Mozilla Bug 641748: GIF decoder doesn't support multipart/x-mixed-replace
+</a>
+<p id="display"></p>
+
+<div id="content">
+ <div id="referenceDiv" style="height: 100px; width: 100px;
+ display: none; background: #0018ff;"></div>
+ <div id="animatedImage">
+ <img id="animatedGif" src="webcam-simulacrum.sjs" style="display: none; height: 100px; width: 100px;">
+ <div id="text-descr"></div>
+ </div>
+ <div id="debug" style="display:none">
+ </div>
+</div>
+<pre id="test">
+<script type="text/javascript">
+const FAILURE_TIMEOUT = 60000; // Fail early after 60 seconds
+
+function main()
+{
+ var animTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceDiv',
+ 'animatedGif', 'debug');
+ animTest.beginTest();
+}
+
+window.onload = main;
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/test_xultree_animation.xhtml b/image/test/mochitest/test_xultree_animation.xhtml
new file mode 100644
index 0000000000..71610c97ef
--- /dev/null
+++ b/image/test/mochitest/test_xultree_animation.xhtml
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html
+xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xml:lang="en" lang="en">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=666446
+-->
+<head>
+ <title>Test for Bug 666446 - Animated Images within SVG Filters</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <script type="application/javascript" src="imgutils.js"></script>
+ <script type="application/javascript" src="animationPolling.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=666446">
+Mozilla Bug 666446: lots of animated gifs swamp us with paint events
+</a>
+<p id="display"></p>
+<div id="content">
+ <xul:caption label="Bug 666446 - XULTree Test" />
+ <xul:separator />
+ <br />
+ <xul:window id="main" title="Bug 666446: XUL Tree Testing" width="100" height="100">
+ <xul:tree flex="1">
+ <xul:treecols>
+ <xul:treecol id="icon" label="Icon" flex="1" />
+ </xul:treecols>
+
+ <xul:treechildren>
+ <xul:treeitem id="referenceItem" hidden="true">
+ <xul:treerow>
+ <xul:treecell src="animated-gif-finalframe.gif" width="40" height="40" />
+ </xul:treerow>
+ </xul:treeitem>
+ <xul:treeitem id="imageItem" hidden="true">
+ <xul:treerow>
+ <xul:treecell src="animated-gif.gif" width="40" height="40" />
+ </xul:treerow>
+ </xul:treeitem>
+ </xul:treechildren>
+ </xul:tree>
+ </xul:window>
+ </div>
+<div id="debug" style="display:none"></div>
+<pre id="test">
+<script type="text/javascript">
+
+/** Test for Bug 666446 nsSVGFEImageElement/RasterImage**/
+
+const FAILURE_TIMEOUT = 5000; // Fail early after 120 seconds (2 minutes)
+
+function main() {
+ var animTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceItem',
+ 'imageItem', 'debug', '',
+ 'animated-gif-finalframe.gif', true);
+ animTest.beginTest();
+}
+
+window.onload = main;
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/image/test/mochitest/transparent.gif b/image/test/mochitest/transparent.gif
new file mode 100644
index 0000000000..48f5c7caf1
--- /dev/null
+++ b/image/test/mochitest/transparent.gif
Binary files differ
diff --git a/image/test/mochitest/transparent.png b/image/test/mochitest/transparent.png
new file mode 100644
index 0000000000..fc8002053a
--- /dev/null
+++ b/image/test/mochitest/transparent.png
Binary files differ
diff --git a/image/test/mochitest/webcam-simulacrum.sjs b/image/test/mochitest/webcam-simulacrum.sjs
new file mode 100644
index 0000000000..23872e675e
--- /dev/null
+++ b/image/test/mochitest/webcam-simulacrum.sjs
@@ -0,0 +1,51 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var counter = 2;
+var frames = ['red.gif', 'blue.gif'];
+var timer = Components.classes["@mozilla.org/timer;1"];
+var partTimer = timer.createInstance(Components.interfaces.nsITimer);
+
+function getFileAsInputStream(aFilename) {
+ var file = Components.classes["@mozilla.org/file/directory_service;1"]
+ .getService(Components.interfaces.nsIProperties)
+ .get("CurWorkD", Components.interfaces.nsIFile);
+
+ file.append("tests");
+ file.append("image");
+ file.append("test");
+ file.append("mochitest");
+ file.append(aFilename);
+
+ var fileStream = Components.classes['@mozilla.org/network/file-input-stream;1']
+ .createInstance(Components.interfaces.nsIFileInputStream);
+ fileStream.init(file, 1, 0, false);
+ return fileStream;
+}
+
+function handleRequest(request, response)
+{
+ response.setHeader("Content-Type",
+ "multipart/x-mixed-replace;boundary=BOUNDARYOMG", false);
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.processAsync();
+ response.write("--BOUNDARYOMG\r\n");
+ while (frames.length > 0) {
+ sendNextPart(response);
+ }
+ response.write("--BOUNDARYOMG--\r\n");
+ response.finish();
+}
+
+function sendNextPart(response) {
+ var nextPartHead = "Content-Type: image/gif\r\n\r\n";
+ var inputStream = getFileAsInputStream(frames.shift());
+ response.bodyOutputStream.write(nextPartHead, nextPartHead.length);
+ response.bodyOutputStream.writeFrom(inputStream, inputStream.available());
+ inputStream.close();
+ // Toss in the boundary, so the browser can know this part is complete
+ response.write("--BOUNDARYOMG\r\n");
+}
+