import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,214 @@
/*
* Test to ensure that image loading/decoding notifications are always
* delivered async, and in the order we expect.
*
* Must be included from a file that has a uri of the image to test defined in
* var uri.
*/
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
var Cr = Components.results;
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource://gre/modules/NetUtil.jsm");
var server = new HttpServer();
server.registerDirectory("/", do_get_file(''));
server.registerContentType("sjs", "sjs");
server.start(-1);
load('image_load_helpers.js');
var requests = [];
// Return a closure that holds on to the listener from the original
// imgIRequest, and compares its results to the cloned one.
function getCloneStopCallback(original_listener)
{
return function cloneStop(listener) {
do_check_eq(original_listener.state, listener.state);
// Sanity check to make sure we didn't accidentally use the same listener
// twice.
do_check_neq(original_listener, listener);
do_test_finished();
}
}
// Make sure that cloned requests get all the same callbacks as the original,
// but they aren't synchronous right now.
function checkClone(other_listener, aRequest)
{
do_test_pending();
// For as long as clone notification is synchronous, we can't test the clone state reliably.
var listener = new ImageListener(null, function(foo, bar) { do_test_finished(); } /*getCloneStopCallback(other_listener)*/);
listener.synchronous = false;
var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
.createScriptedObserver(listener);
var clone = aRequest.clone(outer);
requests.push(clone);
}
// Ensure that all the callbacks were called on aRequest.
function checkSizeAndLoad(listener, aRequest)
{
do_check_neq(listener.state & SIZE_AVAILABLE, 0);
do_check_neq(listener.state & LOAD_COMPLETE, 0);
do_test_finished();
}
function secondLoadDone(oldlistener, aRequest)
{
do_test_pending();
try {
var staticrequest = aRequest.getStaticRequest();
// For as long as clone notification is synchronous, we can't test the
// clone state reliably.
var listener = new ImageListener(null, checkSizeAndLoad);
listener.synchronous = false;
var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
.createScriptedObserver(listener);
var staticrequestclone = staticrequest.clone(outer);
requests.push(staticrequestclone);
} catch(e) {
// We can't create a static request. Most likely the request we started
// with didn't load successfully.
do_test_finished();
}
run_loadImageWithChannel_tests();
do_test_finished();
}
// Load the request a second time. This should come from the image cache, and
// therefore would be at most risk of being served synchronously.
function checkSecondLoad()
{
do_test_pending();
var listener = new ImageListener(checkClone, secondLoadDone);
var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
.createScriptedObserver(listener);
requests.push(gCurrentLoader.loadImageXPCOM(uri, null, null, "default", null, null, outer, null, 0, null));
listener.synchronous = false;
}
function firstLoadDone(oldlistener, aRequest)
{
checkSecondLoad(uri);
do_test_finished();
}
// Return a closure that allows us to check the stream listener's status when the
// image finishes loading.
function getChannelLoadImageStopCallback(streamlistener, next)
{
return function channelLoadStop(imglistener, aRequest) {
next();
do_test_finished();
}
}
// Load the request a second time. This should come from the image cache, and
// therefore would be at most risk of being served synchronously.
function checkSecondChannelLoad()
{
do_test_pending();
var channel = NetUtil.newChannel({uri: uri, loadUsingSystemPrincipal: true});
var channellistener = new ChannelListener();
channel.asyncOpen2(channellistener);
var listener = new ImageListener(null,
getChannelLoadImageStopCallback(channellistener,
all_done_callback));
var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
.createScriptedObserver(listener);
var outlistener = {};
requests.push(gCurrentLoader.loadImageWithChannelXPCOM(channel, outer, null, outlistener));
channellistener.outputListener = outlistener.value;
listener.synchronous = false;
}
function run_loadImageWithChannel_tests()
{
// To ensure we're testing what we expect to, create a new loader and cache.
gCurrentLoader = Cc["@mozilla.org/image/loader;1"].createInstance(Ci.imgILoader);
do_test_pending();
var channel = NetUtil.newChannel({uri: uri, loadUsingSystemPrincipal: true});
var channellistener = new ChannelListener();
channel.asyncOpen2(channellistener);
var listener = new ImageListener(null,
getChannelLoadImageStopCallback(channellistener,
checkSecondChannelLoad));
var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
.createScriptedObserver(listener);
var outlistener = {};
requests.push(gCurrentLoader.loadImageWithChannelXPCOM(channel, outer, null, outlistener));
channellistener.outputListener = outlistener.value;
listener.synchronous = false;
}
function all_done_callback()
{
server.stop(function() { do_test_finished(); });
}
function startImageCallback(otherCb)
{
return function(listener, request)
{
// Make sure we can load the same image immediately out of the cache.
do_test_pending();
var listener2 = new ImageListener(null, function(foo, bar) { do_test_finished(); });
var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
.createScriptedObserver(listener2);
requests.push(gCurrentLoader.loadImageXPCOM(uri, null, null, "default", null, null, outer, null, 0, null));
listener2.synchronous = false;
// Now that we've started another load, chain to the callback.
otherCb(listener, request);
}
}
var gCurrentLoader;
function cleanup()
{
for (var i = 0; i < requests.length; ++i) {
requests[i].cancelAndForgetObserver(0);
}
}
function run_test()
{
do_register_cleanup(cleanup);
gCurrentLoader = Cc["@mozilla.org/image/loader;1"].createInstance(Ci.imgILoader);
do_test_pending();
var listener = new ImageListener(startImageCallback(checkClone), firstLoadDone);
var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
.createScriptedObserver(listener);
var req = gCurrentLoader.loadImageXPCOM(uri, null, null, "default", null, null, outer, null, 0, null);
requests.push(req);
// Ensure that we don't cause any mayhem when we lock an image.
req.lockImage();
listener.synchronous = false;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
image/test/unit/image1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
image/test/unit/image2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 948 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 950 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 879 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

BIN
image/test/unit/image3.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
image/test/unit/image4.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 894 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View file

@ -0,0 +1,122 @@
/*
* Helper structures to track callbacks from image and channel loads.
*/
// START_REQUEST and STOP_REQUEST are used by ChannelListener, and
// stored in ChannelListener.requestStatus.
const START_REQUEST = 0x01;
const STOP_REQUEST = 0x02;
const DATA_AVAILABLE = 0x04;
// One bit per callback that imageListener below implements. Stored in
// ImageListener.state.
const SIZE_AVAILABLE = 0x01;
const FRAME_UPDATE = 0x02;
const FRAME_COMPLETE = 0x04;
const LOAD_COMPLETE = 0x08;
const DECODE_COMPLETE = 0x10;
// Safebrowsing requires that the profile dir is set.
do_get_profile();
// An implementation of imgIScriptedNotificationObserver with the ability to
// call specified functions on onStartRequest and onStopRequest.
function ImageListener(start_callback, stop_callback)
{
this.sizeAvailable = function onSizeAvailable(aRequest)
{
do_check_false(this.synchronous);
this.state |= SIZE_AVAILABLE;
if (this.start_callback)
this.start_callback(this, aRequest);
}
this.frameComplete = function onFrameComplete(aRequest)
{
do_check_false(this.synchronous);
this.state |= FRAME_COMPLETE;
}
this.decodeComplete = function onDecodeComplete(aRequest)
{
do_check_false(this.synchronous);
this.state |= DECODE_COMPLETE;
}
this.loadComplete = function onLoadcomplete(aRequest)
{
do_check_false(this.synchronous);
this.state |= LOAD_COMPLETE;
if (this.stop_callback)
this.stop_callback(this, aRequest);
}
this.frameUpdate = function onFrameUpdate(aRequest)
{
}
this.isAnimated = function onIsAnimated()
{
}
// Initialize the synchronous flag to true to start. This must be set to
// false before exiting to the event loop!
this.synchronous = true;
// A function to call when onStartRequest is called.
this.start_callback = start_callback;
// A function to call when onStopRequest is called.
this.stop_callback = stop_callback;
// The image load/decode state.
// A bitfield that tracks which callbacks have been called. Takes the bits
// defined above.
this.state = 0;
}
function NS_FAILED(val)
{
return !!(val & 0x80000000);
}
function ChannelListener()
{
this.onStartRequest = function onStartRequest(aRequest, aContext)
{
if (this.outputListener)
this.outputListener.onStartRequest(aRequest, aContext);
this.requestStatus |= START_REQUEST;
}
this.onDataAvailable = function onDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount)
{
if (this.outputListener)
this.outputListener.onDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount);
this.requestStatus |= DATA_AVAILABLE;
}
this.onStopRequest = function onStopRequest(aRequest, aContext, aStatusCode)
{
if (this.outputListener)
this.outputListener.onStopRequest(aRequest, aContext, aStatusCode);
// If we failed (or were canceled - failure is implied if canceled),
// there's no use tracking our state, since it's meaningless.
if (NS_FAILED(aStatusCode))
this.requestStatus = 0;
else
this.requestStatus |= STOP_REQUEST;
}
// A listener to pass the notifications we get to.
this.outputListener = null;
// The request's status. A bitfield that holds one or both of START_REQUEST
// and STOP_REQUEST, according to which callbacks have been called on the
// associated request.
this.requestStatus = 0;
}

