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

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/tests/mochitest/ajax/offline/offlineTests.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,439 @@
     1.4 +// Utility functions for offline tests.
     1.5 +var Cc = SpecialPowers.Cc;
     1.6 +var Ci = SpecialPowers.Ci;
     1.7 +var Cu = SpecialPowers.Cu;
     1.8 +var LoadContextInfo = Cu.import("resource://gre/modules/LoadContextInfo.jsm", {}).LoadContextInfo;
     1.9 +
    1.10 +const kNetBase = 2152398848; // 0x804B0000
    1.11 +var NS_ERROR_CACHE_KEY_NOT_FOUND = kNetBase + 61;
    1.12 +var NS_ERROR_CACHE_KEY_WAIT_FOR_VALIDATION = kNetBase + 64;
    1.13 +
    1.14 +// Reading the contents of multiple cache entries asynchronously
    1.15 +function OfflineCacheContents(urls) {
    1.16 +  this.urls = urls;
    1.17 +  this.contents = {};
    1.18 +}
    1.19 +
    1.20 +OfflineCacheContents.prototype = {
    1.21 +QueryInterface: function(iid) {
    1.22 +    if (!iid.equals(Ci.nsISupports) &&
    1.23 +        !iid.equals(Ci.nsICacheListener)) {
    1.24 +      throw Cr.NS_ERROR_NO_INTERFACE;
    1.25 +    }
    1.26 +    return this;
    1.27 +  },
    1.28 +onCacheEntryAvailable: function(desc, accessGranted, status) {
    1.29 +    if (!desc) {
    1.30 +      this.fetch(this.callback);
    1.31 +      return;
    1.32 +    }
    1.33 +
    1.34 +    var stream = desc.QueryInterface(Ci.nsICacheEntryDescriptor).openInputStream(0);
    1.35 +    var sstream = Cc["@mozilla.org/scriptableinputstream;1"]
    1.36 +                 .createInstance(SpecialPowers.Ci.nsIScriptableInputStream);
    1.37 +    sstream.init(stream);
    1.38 +    this.contents[desc.key] = sstream.read(sstream.available());
    1.39 +    sstream.close();
    1.40 +    desc.close();
    1.41 +    this.fetch(this.callback);
    1.42 +  },
    1.43 +
    1.44 +fetch: function(callback)
    1.45 +{
    1.46 +  this.callback = callback;
    1.47 +  if (this.urls.length == 0) {
    1.48 +    callback(this.contents);
    1.49 +    return;
    1.50 +  }
    1.51 +
    1.52 +  var url = this.urls.shift();
    1.53 +  var self = this;
    1.54 +
    1.55 +  var cacheSession = OfflineTest.getActiveSession();
    1.56 +  cacheSession.asyncOpenCacheEntry(url, Ci.nsICache.ACCESS_READ, this);
    1.57 +}
    1.58 +};
    1.59 +
    1.60 +var OfflineTest = {
    1.61 +
    1.62 +_allowedByDefault: false,
    1.63 +
    1.64 +_hasSlave: false,
    1.65 +
    1.66 +// The window where test results should be sent.
    1.67 +_masterWindow: null,
    1.68 +
    1.69 +// Array of all PUT overrides on the server
    1.70 +_pathOverrides: [],
    1.71 +
    1.72 +// SJSs whom state was changed to be reverted on teardown
    1.73 +_SJSsStated: [],
    1.74 +
    1.75 +setupChild: function()
    1.76 +{
    1.77 +  if (this._allowedByDefault) {
    1.78 +    this._masterWindow = window;
    1.79 +    return true;
    1.80 +  }
    1.81 +
    1.82 +  if (window.parent.OfflineTest._hasSlave) {
    1.83 +    return false;
    1.84 +  }
    1.85 +
    1.86 +  this._masterWindow = window.top;
    1.87 +
    1.88 +  return true;
    1.89 +},
    1.90 +
    1.91 +/**
    1.92 + * Setup the tests.  This will reload the current page in a new window
    1.93 + * if necessary.
    1.94 + *
    1.95 + * @return boolean Whether this window is the slave window
    1.96 + *                 to actually run the test in.
    1.97 + */
    1.98 +setup: function()
    1.99 +{
   1.100 +  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
   1.101 +
   1.102 +  try {
   1.103 +    this._allowedByDefault = SpecialPowers.getBoolPref("offline-apps.allow_by_default");
   1.104 +  } catch (e) {}
   1.105 +
   1.106 +  if (this._allowedByDefault) {
   1.107 +    this._masterWindow = window;
   1.108 +
   1.109 +    return true;
   1.110 +  }
   1.111 +
   1.112 +  if (!window.opener || !window.opener.OfflineTest ||
   1.113 +      !window.opener.OfflineTest._hasSlave) {
   1.114 +    // Offline applications must be toplevel windows and have the
   1.115 +    // offline-app permission.  Because we were loaded without the
   1.116 +    // offline-app permission and (probably) in an iframe, we need to
   1.117 +    // enable the pref and spawn a new window to perform the actual
   1.118 +    // tests.  It will use this window to report successes and
   1.119 +    // failures.
   1.120 +
   1.121 +    if (SpecialPowers.testPermission("offline-app", Ci.nsIPermissionManager.ALLOW_ACTION, document)) {
   1.122 +      ok(false, "Previous test failed to clear offline-app permission!  Expect failures.");
   1.123 +    }
   1.124 +    SpecialPowers.addPermission("offline-app", Ci.nsIPermissionManager.ALLOW_ACTION, document);
   1.125 +
   1.126 +    // Tests must run as toplevel windows.  Open a slave window to run
   1.127 +    // the test.
   1.128 +    this._hasSlave = true;
   1.129 +    window.open(window.location, "offlinetest");
   1.130 +
   1.131 +    return false;
   1.132 +  }
   1.133 +
   1.134 +  this._masterWindow = window.opener;
   1.135 +
   1.136 +  return true;
   1.137 +},
   1.138 +
   1.139 +teardownAndFinish: function()
   1.140 +{
   1.141 +  this.teardown(function(self) { self.finish(); });
   1.142 +},
   1.143 +
   1.144 +teardown: function(callback)
   1.145 +{
   1.146 +  // First wait for any pending scheduled updates to finish
   1.147 +  this.waitForUpdates(function(self) {
   1.148 +    // Remove the offline-app permission we gave ourselves.
   1.149 +
   1.150 +    SpecialPowers.removePermission("offline-app", window.document);
   1.151 +
   1.152 +    // Clear all overrides on the server
   1.153 +    for (override in self._pathOverrides)
   1.154 +      self.deleteData(self._pathOverrides[override]);
   1.155 +    for (statedSJS in self._SJSsStated)
   1.156 +      self.setSJSState(self._SJSsStated[statedSJS], "");
   1.157 +
   1.158 +    self.clear();
   1.159 +    callback(self);
   1.160 +  });
   1.161 +},
   1.162 +
   1.163 +finish: function()
   1.164 +{
   1.165 +  if (this._allowedByDefault) {
   1.166 +    SimpleTest.executeSoon(SimpleTest.finish);
   1.167 +  } else if (this._masterWindow) {
   1.168 +    // Slave window: pass control back to master window, close itself.
   1.169 +    this._masterWindow.SimpleTest.executeSoon(this._masterWindow.OfflineTest.finish);
   1.170 +    window.close();
   1.171 +  } else {
   1.172 +    // Master window: finish test.
   1.173 +    SimpleTest.finish();
   1.174 +  }
   1.175 +},
   1.176 +
   1.177 +//
   1.178 +// Mochitest wrappers - These forward tests to the proper mochitest window.
   1.179 +//
   1.180 +ok: function(condition, name, diag)
   1.181 +{
   1.182 +  return this._masterWindow.SimpleTest.ok(condition, name, diag);
   1.183 +},
   1.184 +
   1.185 +is: function(a, b, name)
   1.186 +{
   1.187 +  return this._masterWindow.SimpleTest.is(a, b, name);
   1.188 +},
   1.189 +
   1.190 +isnot: function(a, b, name)
   1.191 +{
   1.192 +  return this._masterWindow.SimpleTest.isnot(a, b, name);
   1.193 +},
   1.194 +
   1.195 +todo: function(a, name)
   1.196 +{
   1.197 +  return this._masterWindow.SimpleTest.todo(a, name);
   1.198 +},
   1.199 +
   1.200 +clear: function()
   1.201 +{
   1.202 +  // XXX: maybe we should just wipe out the entire disk cache.
   1.203 +  var applicationCache = this.getActiveCache();
   1.204 +  if (applicationCache) {
   1.205 +    applicationCache.discard();
   1.206 +  }
   1.207 +},
   1.208 +
   1.209 +waitForUpdates: function(callback)
   1.210 +{
   1.211 +  var self = this;
   1.212 +  var observer = {
   1.213 +    notified: false,
   1.214 +    observe: function(subject, topic, data) {
   1.215 +      if (subject) {
   1.216 +        subject.QueryInterface(SpecialPowers.Ci.nsIOfflineCacheUpdate);
   1.217 +        dump("Update of " + subject.manifestURI.spec + " finished\n");
   1.218 +      }
   1.219 +
   1.220 +      SimpleTest.executeSoon(function() {
   1.221 +        if (observer.notified) {
   1.222 +          return;
   1.223 +        }
   1.224 +
   1.225 +        var updateservice = Cc["@mozilla.org/offlinecacheupdate-service;1"]
   1.226 +                            .getService(SpecialPowers.Ci.nsIOfflineCacheUpdateService);
   1.227 +        var updatesPending = updateservice.numUpdates;
   1.228 +        if (updatesPending == 0) {
   1.229 +          try {
   1.230 +            SpecialPowers.removeObserver(observer, "offline-cache-update-completed");
   1.231 +          } catch(ex) {}
   1.232 +          dump("All pending updates done\n");
   1.233 +          observer.notified = true;
   1.234 +          callback(self);
   1.235 +          return;
   1.236 +        }
   1.237 +
   1.238 +        dump("Waiting for " + updateservice.numUpdates + " update(s) to finish\n");
   1.239 +      });
   1.240 +    }
   1.241 +  }
   1.242 +
   1.243 +  SpecialPowers.addObserver(observer, "offline-cache-update-completed", false);
   1.244 +
   1.245 +  // Call now to check whether there are some updates scheduled
   1.246 +  observer.observe();
   1.247 +},
   1.248 +
   1.249 +failEvent: function(e)
   1.250 +{
   1.251 +  OfflineTest.ok(false, "Unexpected event: " + e.type);
   1.252 +},
   1.253 +
   1.254 +// The offline API as specified has no way to watch the load of a resource
   1.255 +// added with applicationCache.mozAdd().
   1.256 +waitForAdd: function(url, onFinished) {
   1.257 +  // Check every half second for ten seconds.
   1.258 +  var numChecks = 20;
   1.259 +
   1.260 +  var waitForAddListener = {
   1.261 +    onCacheEntryAvailable: function(entry, access, status) {
   1.262 +      if (entry) {
   1.263 +        entry.close();
   1.264 +        onFinished();
   1.265 +        return;
   1.266 +      }
   1.267 +
   1.268 +      if (--numChecks == 0) {
   1.269 +        onFinished();
   1.270 +        return;
   1.271 +      }
   1.272 +
   1.273 +      setTimeout(OfflineTest.priv(waitFunc), 500);
   1.274 +    }
   1.275 +  };
   1.276 +
   1.277 +  var waitFunc = function() {
   1.278 +    var cacheSession = OfflineTest.getActiveSession();
   1.279 +    cacheSession.asyncOpenCacheEntry(url,
   1.280 +                                     Ci.nsICache.ACCESS_READ,
   1.281 +                                     waitForAddListener);
   1.282 +  }
   1.283 +
   1.284 +  setTimeout(this.priv(waitFunc), 500);
   1.285 +},
   1.286 +
   1.287 +manifestURL: function(overload)
   1.288 +{
   1.289 +  var manifestURLspec;
   1.290 +  if (overload) {
   1.291 +    manifestURLspec = overload;
   1.292 +  } else {
   1.293 +    var win = window;
   1.294 +    while (win && !win.document.documentElement.getAttribute("manifest")) {
   1.295 +      if (win == win.parent)
   1.296 +        break;
   1.297 +      win = win.parent;
   1.298 +    }
   1.299 +    if (win)
   1.300 +      manifestURLspec = win.document.documentElement.getAttribute("manifest");
   1.301 +  }
   1.302 +
   1.303 +  var ios = Cc["@mozilla.org/network/io-service;1"]
   1.304 +            .getService(Ci.nsIIOService)
   1.305 +
   1.306 +  var baseURI = ios.newURI(window.location.href, null, null);
   1.307 +  return ios.newURI(manifestURLspec, null, baseURI);
   1.308 +},
   1.309 +
   1.310 +loadContext: function()
   1.311 +{
   1.312 +  return SpecialPowers.wrap(window).QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
   1.313 +                                   .getInterface(SpecialPowers.Ci.nsIWebNavigation)
   1.314 +                                   .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
   1.315 +                                   .getInterface(SpecialPowers.Ci.nsILoadContext);
   1.316 +},
   1.317 +
   1.318 +loadContextInfo: function()
   1.319 +{
   1.320 +  return LoadContextInfo.fromLoadContext(this.loadContext(), false);
   1.321 +},
   1.322 +
   1.323 +getActiveCache: function(overload)
   1.324 +{
   1.325 +  // Note that this is the current active cache in the cache stack, not the
   1.326 +  // one associated with this window.
   1.327 +  var serv = Cc["@mozilla.org/network/application-cache-service;1"]
   1.328 +             .getService(Ci.nsIApplicationCacheService);
   1.329 +  var groupID = serv.buildGroupID(this.manifestURL(overload), this.loadContextInfo());
   1.330 +  return serv.getActiveCache(groupID);
   1.331 +},
   1.332 +
   1.333 +getActiveSession: function()
   1.334 +{
   1.335 +  var cache = this.getActiveCache();
   1.336 +  if (!cache) {
   1.337 +    return null;
   1.338 +  }
   1.339 +
   1.340 +  var cacheService = Cc["@mozilla.org/network/cache-service;1"]
   1.341 +                     .getService(Ci.nsICacheService);
   1.342 +  return cacheService.createSession(cache.clientID,
   1.343 +                                    Ci.nsICache.STORE_OFFLINE,
   1.344 +                                    true);
   1.345 +},
   1.346 +
   1.347 +priv: function(func)
   1.348 +{
   1.349 +  var self = this;
   1.350 +  return function() {
   1.351 +    func(arguments);
   1.352 +  }
   1.353 +},
   1.354 +
   1.355 +checkCacheEntries: function(entries, callback)
   1.356 +{
   1.357 +  var checkNextEntry = function() {
   1.358 +    if (entries.length == 0) {
   1.359 +      setTimeout(OfflineTest.priv(callback), 0);
   1.360 +    } else {
   1.361 +      OfflineTest.checkCache(entries[0][0], entries[0][1], checkNextEntry);
   1.362 +      entries.shift();
   1.363 +    }
   1.364 +  }
   1.365 +
   1.366 +  checkNextEntry();
   1.367 +},
   1.368 +
   1.369 +checkCache: function(url, expectEntry, callback)
   1.370 +{
   1.371 +  var cacheSession = this.getActiveSession();
   1.372 +  this._checkCache(cacheSession, url, expectEntry, callback);
   1.373 +},
   1.374 +
   1.375 +_checkCache: function(cacheSession, url, expectEntry, callback)
   1.376 +{
   1.377 +  if (!cacheSession) {
   1.378 +    if (expectEntry) {
   1.379 +      this.ok(false, url + " should exist in the offline cache (no session)");
   1.380 +    } else {
   1.381 +      this.ok(true, url + " should not exist in the offline cache (no session)");
   1.382 +    }
   1.383 +    if (callback) setTimeout(this.priv(callback), 0);
   1.384 +    return;
   1.385 +  }
   1.386 +
   1.387 +  var _checkCacheListener = {
   1.388 +    onCacheEntryAvailable: function(entry, access, status) {
   1.389 +      if (entry) {
   1.390 +        if (expectEntry) {
   1.391 +          OfflineTest.ok(true, url + " should exist in the offline cache");
   1.392 +        } else {
   1.393 +          OfflineTest.ok(false, url + " should not exist in the offline cache");
   1.394 +        }
   1.395 +        entry.close();
   1.396 +      } else {
   1.397 +        if (status == NS_ERROR_CACHE_KEY_NOT_FOUND) {
   1.398 +          if (expectEntry) {
   1.399 +            OfflineTest.ok(false, url + " should exist in the offline cache");
   1.400 +          } else {
   1.401 +            OfflineTest.ok(true, url + " should not exist in the offline cache");
   1.402 +          }
   1.403 +        } else if (status == NS_ERROR_CACHE_KEY_WAIT_FOR_VALIDATION) {
   1.404 +          // There was a cache key that we couldn't access yet, that's good enough.
   1.405 +          if (expectEntry) {
   1.406 +            OfflineTest.ok(!mustBeValid, url + " should exist in the offline cache");
   1.407 +          } else {
   1.408 +            OfflineTest.ok(mustBeValid, url + " should not exist in the offline cache");
   1.409 +          }
   1.410 +        } else {
   1.411 +          OfflineTest.ok(false, "got invalid error for " + url);
   1.412 +        }
   1.413 +      }
   1.414 +      if (callback) setTimeout(OfflineTest.priv(callback), 0);
   1.415 +    }
   1.416 +  };
   1.417 +
   1.418 +  cacheSession.asyncOpenCacheEntry(url,
   1.419 +                                   Ci.nsICache.ACCESS_READ,
   1.420 +                                   _checkCacheListener,
   1.421 +                                   false);
   1.422 +},
   1.423 +
   1.424 +setSJSState: function(sjsPath, stateQuery)
   1.425 +{
   1.426 +  var client = new XMLHttpRequest();
   1.427 +  client.open("GET", sjsPath + "?state=" + stateQuery, false);
   1.428 +
   1.429 +  var appcachechannel = SpecialPowers.wrap(client).channel.QueryInterface(Ci.nsIApplicationCacheChannel);
   1.430 +  appcachechannel.chooseApplicationCache = false;
   1.431 +  appcachechannel.inheritApplicationCache = false;
   1.432 +  appcachechannel.applicationCache = null;
   1.433 +
   1.434 +  client.send();
   1.435 +
   1.436 +  if (stateQuery == "")
   1.437 +    delete this._SJSsStated[sjsPath];
   1.438 +  else
   1.439 +    this._SJSsStated.push(sjsPath);
   1.440 +}
   1.441 +
   1.442 +};

mercurial