dom/tests/mochitest/ajax/offline/offlineTests.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 // Utility functions for offline tests.
michael@0 2 var Cc = SpecialPowers.Cc;
michael@0 3 var Ci = SpecialPowers.Ci;
michael@0 4 var Cu = SpecialPowers.Cu;
michael@0 5 var LoadContextInfo = Cu.import("resource://gre/modules/LoadContextInfo.jsm", {}).LoadContextInfo;
michael@0 6
michael@0 7 const kNetBase = 2152398848; // 0x804B0000
michael@0 8 var NS_ERROR_CACHE_KEY_NOT_FOUND = kNetBase + 61;
michael@0 9 var NS_ERROR_CACHE_KEY_WAIT_FOR_VALIDATION = kNetBase + 64;
michael@0 10
michael@0 11 // Reading the contents of multiple cache entries asynchronously
michael@0 12 function OfflineCacheContents(urls) {
michael@0 13 this.urls = urls;
michael@0 14 this.contents = {};
michael@0 15 }
michael@0 16
michael@0 17 OfflineCacheContents.prototype = {
michael@0 18 QueryInterface: function(iid) {
michael@0 19 if (!iid.equals(Ci.nsISupports) &&
michael@0 20 !iid.equals(Ci.nsICacheListener)) {
michael@0 21 throw Cr.NS_ERROR_NO_INTERFACE;
michael@0 22 }
michael@0 23 return this;
michael@0 24 },
michael@0 25 onCacheEntryAvailable: function(desc, accessGranted, status) {
michael@0 26 if (!desc) {
michael@0 27 this.fetch(this.callback);
michael@0 28 return;
michael@0 29 }
michael@0 30
michael@0 31 var stream = desc.QueryInterface(Ci.nsICacheEntryDescriptor).openInputStream(0);
michael@0 32 var sstream = Cc["@mozilla.org/scriptableinputstream;1"]
michael@0 33 .createInstance(SpecialPowers.Ci.nsIScriptableInputStream);
michael@0 34 sstream.init(stream);
michael@0 35 this.contents[desc.key] = sstream.read(sstream.available());
michael@0 36 sstream.close();
michael@0 37 desc.close();
michael@0 38 this.fetch(this.callback);
michael@0 39 },
michael@0 40
michael@0 41 fetch: function(callback)
michael@0 42 {
michael@0 43 this.callback = callback;
michael@0 44 if (this.urls.length == 0) {
michael@0 45 callback(this.contents);
michael@0 46 return;
michael@0 47 }
michael@0 48
michael@0 49 var url = this.urls.shift();
michael@0 50 var self = this;
michael@0 51
michael@0 52 var cacheSession = OfflineTest.getActiveSession();
michael@0 53 cacheSession.asyncOpenCacheEntry(url, Ci.nsICache.ACCESS_READ, this);
michael@0 54 }
michael@0 55 };
michael@0 56
michael@0 57 var OfflineTest = {
michael@0 58
michael@0 59 _allowedByDefault: false,
michael@0 60
michael@0 61 _hasSlave: false,
michael@0 62
michael@0 63 // The window where test results should be sent.
michael@0 64 _masterWindow: null,
michael@0 65
michael@0 66 // Array of all PUT overrides on the server
michael@0 67 _pathOverrides: [],
michael@0 68
michael@0 69 // SJSs whom state was changed to be reverted on teardown
michael@0 70 _SJSsStated: [],
michael@0 71
michael@0 72 setupChild: function()
michael@0 73 {
michael@0 74 if (this._allowedByDefault) {
michael@0 75 this._masterWindow = window;
michael@0 76 return true;
michael@0 77 }
michael@0 78
michael@0 79 if (window.parent.OfflineTest._hasSlave) {
michael@0 80 return false;
michael@0 81 }
michael@0 82
michael@0 83 this._masterWindow = window.top;
michael@0 84
michael@0 85 return true;
michael@0 86 },
michael@0 87
michael@0 88 /**
michael@0 89 * Setup the tests. This will reload the current page in a new window
michael@0 90 * if necessary.
michael@0 91 *
michael@0 92 * @return boolean Whether this window is the slave window
michael@0 93 * to actually run the test in.
michael@0 94 */
michael@0 95 setup: function()
michael@0 96 {
michael@0 97 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
michael@0 98
michael@0 99 try {
michael@0 100 this._allowedByDefault = SpecialPowers.getBoolPref("offline-apps.allow_by_default");
michael@0 101 } catch (e) {}
michael@0 102
michael@0 103 if (this._allowedByDefault) {
michael@0 104 this._masterWindow = window;
michael@0 105
michael@0 106 return true;
michael@0 107 }
michael@0 108
michael@0 109 if (!window.opener || !window.opener.OfflineTest ||
michael@0 110 !window.opener.OfflineTest._hasSlave) {
michael@0 111 // Offline applications must be toplevel windows and have the
michael@0 112 // offline-app permission. Because we were loaded without the
michael@0 113 // offline-app permission and (probably) in an iframe, we need to
michael@0 114 // enable the pref and spawn a new window to perform the actual
michael@0 115 // tests. It will use this window to report successes and
michael@0 116 // failures.
michael@0 117
michael@0 118 if (SpecialPowers.testPermission("offline-app", Ci.nsIPermissionManager.ALLOW_ACTION, document)) {
michael@0 119 ok(false, "Previous test failed to clear offline-app permission! Expect failures.");
michael@0 120 }
michael@0 121 SpecialPowers.addPermission("offline-app", Ci.nsIPermissionManager.ALLOW_ACTION, document);
michael@0 122
michael@0 123 // Tests must run as toplevel windows. Open a slave window to run
michael@0 124 // the test.
michael@0 125 this._hasSlave = true;
michael@0 126 window.open(window.location, "offlinetest");
michael@0 127
michael@0 128 return false;
michael@0 129 }
michael@0 130
michael@0 131 this._masterWindow = window.opener;
michael@0 132
michael@0 133 return true;
michael@0 134 },
michael@0 135
michael@0 136 teardownAndFinish: function()
michael@0 137 {
michael@0 138 this.teardown(function(self) { self.finish(); });
michael@0 139 },
michael@0 140
michael@0 141 teardown: function(callback)
michael@0 142 {
michael@0 143 // First wait for any pending scheduled updates to finish
michael@0 144 this.waitForUpdates(function(self) {
michael@0 145 // Remove the offline-app permission we gave ourselves.
michael@0 146
michael@0 147 SpecialPowers.removePermission("offline-app", window.document);
michael@0 148
michael@0 149 // Clear all overrides on the server
michael@0 150 for (override in self._pathOverrides)
michael@0 151 self.deleteData(self._pathOverrides[override]);
michael@0 152 for (statedSJS in self._SJSsStated)
michael@0 153 self.setSJSState(self._SJSsStated[statedSJS], "");
michael@0 154
michael@0 155 self.clear();
michael@0 156 callback(self);
michael@0 157 });
michael@0 158 },
michael@0 159
michael@0 160 finish: function()
michael@0 161 {
michael@0 162 if (this._allowedByDefault) {
michael@0 163 SimpleTest.executeSoon(SimpleTest.finish);
michael@0 164 } else if (this._masterWindow) {
michael@0 165 // Slave window: pass control back to master window, close itself.
michael@0 166 this._masterWindow.SimpleTest.executeSoon(this._masterWindow.OfflineTest.finish);
michael@0 167 window.close();
michael@0 168 } else {
michael@0 169 // Master window: finish test.
michael@0 170 SimpleTest.finish();
michael@0 171 }
michael@0 172 },
michael@0 173
michael@0 174 //
michael@0 175 // Mochitest wrappers - These forward tests to the proper mochitest window.
michael@0 176 //
michael@0 177 ok: function(condition, name, diag)
michael@0 178 {
michael@0 179 return this._masterWindow.SimpleTest.ok(condition, name, diag);
michael@0 180 },
michael@0 181
michael@0 182 is: function(a, b, name)
michael@0 183 {
michael@0 184 return this._masterWindow.SimpleTest.is(a, b, name);
michael@0 185 },
michael@0 186
michael@0 187 isnot: function(a, b, name)
michael@0 188 {
michael@0 189 return this._masterWindow.SimpleTest.isnot(a, b, name);
michael@0 190 },
michael@0 191
michael@0 192 todo: function(a, name)
michael@0 193 {
michael@0 194 return this._masterWindow.SimpleTest.todo(a, name);
michael@0 195 },
michael@0 196
michael@0 197 clear: function()
michael@0 198 {
michael@0 199 // XXX: maybe we should just wipe out the entire disk cache.
michael@0 200 var applicationCache = this.getActiveCache();
michael@0 201 if (applicationCache) {
michael@0 202 applicationCache.discard();
michael@0 203 }
michael@0 204 },
michael@0 205
michael@0 206 waitForUpdates: function(callback)
michael@0 207 {
michael@0 208 var self = this;
michael@0 209 var observer = {
michael@0 210 notified: false,
michael@0 211 observe: function(subject, topic, data) {
michael@0 212 if (subject) {
michael@0 213 subject.QueryInterface(SpecialPowers.Ci.nsIOfflineCacheUpdate);
michael@0 214 dump("Update of " + subject.manifestURI.spec + " finished\n");
michael@0 215 }
michael@0 216
michael@0 217 SimpleTest.executeSoon(function() {
michael@0 218 if (observer.notified) {
michael@0 219 return;
michael@0 220 }
michael@0 221
michael@0 222 var updateservice = Cc["@mozilla.org/offlinecacheupdate-service;1"]
michael@0 223 .getService(SpecialPowers.Ci.nsIOfflineCacheUpdateService);
michael@0 224 var updatesPending = updateservice.numUpdates;
michael@0 225 if (updatesPending == 0) {
michael@0 226 try {
michael@0 227 SpecialPowers.removeObserver(observer, "offline-cache-update-completed");
michael@0 228 } catch(ex) {}
michael@0 229 dump("All pending updates done\n");
michael@0 230 observer.notified = true;
michael@0 231 callback(self);
michael@0 232 return;
michael@0 233 }
michael@0 234
michael@0 235 dump("Waiting for " + updateservice.numUpdates + " update(s) to finish\n");
michael@0 236 });
michael@0 237 }
michael@0 238 }
michael@0 239
michael@0 240 SpecialPowers.addObserver(observer, "offline-cache-update-completed", false);
michael@0 241
michael@0 242 // Call now to check whether there are some updates scheduled
michael@0 243 observer.observe();
michael@0 244 },
michael@0 245
michael@0 246 failEvent: function(e)
michael@0 247 {
michael@0 248 OfflineTest.ok(false, "Unexpected event: " + e.type);
michael@0 249 },
michael@0 250
michael@0 251 // The offline API as specified has no way to watch the load of a resource
michael@0 252 // added with applicationCache.mozAdd().
michael@0 253 waitForAdd: function(url, onFinished) {
michael@0 254 // Check every half second for ten seconds.
michael@0 255 var numChecks = 20;
michael@0 256
michael@0 257 var waitForAddListener = {
michael@0 258 onCacheEntryAvailable: function(entry, access, status) {
michael@0 259 if (entry) {
michael@0 260 entry.close();
michael@0 261 onFinished();
michael@0 262 return;
michael@0 263 }
michael@0 264
michael@0 265 if (--numChecks == 0) {
michael@0 266 onFinished();
michael@0 267 return;
michael@0 268 }
michael@0 269
michael@0 270 setTimeout(OfflineTest.priv(waitFunc), 500);
michael@0 271 }
michael@0 272 };
michael@0 273
michael@0 274 var waitFunc = function() {
michael@0 275 var cacheSession = OfflineTest.getActiveSession();
michael@0 276 cacheSession.asyncOpenCacheEntry(url,
michael@0 277 Ci.nsICache.ACCESS_READ,
michael@0 278 waitForAddListener);
michael@0 279 }
michael@0 280
michael@0 281 setTimeout(this.priv(waitFunc), 500);
michael@0 282 },
michael@0 283
michael@0 284 manifestURL: function(overload)
michael@0 285 {
michael@0 286 var manifestURLspec;
michael@0 287 if (overload) {
michael@0 288 manifestURLspec = overload;
michael@0 289 } else {
michael@0 290 var win = window;
michael@0 291 while (win && !win.document.documentElement.getAttribute("manifest")) {
michael@0 292 if (win == win.parent)
michael@0 293 break;
michael@0 294 win = win.parent;
michael@0 295 }
michael@0 296 if (win)
michael@0 297 manifestURLspec = win.document.documentElement.getAttribute("manifest");
michael@0 298 }
michael@0 299
michael@0 300 var ios = Cc["@mozilla.org/network/io-service;1"]
michael@0 301 .getService(Ci.nsIIOService)
michael@0 302
michael@0 303 var baseURI = ios.newURI(window.location.href, null, null);
michael@0 304 return ios.newURI(manifestURLspec, null, baseURI);
michael@0 305 },
michael@0 306
michael@0 307 loadContext: function()
michael@0 308 {
michael@0 309 return SpecialPowers.wrap(window).QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
michael@0 310 .getInterface(SpecialPowers.Ci.nsIWebNavigation)
michael@0 311 .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
michael@0 312 .getInterface(SpecialPowers.Ci.nsILoadContext);
michael@0 313 },
michael@0 314
michael@0 315 loadContextInfo: function()
michael@0 316 {
michael@0 317 return LoadContextInfo.fromLoadContext(this.loadContext(), false);
michael@0 318 },
michael@0 319
michael@0 320 getActiveCache: function(overload)
michael@0 321 {
michael@0 322 // Note that this is the current active cache in the cache stack, not the
michael@0 323 // one associated with this window.
michael@0 324 var serv = Cc["@mozilla.org/network/application-cache-service;1"]
michael@0 325 .getService(Ci.nsIApplicationCacheService);
michael@0 326 var groupID = serv.buildGroupID(this.manifestURL(overload), this.loadContextInfo());
michael@0 327 return serv.getActiveCache(groupID);
michael@0 328 },
michael@0 329
michael@0 330 getActiveSession: function()
michael@0 331 {
michael@0 332 var cache = this.getActiveCache();
michael@0 333 if (!cache) {
michael@0 334 return null;
michael@0 335 }
michael@0 336
michael@0 337 var cacheService = Cc["@mozilla.org/network/cache-service;1"]
michael@0 338 .getService(Ci.nsICacheService);
michael@0 339 return cacheService.createSession(cache.clientID,
michael@0 340 Ci.nsICache.STORE_OFFLINE,
michael@0 341 true);
michael@0 342 },
michael@0 343
michael@0 344 priv: function(func)
michael@0 345 {
michael@0 346 var self = this;
michael@0 347 return function() {
michael@0 348 func(arguments);
michael@0 349 }
michael@0 350 },
michael@0 351
michael@0 352 checkCacheEntries: function(entries, callback)
michael@0 353 {
michael@0 354 var checkNextEntry = function() {
michael@0 355 if (entries.length == 0) {
michael@0 356 setTimeout(OfflineTest.priv(callback), 0);
michael@0 357 } else {
michael@0 358 OfflineTest.checkCache(entries[0][0], entries[0][1], checkNextEntry);
michael@0 359 entries.shift();
michael@0 360 }
michael@0 361 }
michael@0 362
michael@0 363 checkNextEntry();
michael@0 364 },
michael@0 365
michael@0 366 checkCache: function(url, expectEntry, callback)
michael@0 367 {
michael@0 368 var cacheSession = this.getActiveSession();
michael@0 369 this._checkCache(cacheSession, url, expectEntry, callback);
michael@0 370 },
michael@0 371
michael@0 372 _checkCache: function(cacheSession, url, expectEntry, callback)
michael@0 373 {
michael@0 374 if (!cacheSession) {
michael@0 375 if (expectEntry) {
michael@0 376 this.ok(false, url + " should exist in the offline cache (no session)");
michael@0 377 } else {
michael@0 378 this.ok(true, url + " should not exist in the offline cache (no session)");
michael@0 379 }
michael@0 380 if (callback) setTimeout(this.priv(callback), 0);
michael@0 381 return;
michael@0 382 }
michael@0 383
michael@0 384 var _checkCacheListener = {
michael@0 385 onCacheEntryAvailable: function(entry, access, status) {
michael@0 386 if (entry) {
michael@0 387 if (expectEntry) {
michael@0 388 OfflineTest.ok(true, url + " should exist in the offline cache");
michael@0 389 } else {
michael@0 390 OfflineTest.ok(false, url + " should not exist in the offline cache");
michael@0 391 }
michael@0 392 entry.close();
michael@0 393 } else {
michael@0 394 if (status == NS_ERROR_CACHE_KEY_NOT_FOUND) {
michael@0 395 if (expectEntry) {
michael@0 396 OfflineTest.ok(false, url + " should exist in the offline cache");
michael@0 397 } else {
michael@0 398 OfflineTest.ok(true, url + " should not exist in the offline cache");
michael@0 399 }
michael@0 400 } else if (status == NS_ERROR_CACHE_KEY_WAIT_FOR_VALIDATION) {
michael@0 401 // There was a cache key that we couldn't access yet, that's good enough.
michael@0 402 if (expectEntry) {
michael@0 403 OfflineTest.ok(!mustBeValid, url + " should exist in the offline cache");
michael@0 404 } else {
michael@0 405 OfflineTest.ok(mustBeValid, url + " should not exist in the offline cache");
michael@0 406 }
michael@0 407 } else {
michael@0 408 OfflineTest.ok(false, "got invalid error for " + url);
michael@0 409 }
michael@0 410 }
michael@0 411 if (callback) setTimeout(OfflineTest.priv(callback), 0);
michael@0 412 }
michael@0 413 };
michael@0 414
michael@0 415 cacheSession.asyncOpenCacheEntry(url,
michael@0 416 Ci.nsICache.ACCESS_READ,
michael@0 417 _checkCacheListener,
michael@0 418 false);
michael@0 419 },
michael@0 420
michael@0 421 setSJSState: function(sjsPath, stateQuery)
michael@0 422 {
michael@0 423 var client = new XMLHttpRequest();
michael@0 424 client.open("GET", sjsPath + "?state=" + stateQuery, false);
michael@0 425
michael@0 426 var appcachechannel = SpecialPowers.wrap(client).channel.QueryInterface(Ci.nsIApplicationCacheChannel);
michael@0 427 appcachechannel.chooseApplicationCache = false;
michael@0 428 appcachechannel.inheritApplicationCache = false;
michael@0 429 appcachechannel.applicationCache = null;
michael@0 430
michael@0 431 client.send();
michael@0 432
michael@0 433 if (stateQuery == "")
michael@0 434 delete this._SJSsStated[sjsPath];
michael@0 435 else
michael@0 436 this._SJSsStated.push(sjsPath);
michael@0 437 }
michael@0 438
michael@0 439 };

mercurial