View file

@ -0,0 +1,10 @@
/*
* Test for asynchronous image load/decode notifications in the case that the image load works.
*/
// A simple 3x3 png; rows go red, green, blue. Stolen from the PNG encoder test.
var pngspec = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII=";
var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
var uri = ioService.newURI(pngspec, null, null);
load('async_load_tests.js');

View file

@ -0,0 +1,16 @@
/*
* Test to ensure that load/decode notifications are delivered completely and
* asynchronously when dealing with a file that's a 404.
*/
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
var ioService = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
XPCOMUtils.defineLazyGetter(this, "uri", function() {
return ioService.newURI("http://localhost:" +
server.identity.primaryPort +
"/async-notification-never-here.jpg", null, null);
});
load('async_load_tests.js');

View file

@ -0,0 +1,14 @@
/*
* Test for asynchronous image load/decode notifications in the case that the
* image load works, but for an animated image.
*
* If this fails because a request wasn't cancelled, it's possible that
* imgContainer::ExtractFrame didn't set the new image's status correctly.
*/
// transparent-animation.gif from the gif reftests.
var spec = "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=";
var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
var uri = ioService.newURI(spec, null, null);
load('async_load_tests.js');

View file

@ -0,0 +1,470 @@
/*
* Test for APNG encoding in ImageLib
*
*/
var Ci = Components.interfaces;
var Cc = Components.classes;
// dispose=[none|background|previous]
// blend=[source|over]
var apng1A = {
// A 3x3 image with 3 frames, alternating red, green, blue. RGB format.
width : 3, height : 3, skipFirstFrame : false,
format : Ci.imgIEncoder.INPUT_FORMAT_RGB,
transparency : null,
plays : 0,
frames : [
{ // frame #1
width : 3, height : 3,
x_offset : 0, y_offset : 0,
dispose : "none", blend : "source", delay : 500,
format : Ci.imgIEncoder.INPUT_FORMAT_RGB, stride : 9,
transparency : null,
pixels : [
255,0,0, 255,0,0, 255,0,0,
255,0,0, 255,0,0, 255,0,0,
255,0,0, 255,0,0, 255,0,0
]
},
{ // frame #2
width : 3, height : 3,
x_offset : 0, y_offset : 0,
dispose : "none", blend : "source", delay : 500,
format : Ci.imgIEncoder.INPUT_FORMAT_RGB, stride : 9,
transparency : null,
pixels : [
0,255,0, 0,255,0, 0,255,0,
0,255,0, 0,255,0, 0,255,0,
0,255,0, 0,255,0, 0,255,0
]
},
{ // frame #3
width : 3, height : 3,
x_offset : 0, y_offset : 0,
dispose : "none", blend : "source", delay : 500,
format : Ci.imgIEncoder.INPUT_FORMAT_RGB, stride : 9,
transparency : null,
pixels : [
0,0,255, 0,0,255, 0,0,255,
0,0,255, 0,0,255, 0,0,255,
0,0,255, 0,0,255, 0,0,255
]
}
],
expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAACGFjVEwAAAADAAAAAM7tusAAAAAaZmNUTAAAAAAAAAADAAAAAwAAAAAAAAAAAfQD6AAAdRYgGAAAABBJREFUCJlj+M/AAEEMWFgAj44I+H2CySsAAAAaZmNUTAAAAAEAAAADAAAAAwAAAAAAAAAAAfQD6AAA7mXKzAAAABNmZEFUAAAAAgiZY2D4zwBFWFgAhpcI+I731VcAAAAaZmNUTAAAAAMAAAADAAAAAwAAAAAAAAAAAfQD6AAAA/MZJQAAABNmZEFUAAAABAiZY2Bg+A9DmCwAfaAI+AGmQVoAAAAASUVORK5CYII="
};
var apng1B = {
// A 3x3 image with 3 frames, alternating red, green, blue. RGBA format.
width : 3, height : 3, skipFirstFrame : false,
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA,
transparency : null,
plays : 0,
frames : [
{ // frame #1
width : 3, height : 3,
x_offset : 0, y_offset : 0,
dispose : "none", blend : "source", delay : 500,
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
pixels : [
255,0,0,255, 255,0,0,255, 255,0,0,255,
255,0,0,255, 255,0,0,255, 255,0,0,255,
255,0,0,255, 255,0,0,255, 255,0,0,255
]
},
{ // frame #2
width : 3, height : 3,
x_offset : 0, y_offset : 0,
dispose : "none", blend : "source", delay : 500,
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
pixels : [
0,255,0,255, 0,255,0,255, 0,255,0,255,
0,255,0,255, 0,255,0,255, 0,255,0,255,
0,255,0,255, 0,255,0,255, 0,255,0,255
]
},
{ // frame #3
width : 3, height : 3,
x_offset : 0, y_offset : 0,
dispose : "none", blend : "source", delay : 500,
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
pixels : [
0,0,255,255, 0,0,255,255, 0,0,255,255,
0,0,255,255, 0,0,255,255, 0,0,255,255,
0,0,255,255, 0,0,255,255, 0,0,255,255
]
}
],
expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAACGFjVEwAAAADAAAAAM7tusAAAAAaZmNUTAAAAAAAAAADAAAAAwAAAAAAAAAAAfQD6AAAdRYgGAAAABFJREFUCJlj+M/A8B+GGXByAF3XEe/CoiJ1AAAAGmZjVEwAAAABAAAAAwAAAAMAAAAAAAAAAAH0A+gAAO5lyswAAAASZmRBVAAAAAIImWNg+I8EcXIAVOAR77Vyl9QAAAAaZmNUTAAAAAMAAAADAAAAAwAAAAAAAAAAAfQD6AAAA/MZJQAAABRmZEFUAAAABAiZY2Bg+P8fgXFxAEvpEe8rClxSAAAAAElFTkSuQmCC"
};
var apng1C = {
// A 3x3 image with 3 frames, alternating red, green, blue. RGBA format.
// The first frame is skipped, so it will only flash green/blue (or static red in an APNG-unaware viewer)
width : 3, height : 3, skipFirstFrame : true,
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA,
transparency : null,
plays : 0,
frames : [
{ // frame #1
width : 3, height : 3,
x_offset : 0, y_offset : 0,
dispose : "none", blend : "source", delay : 500,
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
pixels : [
255,0,0,255, 255,0,0,255, 255,0,0,255,
255,0,0,255, 255,0,0,255, 255,0,0,255,
255,0,0,255, 255,0,0,255, 255,0,0,255
]
},
{ // frame #2
width : 3, height : 3,
x_offset : 0, y_offset : 0,
dispose : "none", blend : "source", delay : 500,
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
pixels : [
0,255,0,255, 0,255,0,255, 0,255,0,255,
0,255,0,255, 0,255,0,255, 0,255,0,255,
0,255,0,255, 0,255,0,255, 0,255,0,255
]
},
{ // frame #3
width : 3, height : 3,
x_offset : 0, y_offset : 0,
dispose : "none", blend : "source", delay : 500,
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
pixels : [
0,0,255,255, 0,0,255,255, 0,0,255,255,
0,0,255,255, 0,0,255,255, 0,0,255,255,
0,0,255,255, 0,0,255,255, 0,0,255,255
]
}
],
expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAACGFjVEwAAAACAAAAAPONk3AAAAARSURBVAiZY/jPwPAfhhlwcgBd1xHvwqIidQAAABpmY1RMAAAAAAAAAAMAAAADAAAAAAAAAAAB9APoAAB1FiAYAAAAEmZkQVQAAAABCJljYPiPBHFyAFTgEe+kD/2tAAAAGmZjVEwAAAACAAAAAwAAAAMAAAAAAAAAAAH0A+gAAJiA8/EAAAAUZmRBVAAAAAMImWNgYPj/H4FxcQBL6RHvC5ggGQAAAABJRU5ErkJggg=="
};
var apng2A = {
// A 3x3 image with 3 frames, alternating red, green, blue. RGBA format.
// blend = over mode
// (The green frame is a horizontal gradient, and the blue frame is a
// vertical gradient. They stack as they animate.)
width : 3, height : 3, skipFirstFrame : false,
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA,
transparency : null,
plays : 0,
frames : [
{ // frame #1
width : 3, height : 3,
x_offset : 0, y_offset : 0,
dispose : "none", blend : "source", delay : 500,
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
pixels : [
255,0,0,255, 255,0,0,255, 255,0,0,255,
255,0,0,255, 255,0,0,255, 255,0,0,255,
255,0,0,255, 255,0,0,255, 255,0,0,255
]
},
{ // frame #2
width : 3, height : 3,
x_offset : 0, y_offset : 0,
dispose : "none", blend : "over", delay : 500,
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
pixels : [
0,255,0,255, 0,255,0,180, 0,255,0,75,
0,255,0,255, 0,255,0,180, 0,255,0,75,
0,255,0,255, 0,255,0,180, 0,255,0,75
]
},
{ // frame #3
width : 3, height : 3,
x_offset : 0, y_offset : 0,
dispose : "none", blend : "over", delay : 500,
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
pixels : [
0,0,255,75, 0,0,255,75, 0,0,255,75,
0,0,255,180, 0,0,255,180, 0,0,255,180,
0,0,255,255, 0,0,255,255, 0,0,255,255
]
}
],
expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAACGFjVEwAAAADAAAAAM7tusAAAAAaZmNUTAAAAAAAAAADAAAAAwAAAAAAAAAAAfQD6AAAdRYgGAAAABFJREFUCJlj+M/A8B+GGXByAF3XEe/CoiJ1AAAAGmZjVEwAAAABAAAAAwAAAAMAAAAAAAAAAAH0A+gAAZli+loAAAAcZmRBVAAAAAIImWNg+M/wn+E/wxaG/wzeDDg5ACeGDvKVa3pyAAAAGmZjVEwAAAADAAAAAwAAAAMAAAAAAAAAAAH0A+gAAXT0KbMAAAAcZmRBVAAAAAQImWNgYPjvjcAM/7cgMMP//zAMAPqkDvLn1m3SAAAAAElFTkSuQmCC"
};
var apng2B = {
// A 3x3 image with 3 frames, alternating red, green, blue. RGBA format.
// blend = over, dispose = background
// (The green frame is a horizontal gradient, and the blue frame is a
// vertical gradient. Each frame is displayed individually, blended to
// whatever the background is.)
width : 3, height : 3, skipFirstFrame : false,
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA,
transparency : null,
plays : 0,
frames : [
{ // frame #1
width : 3, height : 3,
x_offset : 0, y_offset : 0,
dispose : "background", blend : "source", delay : 500,
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
pixels : [
255,0,0,255, 255,0,0,255, 255,0,0,255,
255,0,0,255, 255,0,0,255, 255,0,0,255,
255,0,0,255, 255,0,0,255, 255,0,0,255
]
},
{ // frame #2
width : 3, height : 3,
x_offset : 0, y_offset : 0,
dispose : "background", blend : "over", delay : 500,
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
pixels : [
0,255,0,255, 0,255,0,180, 0,255,0,75,
0,255,0,255, 0,255,0,180, 0,255,0,75,
0,255,0,255, 0,255,0,180, 0,255,0,75
]
},
{ // frame #3
width : 3, height : 3,
x_offset : 0, y_offset : 0,
dispose : "background", blend : "over", delay : 500,
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
pixels : [
0,0,255,75, 0,0,255,75, 0,0,255,75,
0,0,255,180, 0,0,255,180, 0,0,255,180,
0,0,255,255, 0,0,255,255, 0,0,255,255
]
}
],
expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAACGFjVEwAAAADAAAAAM7tusAAAAAaZmNUTAAAAAAAAAADAAAAAwAAAAAAAAAAAfQD6AEAbA0RWQAAABFJREFUCJlj+M/A8B+GGXByAF3XEe/CoiJ1AAAAGmZjVEwAAAABAAAAAwAAAAMAAAAAAAAAAAH0A+gBAYB5yxsAAAAcZmRBVAAAAAIImWNg+M/wn+E/wxaG/wzeDDg5ACeGDvKVa3pyAAAAGmZjVEwAAAADAAAAAwAAAAMAAAAAAAAAAAH0A+gBAW3vGPIAAAAcZmRBVAAAAAQImWNgYPjvjcAM/7cgMMP//zAMAPqkDvLn1m3SAAAAAElFTkSuQmCC"
};
var apng3 = {
// A 3x3 image with 4 frames. First frame is white, then 1x1 frames draw a diagonal line
width : 3, height : 3, skipFirstFrame : false,
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA,
transparency : null,
plays : 0,
frames : [
{ // frame #1
width : 3, height : 3,
x_offset : 0, y_offset : 0,
dispose : "none", blend : "source", delay : 500,
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
pixels : [
255,255,255,255, 255,255,255,255, 255,255,255,255,
255,255,255,255, 255,255,255,255, 255,255,255,255,
255,255,255,255, 255,255,255,255, 255,255,255,255
]
},
{ // frame #2
width : 1, height : 1,
x_offset : 0, y_offset : 0,
dispose : "none", blend : "source", delay : 500,
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
pixels : [
0,0,0,255
]
},
{ // frame #3
width : 1, height : 1,
x_offset : 1, y_offset : 1,
dispose : "none", blend : "source", delay : 500,
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
pixels : [
0,0,0,255
]
},
{ // frame #4
width : 1, height : 1,
x_offset : 2, y_offset : 2,
dispose : "none", blend : "source", delay : 500,
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
pixels : [
0,0,0,255
]
}
],
expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAACGFjVEwAAAAEAAAAAHzNZtAAAAAaZmNUTAAAAAAAAAADAAAAAwAAAAAAAAAAAfQD6AAAdRYgGAAAAA5JREFUCJlj+I8EGHByALuHI91kueRqAAAAGmZjVEwAAAABAAAAAQAAAAEAAAAAAAAAAAH0A+gAADJXfawAAAARZmRBVAAAAAIImWNgYGD4DwABBAEAbr5mpgAAABpmY1RMAAAAAwAAAAEAAAABAAAAAQAAAAEB9APoAAC4OHoxAAAAEWZkQVQAAAAECJljYGBg+A8AAQQBAJZ8LRAAAAAaZmNUTAAAAAUAAAABAAAAAQAAAAIAAAACAfQD6AAA/fh01wAAABFmZEFUAAAABgiZY2BgYPgPAAEEAQB3Eum9AAAAAElFTkSuQmCC"
};
// Main test entry point.
function run_test() {
dump("Checking apng1A...\n");
run_test_for(apng1A);
dump("Checking apng1B...\n");
run_test_for(apng1B);
dump("Checking apng1C...\n");
run_test_for(apng1C);
dump("Checking apng2A...\n");
run_test_for(apng2A);
dump("Checking apng2B...\n");
run_test_for(apng2B);
dump("Checking apng3...\n");
run_test_for(apng3);
};
function run_test_for(input) {
var encoder, dataURL;
encoder = encodeImage(input);
dataURL = makeDataURL(encoder, "image/png");
do_check_eq(dataURL, input.expected);
};
function encodeImage(input) {
var encoder = Cc["@mozilla.org/image/encoder;2?type=image/png"].createInstance();
encoder.QueryInterface(Ci.imgIEncoder);
var options = "";
if (input.transparency) { options += "transparency=" + input.transparency; }
options += ";frames=" + input.frames.length;
options += ";skipfirstframe=" + (input.skipFirstFrame ? "yes" : "no");
options += ";plays=" + input.plays;
encoder.startImageEncode(input.width, input.height, input.format, options);
for (var i = 0; i < input.frames.length; i++) {
var frame = input.frames[i];
options = "";
if (frame.transparency) { options += "transparency=" + input.transparency; }
options += ";delay=" + frame.delay;
options += ";dispose=" + frame.dispose;
options += ";blend=" + frame.blend;
if (frame.x_offset > 0) { options += ";xoffset=" + frame.x_offset; }
if (frame.y_offset > 0) { options += ";yoffset=" + frame.y_offset; }
encoder.addImageFrame(frame.pixels, frame.pixels.length,
frame.width, frame.height, frame.stride, frame.format, options);
}
encoder.endImageEncode();
return encoder;
}
function makeDataURL(encoder, mimetype) {
var rawStream = encoder.QueryInterface(Ci.nsIInputStream);
var stream = Cc["@mozilla.org/binaryinputstream;1"].createInstance();
stream.QueryInterface(Ci.nsIBinaryInputStream);
stream.setInputStream(rawStream);
var bytes = stream.readByteArray(stream.available()); // returns int[]
var base64String = toBase64(bytes);
return "data:" + mimetype + ";base64," + base64String;
}
/* toBase64 copied from extensions/xml-rpc/src/nsXmlRpcClient.js */
/* Convert data (an array of integers) to a Base64 string. */
const toBase64Table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' +
'0123456789+/';
const base64Pad = '=';
function toBase64(data) {
var result = '';
var length = data.length;
var i;
// Convert every three bytes to 4 ascii characters.
for (i = 0; i < (length - 2); i += 3) {
result += toBase64Table[data[i] >> 2];
result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
result += toBase64Table[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)];
result += toBase64Table[data[i+2] & 0x3f];
}
// Convert the remaining 1 or 2 bytes, pad out to 4 characters.
if (length%3) {
i = length - (length%3);
result += toBase64Table[data[i] >> 2];
if ((length%3) == 2) {
result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
result += toBase64Table[(data[i+1] & 0x0f) << 2];
result += base64Pad;
} else {
result += toBase64Table[(data[i] & 0x03) << 4];
result += base64Pad + base64Pad;
}
}
return result;
}

