image/test/unit/test_encoder_png.js

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /*
     2  * Test for PNG encoding in libpr0n
     3  *
     4  */
     6 const Ci = Components.interfaces;
     7 const Cc = Components.classes;
     9 var png1A = {
    10         // A 3x3 image, rows are red, green, blue.
    11         // RGB format, transparency defaults.
    13         transparency : null,
    15         frames  : [
    16                 {
    17                         width  : 3,    height : 3,
    19                         format : Ci.imgIEncoder.INPUT_FORMAT_RGB, stride : 9,
    21                         pixels : [
    22                                 255,0,0,  255,0,0, 255,0,0,
    23                                 0,255,0,  0,255,0, 0,255,0,
    24                                 0,0,255,  0,0,255, 0,0,255,
    25                                 ]
    26                 }
    28                 ],
    29         expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII="
    30 };
    32 var png1B = {
    33         // A 3x3 image, rows are red, green, blue.
    34         // RGB format, transparency=none.
    36         transparency : "none",
    38         frames  : [
    39                 {
    40                         width  : 3,    height : 3,
    42                         format : Ci.imgIEncoder.INPUT_FORMAT_RGB, stride : 9,
    44                         pixels : [
    45                                 255,0,0,  255,0,0, 255,0,0,
    46                                 0,255,0,  0,255,0, 0,255,0,
    47                                 0,0,255,  0,0,255, 0,0,255,
    48                                 ]
    49                 }
    51                 ],
    52         expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII="
    53 };
    55 var png2A = {
    56         // A 3x3 image, rows are: red, green, blue. Columns are: 0%, 33%, 66% transparent.
    58         transparency : null,
    60         frames  : [
    61                 {
    62                         width  : 3,    height : 3,
    64                         format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
    66                         pixels : [
    67                                 255,0,0,255,  255,0,0,170, 255,0,0,85,
    68                                 0,255,0,255,  0,255,0,170, 0,255,0,85,
    69                                 0,0,255,255,  0,0,255,170, 0,0,255,85
    70                                 ]
    71                 }
    73                 ],
    74         expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAAIElEQVQImSXJMQEAMAwEIUy+yZi8DmVFFBcjycn86GgPJw4O8v9DkkEAAAAASUVORK5CYII="
    75 };
    77 var png2B = {
    78         // A 3x3 image, rows are: red, green, blue. Columns are: 0%, 33%, 66% transparent,
    79         // but transparency will be ignored.
    81         transparency : "none",
    83         frames  : [
    84                 {
    85                         width  : 3,    height : 3,
    87                         format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
    89                         pixels : [
    90                                 255,0,0,255,  255,0,0,170, 255,0,0,85,
    91                                 0,255,0,255,  0,255,0,170, 0,255,0,85,
    92                                 0,0,255,255,  0,0,255,170, 0,0,255,85
    93                                 ]
    94                 }
    96                 ],
    97         expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII="
    98 };
   100 // Main test entry point.
   101 function run_test() {
   102         dump("Checking png1A...\n")
   103         run_test_for(png1A);
   104         dump("Checking png1B...\n")
   105         run_test_for(png1B);
   106         dump("Checking png2A...\n")
   107         run_test_for(png2A);
   108         dump("Checking png2B...\n")
   109         run_test_for(png2B);
   110 }; 
   113 function run_test_for(input) {
   114         var encoder, dataURL;
   116         encoder = encodeImage(input);
   117         dataURL = makeDataURL(encoder, "image/png");
   118         do_check_eq(dataURL, input.expected);
   120         encoder = encodeImageAsync(input);
   121         dataURL = makeDataURLFromAsync(encoder, "image/png", input.expected);
   122 };
   125 function encodeImage(input) {
   126         var encoder = Cc["@mozilla.org/image/encoder;2?type=image/png"].createInstance();
   127         encoder.QueryInterface(Ci.imgIEncoder);
   129         var options = "";
   130         if (input.transparency) {
   131                 options += "transparency=" + input.transparency;
   132         }
   134         var frame = input.frames[0];
   135         encoder.initFromData(frame.pixels, frame.pixels.length,
   136                                 frame.width, frame.height, frame.stride,
   137                                 frame.format, options);
   138         return encoder;
   139 }
   141 function _encodeImageAsyncFactory(frame, options, encoder)
   142 {
   143         function finishEncode() {
   144             encoder.addImageFrame(frame.pixels, frame.pixels.length,
   145                                   frame.width, frame.height, frame.stride,
   146                                   frame.format, options);
   147             encoder.endImageEncode();
   148         }
   149         return finishEncode;
   150 }
   152 function encodeImageAsync(input)
   153 {
   154         var encoder = Cc["@mozilla.org/image/encoder;2?type=image/png"].createInstance();
   155         encoder.QueryInterface(Ci.imgIEncoder);
   157         var options = "";
   158         if (input.transparency) {
   159                 options += "transparency=" + input.transparency;
   160         }
   162         var frame = input.frames[0];
   163         encoder.startImageEncode(frame.width, frame.height,
   164                                  frame.format, options);
   166         do_timeout(50, _encodeImageAsyncFactory(frame, options, encoder));
   167         return encoder;
   168 }
   171 function makeDataURL(encoder, mimetype) {
   172         var rawStream = encoder.QueryInterface(Ci.nsIInputStream);
   174         var stream = Cc["@mozilla.org/binaryinputstream;1"].createInstance();
   175         stream.QueryInterface(Ci.nsIBinaryInputStream);
   177         stream.setInputStream(rawStream);
   179         var bytes = stream.readByteArray(stream.available()); // returns int[]
   181         var base64String = toBase64(bytes);
   183         return "data:" + mimetype + ";base64," + base64String;
   184 }
   186 function makeDataURLFromAsync(encoder, mimetype, expected) {
   187         do_test_pending();
   188         var rawStream = encoder.QueryInterface(Ci.nsIAsyncInputStream);
   190         var currentThread = Cc["@mozilla.org/thread-manager;1"].getService().currentThread;
   192         var bytes = [];
   194         var binarystream = Cc["@mozilla.org/binaryinputstream;1"].createInstance();
   195         binarystream.QueryInterface(Ci.nsIBinaryInputStream);
   197         var asyncReader =
   198         {
   199             onInputStreamReady: function(stream)
   200             {
   201                 binarystream.setInputStream(stream);
   202                 var available = 0;
   203                 try {
   204                   available = stream.available();
   205                 } catch(e) { }
   207                 if (available > 0)
   208                 {
   209                     bytes = bytes.concat(binarystream.readByteArray(available));
   210                     stream.asyncWait(this, 0, 0, currentThread);
   211                 } else {
   212                     var base64String = toBase64(bytes);
   213                     var dataURL = "data:" + mimetype + ";base64," + base64String;
   214                     do_check_eq(dataURL, expected);
   215                     do_test_finished();
   216                 }
   218             }
   219         };
   220         rawStream.asyncWait(asyncReader, 0, 0, currentThread);
   221 }
   223 /* toBase64 copied from extensions/xml-rpc/src/nsXmlRpcClient.js */
   225 /* Convert data (an array of integers) to a Base64 string. */
   226 const toBase64Table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' +
   227     '0123456789+/';
   228 const base64Pad = '=';
   229 function toBase64(data) {
   230     var result = '';
   231     var length = data.length;
   232     var i;
   233     // Convert every three bytes to 4 ascii characters.
   234     for (i = 0; i < (length - 2); i += 3) {
   235         result += toBase64Table[data[i] >> 2];
   236         result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
   237         result += toBase64Table[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)];
   238         result += toBase64Table[data[i+2] & 0x3f];
   239     }
   241     // Convert the remaining 1 or 2 bytes, pad out to 4 characters.
   242     if (length%3) {
   243         i = length - (length%3);
   244         result += toBase64Table[data[i] >> 2];
   245         if ((length%3) == 2) {
   246             result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
   247             result += toBase64Table[(data[i+1] & 0x0f) << 2];
   248             result += base64Pad;
   249         } else {
   250             result += toBase64Table[(data[i] & 0x03) << 4];
   251             result += base64Pad + base64Pad;
   252         }
   253     }
   255     return result;
   256 }

mercurial