Thu, 15 Jan 2015 15:59:08 +0100
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 }