View file

@ -0,0 +1,256 @@
/*
* Test for PNG encoding in ImageLib
*
*/
var Ci = Components.interfaces;
var Cc = Components.classes;
var png1A = {
// A 3x3 image, rows are red, green, blue.
// RGB format, transparency defaults.
transparency : null,
frames : [
{
width : 3, height : 3,
format : Ci.imgIEncoder.INPUT_FORMAT_RGB, stride : 9,
pixels : [
255,0,0, 255,0,0, 255,0,0,
0,255,0, 0,255,0, 0,255,0,
0,0,255, 0,0,255, 0,0,255,
]
}
],
expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII="
};
var png1B = {
// A 3x3 image, rows are red, green, blue.
// RGB format, transparency=none.
transparency : "none",
frames : [
{
width : 3, height : 3,
format : Ci.imgIEncoder.INPUT_FORMAT_RGB, stride : 9,
pixels : [
255,0,0, 255,0,0, 255,0,0,
0,255,0, 0,255,0, 0,255,0,
0,0,255, 0,0,255, 0,0,255,
]
}
],
expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII="
};
var png2A = {
// A 3x3 image, rows are: red, green, blue. Columns are: 0%, 33%, 66% transparent.
transparency : null,
frames : [
{
width : 3, height : 3,
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
pixels : [
255,0,0,255, 255,0,0,170, 255,0,0,85,
0,255,0,255, 0,255,0,170, 0,255,0,85,
0,0,255,255, 0,0,255,170, 0,0,255,85
]
}
],
expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAAIElEQVQImSXJMQEAMAwEIUy+yZi8DmVFFBcjycn86GgPJw4O8v9DkkEAAAAASUVORK5CYII="
};
var png2B = {
// A 3x3 image, rows are: red, green, blue. Columns are: 0%, 33%, 66% transparent,
// but transparency will be ignored.
transparency : "none",
frames : [
{
width : 3, height : 3,
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
pixels : [
255,0,0,255, 255,0,0,170, 255,0,0,85,
0,255,0,255, 0,255,0,170, 0,255,0,85,
0,0,255,255, 0,0,255,170, 0,0,255,85
]
}
],
expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII="
};
// Main test entry point.
function run_test() {
dump("Checking png1A...\n")
run_test_for(png1A);
dump("Checking png1B...\n")
run_test_for(png1B);
dump("Checking png2A...\n")
run_test_for(png2A);
dump("Checking png2B...\n")
run_test_for(png2B);
};
function run_test_for(input) {
var encoder, dataURL;
encoder = encodeImage(input);
dataURL = makeDataURL(encoder, "image/png");
do_check_eq(dataURL, input.expected);
encoder = encodeImageAsync(input);
dataURL = makeDataURLFromAsync(encoder, "image/png", input.expected);
};
function encodeImage(input) {
var encoder = Cc["@mozilla.org/image/encoder;2?type=image/png"].createInstance();
encoder.QueryInterface(Ci.imgIEncoder);
var options = "";
if (input.transparency) {
options += "transparency=" + input.transparency;
}
var frame = input.frames[0];
encoder.initFromData(frame.pixels, frame.pixels.length,
frame.width, frame.height, frame.stride,
frame.format, options);
return encoder;
}
function _encodeImageAsyncFactory(frame, options, encoder)
{
function finishEncode() {
encoder.addImageFrame(frame.pixels, frame.pixels.length,
frame.width, frame.height, frame.stride,
frame.format, options);
encoder.endImageEncode();
}
return finishEncode;
}
function encodeImageAsync(input)
{
var encoder = Cc["@mozilla.org/image/encoder;2?type=image/png"].createInstance();
encoder.QueryInterface(Ci.imgIEncoder);
var options = "";
if (input.transparency) {
options += "transparency=" + input.transparency;
}
var frame = input.frames[0];
encoder.startImageEncode(frame.width, frame.height,
frame.format, options);
do_timeout(50, _encodeImageAsyncFactory(frame, options, encoder));
return encoder;
}
function makeDataURL(encoder, mimetype) {
var rawStream = encoder.QueryInterface(Ci.nsIInputStream);
var stream = Cc["@mozilla.org/binaryinputstream;1"].createInstance();
stream.QueryInterface(Ci.nsIBinaryInputStream);
stream.setInputStream(rawStream);
var bytes = stream.readByteArray(stream.available()); // returns int[]
var base64String = toBase64(bytes);
return "data:" + mimetype + ";base64," + base64String;
}
function makeDataURLFromAsync(encoder, mimetype, expected) {
do_test_pending();
var rawStream = encoder.QueryInterface(Ci.nsIAsyncInputStream);
var currentThread = Cc["@mozilla.org/thread-manager;1"].getService().currentThread;
var bytes = [];
var binarystream = Cc["@mozilla.org/binaryinputstream;1"].createInstance();
binarystream.QueryInterface(Ci.nsIBinaryInputStream);
var asyncReader =
{
onInputStreamReady: function(stream)
{
binarystream.setInputStream(stream);
var available = 0;
try {
available = stream.available();
} catch(e) { }
if (available > 0)
{
bytes = bytes.concat(binarystream.readByteArray(available));
stream.asyncWait(this, 0, 0, currentThread);
} else {
var base64String = toBase64(bytes);
var dataURL = "data:" + mimetype + ";base64," + base64String;
do_check_eq(dataURL, expected);
do_test_finished();
}
}
};
rawStream.asyncWait(asyncReader, 0, 0, currentThread);
}
/* toBase64 copied from extensions/xml-rpc/src/nsXmlRpcClient.js */
/* Convert data (an array of integers) to a Base64 string. */
const toBase64Table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' +
'0123456789+/';
const base64Pad = '=';
function toBase64(data) {
var result = '';
var length = data.length;
var i;
// Convert every three bytes to 4 ascii characters.
for (i = 0; i < (length - 2); i += 3) {
result += toBase64Table[data[i] >> 2];
result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
result += toBase64Table[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)];
result += toBase64Table[data[i+2] & 0x3f];
}
// Convert the remaining 1 or 2 bytes, pad out to 4 characters.
if (length%3) {
i = length - (length%3);
result += toBase64Table[data[i] >> 2];
if ((length%3) == 2) {
result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
result += toBase64Table[(data[i+1] & 0x0f) << 2];
result += base64Pad;
} else {
result += toBase64Table[(data[i] & 0x03) << 4];
result += base64Pad + base64Pad;
}
}
return result;
}

