async_load_tests.js (7818B)
1 /* 2 * Test to ensure that image loading/decoding notifications are always 3 * delivered async, and in the order we expect. 4 * 5 * Must be included from a file that has a uri of the image to test defined in 6 * var uri. 7 */ 8 /* import-globals-from image_load_helpers.js */ 9 10 const { HttpServer } = ChromeUtils.importESModule( 11 "resource://testing-common/httpd.sys.mjs" 12 ); 13 const { NetUtil } = ChromeUtils.importESModule( 14 "resource://gre/modules/NetUtil.sys.mjs" 15 ); 16 const ReferrerInfo = Components.Constructor( 17 "@mozilla.org/referrer-info;1", 18 "nsIReferrerInfo", 19 "init" 20 ); 21 22 var server = new HttpServer(); 23 server.registerDirectory("/", do_get_file("")); 24 server.registerContentType("sjs", "sjs"); 25 server.start(-1); 26 27 load("image_load_helpers.js"); 28 29 var requests = []; 30 /* global uri */ 31 32 // Return a closure that holds on to the listener from the original 33 // imgIRequest, and compares its results to the cloned one. 34 function getCloneStopCallback(original_listener) { 35 return function cloneStop(listener) { 36 Assert.equal(original_listener.state, listener.state); 37 38 // Sanity check to make sure we didn't accidentally use the same listener 39 // twice. 40 Assert.notEqual(original_listener, listener); 41 do_test_finished(); 42 }; 43 } 44 45 // Make sure that cloned requests get all the same callbacks as the original, 46 // but they aren't synchronous right now. 47 function checkClone(other_listener, aRequest) { 48 do_test_pending(); 49 50 // For as long as clone notification is synchronous, we can't test the clone state reliably. 51 var listener = new ImageListener( 52 null, 53 function () { 54 do_test_finished(); 55 } /* getCloneStopCallback(other_listener)*/ 56 ); 57 listener.synchronous = false; 58 var outer = Cc["@mozilla.org/image/tools;1"] 59 .getService(Ci.imgITools) 60 .createScriptedObserver(listener); 61 var clone = aRequest.clone(outer); 62 requests.push({ request: clone, locked: false }); 63 } 64 65 // Ensure that all the callbacks were called on aRequest. 66 function checkSizeAndLoad(listener) { 67 Assert.notEqual(listener.state & SIZE_AVAILABLE, 0); 68 Assert.notEqual(listener.state & LOAD_COMPLETE, 0); 69 70 do_test_finished(); 71 } 72 73 function secondLoadDone(oldlistener, aRequest) { 74 do_test_pending(); 75 76 try { 77 var staticrequest = aRequest.getStaticRequest(); 78 79 // For as long as clone notification is synchronous, we can't test the 80 // clone state reliably. 81 var listener = new ImageListener(null, checkSizeAndLoad); 82 listener.synchronous = false; 83 var outer = Cc["@mozilla.org/image/tools;1"] 84 .getService(Ci.imgITools) 85 .createScriptedObserver(listener); 86 var staticrequestclone = staticrequest.clone(outer); 87 requests.push({ request: staticrequestclone, locked: false }); 88 } catch (e) { 89 // We can't create a static request. Most likely the request we started 90 // with didn't load successfully. 91 do_test_finished(); 92 } 93 94 run_loadImageWithChannel_tests(); 95 96 do_test_finished(); 97 } 98 99 // Load the request a second time. This should come from the image cache, and 100 // therefore would be at most risk of being served synchronously. 101 function checkSecondLoad() { 102 do_test_pending(); 103 104 var listener = new ImageListener(checkClone, secondLoadDone); 105 var outer = Cc["@mozilla.org/image/tools;1"] 106 .getService(Ci.imgITools) 107 .createScriptedObserver(listener); 108 var referrerInfo = new ReferrerInfo( 109 Ci.nsIReferrerInfo.NO_REFERRER_WHEN_DOWNGRADE, 110 true, 111 null 112 ); 113 requests.push({ 114 request: gCurrentLoader.loadImageXPCOM( 115 uri, 116 null, 117 referrerInfo, 118 null, 119 null, 120 outer, 121 null, 122 0, 123 null 124 ), 125 locked: false, 126 }); 127 listener.synchronous = false; 128 } 129 130 function firstLoadDone() { 131 checkSecondLoad(uri); 132 133 do_test_finished(); 134 } 135 136 // Return a closure that allows us to check the stream listener's status when the 137 // image finishes loading. 138 function getChannelLoadImageStopCallback(streamlistener, next) { 139 return function channelLoadStop() { 140 next(); 141 142 do_test_finished(); 143 }; 144 } 145 146 // Load the request a second time. This should come from the image cache, and 147 // therefore would be at most risk of being served synchronously. 148 function checkSecondChannelLoad() { 149 do_test_pending(); 150 var channel = NetUtil.newChannel({ uri, loadUsingSystemPrincipal: true }); 151 var channellistener = new ChannelListener(); 152 channel.asyncOpen(channellistener); 153 154 var listener = new ImageListener( 155 null, 156 getChannelLoadImageStopCallback(channellistener, all_done_callback) 157 ); 158 var outer = Cc["@mozilla.org/image/tools;1"] 159 .getService(Ci.imgITools) 160 .createScriptedObserver(listener); 161 var outlistener = {}; 162 requests.push({ 163 request: gCurrentLoader.loadImageWithChannelXPCOM( 164 channel, 165 outer, 166 null, 167 outlistener 168 ), 169 locked: false, 170 }); 171 channellistener.outputListener = outlistener.value; 172 173 listener.synchronous = false; 174 } 175 176 function run_loadImageWithChannel_tests() { 177 // To ensure we're testing what we expect to, create a new loader and cache. 178 gCurrentLoader = Cc["@mozilla.org/image/loader;1"].createInstance( 179 Ci.imgILoader 180 ); 181 182 do_test_pending(); 183 var channel = NetUtil.newChannel({ uri, loadUsingSystemPrincipal: true }); 184 var channellistener = new ChannelListener(); 185 channel.asyncOpen(channellistener); 186 187 var listener = new ImageListener( 188 null, 189 getChannelLoadImageStopCallback(channellistener, checkSecondChannelLoad) 190 ); 191 var outer = Cc["@mozilla.org/image/tools;1"] 192 .getService(Ci.imgITools) 193 .createScriptedObserver(listener); 194 var outlistener = {}; 195 requests.push({ 196 request: gCurrentLoader.loadImageWithChannelXPCOM( 197 channel, 198 outer, 199 null, 200 outlistener 201 ), 202 locked: false, 203 }); 204 channellistener.outputListener = outlistener.value; 205 206 listener.synchronous = false; 207 } 208 209 function all_done_callback() { 210 server.stop(function () { 211 do_test_finished(); 212 }); 213 } 214 215 function startImageCallback(otherCb) { 216 return function (listener, request) { 217 // Make sure we can load the same image immediately out of the cache. 218 do_test_pending(); 219 var listener2 = new ImageListener(null, function () { 220 do_test_finished(); 221 }); 222 var outer = Cc["@mozilla.org/image/tools;1"] 223 .getService(Ci.imgITools) 224 .createScriptedObserver(listener2); 225 var referrerInfo = new ReferrerInfo( 226 Ci.nsIReferrerInfo.NO_REFERRER_WHEN_DOWNGRADE, 227 true, 228 null 229 ); 230 requests.push({ 231 request: gCurrentLoader.loadImageXPCOM( 232 uri, 233 null, 234 referrerInfo, 235 null, 236 null, 237 outer, 238 null, 239 0, 240 null 241 ), 242 locked: false, 243 }); 244 listener2.synchronous = false; 245 246 // Now that we've started another load, chain to the callback. 247 otherCb(listener, request); 248 }; 249 } 250 251 var gCurrentLoader; 252 253 function cleanup() { 254 for (let { request, locked } of requests) { 255 if (locked) { 256 try { 257 request.unlockImage(); 258 } catch (e) {} 259 } 260 request.cancelAndForgetObserver(0); 261 } 262 } 263 264 function run_test() { 265 registerCleanupFunction(cleanup); 266 267 gCurrentLoader = Cc["@mozilla.org/image/loader;1"].createInstance( 268 Ci.imgILoader 269 ); 270 271 do_test_pending(); 272 var listener = new ImageListener( 273 startImageCallback(checkClone), 274 firstLoadDone 275 ); 276 var outer = Cc["@mozilla.org/image/tools;1"] 277 .getService(Ci.imgITools) 278 .createScriptedObserver(listener); 279 var referrerInfo = new ReferrerInfo( 280 Ci.nsIReferrerInfo.NO_REFERRER_WHEN_DOWNGRADE, 281 true, 282 null 283 ); 284 var req = gCurrentLoader.loadImageXPCOM( 285 uri, 286 null, 287 referrerInfo, 288 null, 289 null, 290 outer, 291 null, 292 0, 293 null 294 ); 295 296 // Ensure that we don't cause any mayhem when we lock an image. 297 req.lockImage(); 298 299 requests.push({ request: req, locked: true }); 300 301 listener.synchronous = false; 302 }