View file

@ -0,0 +1,737 @@
/*
* Tests for imgITools
*/
var Ci = Components.interfaces;
var Cc = Components.classes;
/*
* dumpToFile()
*
* For test development, dumps the specified array to a file.
* Call |dumpToFile(outData);| in a test to file to a file.
*/
function dumpToFile(aData) {
var outputFile = do_get_tempdir();
outputFile.append("testdump.png");
var outputStream = Cc["@mozilla.org/network/file-output-stream;1"].
createInstance(Ci.nsIFileOutputStream);
// WR_ONLY|CREAT|TRUNC
outputStream.init(outputFile, 0x02 | 0x08 | 0x20, 0o644, null);
var bos = Cc["@mozilla.org/binaryoutputstream;1"].
createInstance(Ci.nsIBinaryOutputStream);
bos.setOutputStream(outputStream);
bos.writeByteArray(aData, aData.length);
outputStream.close();
}
/*
* getFileInputStream()
*
* Returns an input stream for the specified file.
*/
function getFileInputStream(aFile) {
var inputStream = Cc["@mozilla.org/network/file-input-stream;1"].
createInstance(Ci.nsIFileInputStream);
// init the stream as RD_ONLY, -1 == default permissions.
inputStream.init(aFile, 0x01, -1, null);
// Blah. The image decoders use ReadSegments, which isn't implemented on
// file input streams. Use a buffered stream to make it work.
var bis = Cc["@mozilla.org/network/buffered-input-stream;1"].
createInstance(Ci.nsIBufferedInputStream);
bis.init(inputStream, 1024);
return bis;
}
/*
* streamToArray()
*
* Consumes an input stream, and returns its bytes as an array.
*/
function streamToArray(aStream) {
var size = aStream.available();
// use a binary input stream to grab the bytes.
var bis = Cc["@mozilla.org/binaryinputstream;1"].
createInstance(Ci.nsIBinaryInputStream);
bis.setInputStream(aStream);
var bytes = bis.readByteArray(size);
if (size != bytes.length)
throw "Didn't read expected number of bytes";
return bytes;
}
/*
* compareArrays
*
* Compares two arrays, and throws if there's a difference.
*/
function compareArrays(aArray1, aArray2) {
do_check_eq(aArray1.length, aArray2.length);
for (var i = 0; i < aArray1.length; i++)
if (aArray1[i] != aArray2[i])
throw "arrays differ at index " + i;
}
/*
* checkExpectedError
*
* Checks to see if a thrown error was expected or not, and if it
* matches the expected value.
*/
function checkExpectedError (aExpectedError, aActualError) {
if (aExpectedError) {
if (!aActualError)
throw "Didn't throw as expected (" + aExpectedError + ")";
if (!aExpectedError.test(aActualError))
throw "Threw (" + aActualError + "), not (" + aExpectedError;
// We got the expected error, so make a note in the test log.
dump("...that error was expected.\n\n");
} else if (aActualError) {
throw "Threw unexpected error: " + aActualError;
}
}
function run_test() {
try {
/* ========== 0 ========== */
var testnum = 0;
var testdesc = "imgITools setup";
var err = null;
var imgTools = Cc["@mozilla.org/image/tools;1"].
getService(Ci.imgITools);
if (!imgTools)
throw "Couldn't get imgITools service"
// Ugh, this is an ugly hack. The pixel values we get in Windows are sometimes
// +/- 1 value compared to other platforms, so we need to compare against a
// different set of reference images. nsIXULRuntime.OS doesn't seem to be
// available in xpcshell, so we'll use this as a kludgy way to figure out if
// we're running on Windows.
var isWindows = mozinfo.os == "win";
/* ========== 1 ========== */
testnum++;
testdesc = "test decoding a PNG";
// 64x64 png, 8415 bytes.
var imgName = "image1.png";
var inMimeType = "image/png";
var imgFile = do_get_file(imgName);
var istream = getFileInputStream(imgFile);
do_check_eq(istream.available(), 8415);
// Use decodeImageData for this test even though it's deprecated to ensure that
// it correctly forwards to decodeImage and continues to work.
var outParam = { value: null };
imgTools.decodeImageData(istream, inMimeType, outParam);
var container = outParam.value;
// It's not easy to look at the pixel values from JS, so just
// check the container's size.
do_check_eq(container.width, 64);
do_check_eq(container.height, 64);
/* ========== 2 ========== */
testnum++;
testdesc = "test encoding a scaled JPEG";
// we'll reuse the container from the previous test
istream = imgTools.encodeScaledImage(container, "image/jpeg", 16, 16);
var encodedBytes = streamToArray(istream);
// Get bytes for exected result
var refName = "image1png16x16.jpg";
var refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
do_check_eq(istream.available(), 1051);
var referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 3 ========== */
testnum++;
testdesc = "test encoding an unscaled JPEG";
// we'll reuse the container from the previous test
istream = imgTools.encodeImage(container, "image/jpeg");
encodedBytes = streamToArray(istream);
// Get bytes for exected result
refName = "image1png64x64.jpg";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
do_check_eq(istream.available(), 4503);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 4 ========== */
testnum++;
testdesc = "test decoding a JPEG";
// 32x32 jpeg, 3494 bytes.
imgName = "image2.jpg";
inMimeType = "image/jpeg";
imgFile = do_get_file(imgName);
istream = getFileInputStream(imgFile);
do_check_eq(istream.available(), 3494);
container = imgTools.decodeImage(istream, inMimeType);
// It's not easy to look at the pixel values from JS, so just
// check the container's size.
do_check_eq(container.width, 32);
do_check_eq(container.height, 32);
/* ========== 5 ========== */
testnum++;
testdesc = "test encoding a scaled PNG";
if (!isWindows) {
// we'll reuse the container from the previous test
istream = imgTools.encodeScaledImage(container, "image/png", 16, 16);
encodedBytes = streamToArray(istream);
// Get bytes for exected result
refName = isWindows ? "image2jpg16x16-win.png" : "image2jpg16x16.png";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
do_check_eq(istream.available(), 950);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
}
/* ========== 6 ========== */
testnum++;
testdesc = "test encoding an unscaled PNG";
if (!isWindows) {
// we'll reuse the container from the previous test
istream = imgTools.encodeImage(container, "image/png");
encodedBytes = streamToArray(istream);
// Get bytes for exected result
refName = isWindows ? "image2jpg32x32-win.png" : "image2jpg32x32.png";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
do_check_eq(istream.available(), 3105);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
}
/* ========== 7 ========== */
testnum++;
testdesc = "test decoding a ICO";
// 16x16 ico, 1406 bytes.
imgName = "image3.ico";
inMimeType = "image/x-icon";
imgFile = do_get_file(imgName);
istream = getFileInputStream(imgFile);
do_check_eq(istream.available(), 1406);
container = imgTools.decodeImage(istream, inMimeType);
// It's not easy to look at the pixel values from JS, so just
// check the container's size.
do_check_eq(container.width, 16);
do_check_eq(container.height, 16);
/* ========== 8 ========== */
testnum++;
testdesc = "test encoding a scaled PNG"; // note that we're scaling UP
// we'll reuse the container from the previous test
istream = imgTools.encodeScaledImage(container, "image/png", 32, 32);
encodedBytes = streamToArray(istream);
// Get bytes for exected result
refName = "image3ico32x32.png";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
do_check_eq(istream.available(), 2285);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 9 ========== */
testnum++;
testdesc = "test encoding an unscaled PNG";
// we'll reuse the container from the previous test
istream = imgTools.encodeImage(container, "image/png");
encodedBytes = streamToArray(istream);
// Get bytes for exected result
refName = "image3ico16x16.png";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
do_check_eq(istream.available(), 330);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 10 ========== */
testnum++;
testdesc = "test decoding a GIF";
// 32x32 gif, 1809 bytes.
imgName = "image4.gif";
inMimeType = "image/gif";
imgFile = do_get_file(imgName);
istream = getFileInputStream(imgFile);
do_check_eq(istream.available(), 1809);
container = imgTools.decodeImage(istream, inMimeType);
// It's not easy to look at the pixel values from JS, so just
// check the container's size.
do_check_eq(container.width, 32);
do_check_eq(container.height, 32);
/* ========== 11 ========== */
testnum++;
testdesc = "test encoding an unscaled ICO with format options " +
"(format=bmp;bpp=32)";
// we'll reuse the container from the previous test
istream = imgTools.encodeImage(container,
"image/vnd.microsoft.icon",
"format=bmp;bpp=32");
encodedBytes = streamToArray(istream);
// Get bytes for exected result
refName = "image4gif32x32bmp32bpp.ico";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
do_check_eq(istream.available(), 4286);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 12 ========== */
testnum++;
testdesc = "test encoding a scaled ICO with format options " +
"(format=bmp;bpp=32)";
// we'll reuse the container from the previous test
istream = imgTools.encodeScaledImage(container,
"image/vnd.microsoft.icon",
16,
16,
"format=bmp;bpp=32");
encodedBytes = streamToArray(istream);
// Get bytes for exected result
refName = "image4gif16x16bmp32bpp.ico";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
do_check_eq(istream.available(), 1150);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 13 ========== */
testnum++;
testdesc = "test encoding an unscaled ICO with format options " +
"(format=bmp;bpp=24)";
// we'll reuse the container from the previous test
istream = imgTools.encodeImage(container,
"image/vnd.microsoft.icon",
"format=bmp;bpp=24");
encodedBytes = streamToArray(istream);
// Get bytes for exected result
refName = "image4gif32x32bmp24bpp.ico";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
do_check_eq(istream.available(), 3262);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 14 ========== */
testnum++;
testdesc = "test encoding a scaled ICO with format options " +
"(format=bmp;bpp=24)";
// we'll reuse the container from the previous test
istream = imgTools.encodeScaledImage(container,
"image/vnd.microsoft.icon",
16,
16,
"format=bmp;bpp=24");
encodedBytes = streamToArray(istream);
// Get bytes for exected result
refName = "image4gif16x16bmp24bpp.ico";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
do_check_eq(istream.available(), 894);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 15 ========== */
testnum++;
testdesc = "test cropping a JPG";
// 32x32 jpeg, 3494 bytes.
imgName = "image2.jpg";
inMimeType = "image/jpeg";
imgFile = do_get_file(imgName);
istream = getFileInputStream(imgFile);
do_check_eq(istream.available(), 3494);
container = imgTools.decodeImage(istream, inMimeType);
// It's not easy to look at the pixel values from JS, so just
// check the container's size.
do_check_eq(container.width, 32);
do_check_eq(container.height, 32);
// encode a cropped image
istream = imgTools.encodeCroppedImage(container, "image/jpeg", 0, 0, 16, 16);
encodedBytes = streamToArray(istream);
// Get bytes for exected result
refName = "image2jpg16x16cropped.jpg";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
do_check_eq(istream.available(), 879);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 16 ========== */
testnum++;
testdesc = "test cropping a JPG with an offset";
// we'll reuse the container from the previous test
istream = imgTools.encodeCroppedImage(container, "image/jpeg", 16, 16, 16, 16);
encodedBytes = streamToArray(istream);
// Get bytes for exected result
refName = "image2jpg16x16cropped2.jpg";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
do_check_eq(istream.available(), 878);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 17 ========== */
testnum++;
testdesc = "test cropping a JPG without a given height";
// we'll reuse the container from the previous test
istream = imgTools.encodeCroppedImage(container, "image/jpeg", 0, 0, 16, 0);
encodedBytes = streamToArray(istream);
// Get bytes for exected result
refName = "image2jpg16x32cropped3.jpg";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
do_check_eq(istream.available(), 1127);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 18 ========== */
testnum++;
testdesc = "test cropping a JPG without a given width";
// we'll reuse the container from the previous test
istream = imgTools.encodeCroppedImage(container, "image/jpeg", 0, 0, 0, 16);
encodedBytes = streamToArray(istream);
// Get bytes for exected result
refName = "image2jpg32x16cropped4.jpg";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
do_check_eq(istream.available(), 1135);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 19 ========== */
testnum++;
testdesc = "test cropping a JPG without a given width and height";
// we'll reuse the container from the previous test
istream = imgTools.encodeCroppedImage(container, "image/jpeg", 0, 0, 0, 0);
encodedBytes = streamToArray(istream);
// Get bytes for exected result
refName = "image2jpg32x32.jpg";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
do_check_eq(istream.available(), 1634);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 20 ========== */
testnum++;
testdesc = "test scaling a JPG without a given width";
// we'll reuse the container from the previous test
istream = imgTools.encodeScaledImage(container, "image/jpeg", 0, 16);
encodedBytes = streamToArray(istream);
// Get bytes for exected result
refName = "image2jpg32x16scaled.jpg";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
do_check_eq(istream.available(), 1227);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 21 ========== */
testnum++;
testdesc = "test scaling a JPG without a given height";
// we'll reuse the container from the previous test
istream = imgTools.encodeScaledImage(container, "image/jpeg", 16, 0);
encodedBytes = streamToArray(istream);
// Get bytes for exected result
refName = "image2jpg16x32scaled.jpg";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
do_check_eq(istream.available(), 1219);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 22 ========== */
testnum++;
testdesc = "test scaling a JPG without a given width and height";
// we'll reuse the container from the previous test
istream = imgTools.encodeScaledImage(container, "image/jpeg", 0, 0);
encodedBytes = streamToArray(istream);
// Get bytes for exected result
refName = "image2jpg32x32.jpg";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
do_check_eq(istream.available(), 1634);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 22 ========== */
testnum++;
testdesc = "test invalid arguments for cropping";
var numErrors = 0;
try {
// width/height can't be negative
imgTools.encodeScaledImage(container, "image/jpeg", -1, -1);
} catch (e) { numErrors++; }
try {
// offsets can't be negative
imgTools.encodeCroppedImage(container, "image/jpeg", -1, -1, 16, 16);
} catch (e) { numErrors++; }
try {
// width/height can't be negative
imgTools.encodeCroppedImage(container, "image/jpeg", 0, 0, -1, -1);
} catch (e) { numErrors++; }
try {
// out of bounds
imgTools.encodeCroppedImage(container, "image/jpeg", 17, 17, 16, 16);
} catch (e) { numErrors++; }
try {
// out of bounds
imgTools.encodeCroppedImage(container, "image/jpeg", 0, 0, 33, 33);
} catch (e) { numErrors++; }
try {
// out of bounds
imgTools.encodeCroppedImage(container, "image/jpeg", 1, 1, 0, 0);
} catch (e) { numErrors++; }
do_check_eq(numErrors, 6);
/* ========== bug 363986 ========== */
testnum = 363986;
testdesc = "test PNG and JPEG encoders' Read/ReadSegments methods";
var testData =
[{preImage: "image3.ico",
preImageMimeType: "image/x-icon",
refImage: "image3ico16x16.png",
refImageMimeType: "image/png"},
{preImage: "image1.png",
preImageMimeType: "image/png",
refImage: "image1png64x64.jpg",
refImageMimeType: "image/jpeg"}];
for(var i=0; i<testData.length; ++i) {
var dict = testData[i];
var imgFile = do_get_file(dict["refImage"]);
var istream = getFileInputStream(imgFile);
var refBytes = streamToArray(istream);
imgFile = do_get_file(dict["preImage"]);
istream = getFileInputStream(imgFile);
var container = imgTools.decodeImage(istream, dict["preImageMimeType"]);
istream = imgTools.encodeImage(container, dict["refImageMimeType"]);
var sstream = Cc["@mozilla.org/storagestream;1"].
createInstance(Ci.nsIStorageStream);
sstream.init(4096, 4294967295, null);
var ostream = sstream.getOutputStream(0);
var bostream = Cc["@mozilla.org/network/buffered-output-stream;1"].
createInstance(Ci.nsIBufferedOutputStream);
//use a tiny buffer to make sure the image data doesn't fully fit in it
bostream.init(ostream, 8);
bostream.writeFrom(istream, istream.available());
bostream.flush(); bostream.close();
var encBytes = streamToArray(sstream.newInputStream(0));
compareArrays(refBytes, encBytes);
}
/* ========== bug 413512 ========== */
testnum = 413512;
testdesc = "test decoding bad favicon (bug 413512)";
imgName = "bug413512.ico";
inMimeType = "image/x-icon";
imgFile = do_get_file(imgName);
istream = getFileInputStream(imgFile);
do_check_eq(istream.available(), 17759);
var errsrc = "none";
try {
container = imgTools.decodeImage(istream, inMimeType);
// We expect to hit an error during encoding because the ICO header of the
// image is fine, but the actual resources are corrupt. Since decodeImage()
// only performs a metadata decode, it doesn't decode far enough to realize
// this, but we'll find out when we do a full decode during encodeImage().
try {
istream = imgTools.encodeImage(container, "image/png");
} catch (e) {
err = e;
errsrc = "encode";
}
} catch (e) {
err = e;
errsrc = "decode";
}
do_check_eq(errsrc, "encode");
checkExpectedError(/NS_ERROR_FAILURE/, err);
/* ========== bug 815359 ========== */
testnum = 815359;
testdesc = "test correct ico hotspots (bug 815359)";
imgName = "bug815359.ico";
inMimeType = "image/x-icon";
imgFile = do_get_file(imgName);
istream = getFileInputStream(imgFile);
do_check_eq(istream.available(), 4286);
container = imgTools.decodeImage(istream, inMimeType);
var props = container.QueryInterface(Ci.nsIProperties);
do_check_eq(props.get("hotspotX", Ci.nsISupportsPRUint32).data, 10);
do_check_eq(props.get("hotspotY", Ci.nsISupportsPRUint32).data, 9);
/* ========== end ========== */
} catch (e) {
throw "FAILED in test #" + testnum + " -- " + testdesc + ": " + e;
}
};

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,143 @@
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cr = Components.results;
var Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://testing-common/httpd.js");
var server = new HttpServer();
server.registerPathHandler('/image.png', imageHandler);
server.start(-1);
load('image_load_helpers.js');
var gHits = 0;
var gIoService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
var gPublicLoader = Cc["@mozilla.org/image/loader;1"].createInstance(Ci.imgILoader);
var gPrivateLoader = Cc["@mozilla.org/image/loader;1"].createInstance(Ci.imgILoader);
gPrivateLoader.QueryInterface(Ci.imgICache).respectPrivacyNotifications();
function imageHandler(metadata, response) {
gHits++;
response.setHeader("Cache-Control", "max-age=10000", false);
response.setStatusLine(metadata.httpVersion, 200, "OK");
response.setHeader("Content-Type", "image/png", false);
var body = "iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII=";
response.bodyOutputStream.write(body, body.length);
}
var requests = [];
var listeners = [];
function NotificationCallbacks(isPrivate) {
this.originAttributes.privateBrowsingId = isPrivate ? 1 : 0;
this.usePrivateBrowsing = isPrivate;
}
NotificationCallbacks.prototype = {
QueryInterface: function (iid) {
if (iid.equals(Ci.nsISupports) ||
iid.equals(Ci.nsILoadContext))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
},
getInterface: function(iid) {
if (iid.equals(Ci.nsILoadContext))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
},
originAttributes: {
privateBrowsingId: 0
}
};
var gImgPath = 'http://localhost:' + server.identity.primaryPort + '/image.png';
function setup_chan(path, isPrivate, callback) {
var uri = NetUtil.newURI(gImgPath);
var securityFlags = Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL;
var principal = Services.scriptSecurityManager
.createCodebasePrincipal(uri, {privateBrowsingId: isPrivate ? 1 : 0});
var chan = NetUtil.newChannel({uri: uri, loadingPrincipal: principal,
securityFlags: securityFlags,
contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE});
chan.notificationCallbacks = new NotificationCallbacks(isPrivate);
var channelListener = new ChannelListener();
chan.asyncOpen2(channelListener);
var listener = new ImageListener(null, callback);
var outlistener = {};
var loader = isPrivate ? gPrivateLoader : gPublicLoader;
var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
.createScriptedObserver(listener);
listeners.push(outer);
requests.push(loader.loadImageWithChannelXPCOM(chan, outer, null, outlistener));
channelListener.outputListener = outlistener.value;
listener.synchronous = false;
}
function loadImage(isPrivate, callback) {
var listener = new ImageListener(null, callback);
var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
.createScriptedObserver(listener);
var uri = gIoService.newURI(gImgPath, null, null);
var loadGroup = Cc["@mozilla.org/network/load-group;1"].createInstance(Ci.nsILoadGroup);
loadGroup.notificationCallbacks = new NotificationCallbacks(isPrivate);
var loader = isPrivate ? gPrivateLoader : gPublicLoader;
requests.push(loader.loadImageXPCOM(uri, null, null, "default", null, loadGroup, outer, null, 0, null));
listener.synchronous = false;
}
function run_loadImage_tests() {
function observer() {
Services.obs.removeObserver(observer, "cacheservice:empty-cache");
gHits = 0;
loadImage(false, function() {
loadImage(false, function() {
loadImage(true, function() {
loadImage(true, function() {
do_check_eq(gHits, 2);
server.stop(do_test_finished);
});
});
});
});
}
Services.obs.addObserver(observer, "cacheservice:empty-cache", false);
let cs = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
.getService(Ci.nsICacheStorageService);
cs.clear();
}
function cleanup()
{
for (var i = 0; i < requests.length; ++i) {
requests[i].cancelAndForgetObserver(0);
}
}
function run_test() {
do_register_cleanup(cleanup);
do_test_pending();
// We create a public channel that loads an image, then an identical
// one that should cause a cache read. We then create a private channel
// and load the same image, and do that a second time to ensure a cache
// read. In total, we should cause two separate http responses to occur,
// since the private channels shouldn't be able to use the public cache.
setup_chan('/image.png', false, function() {
setup_chan('/image.png', false, function() {
setup_chan('/image.png', true, function() {
setup_chan('/image.png', true, function() {
do_check_eq(gHits, 2);
run_loadImage_tests();
});
});
});
});
}

View file

@ -0,0 +1,45 @@
[DEFAULT]
head =
tail =
support-files =
async_load_tests.js
bug413512.ico
bug815359.ico
image1.png
image1png16x16.jpg
image1png64x64.jpg
image2.jpg
image2jpg16x16-win.png
image2jpg16x16.png
image2jpg16x16cropped.jpg
image2jpg16x16cropped2.jpg
image2jpg16x32cropped3.jpg
image2jpg16x32scaled.jpg
image2jpg32x16cropped4.jpg
image2jpg32x16scaled.jpg
image2jpg32x32-win.png
image2jpg32x32.jpg
image2jpg32x32.png
image3.ico
image3ico16x16.png
image3ico32x32.png
image4.gif
image4gif16x16bmp24bpp.ico
image4gif16x16bmp32bpp.ico
image4gif32x32bmp24bpp.ico
image4gif32x32bmp32bpp.ico
image_load_helpers.js
[test_async_notification.js]
# Bug 1156452: frequent crash on Android 4.3
skip-if = os == "android"
[test_async_notification_404.js]
[test_async_notification_animated.js]
[test_encoder_apng.js]
[test_encoder_png.js]
[test_imgtools.js]
# Bug 676968
skip-if = os == "android"
[test_moz_icon_uri.js]
[test_private_channel.js]