michael@0: /* -*- Mode: Javasript; indent-tab-mode: nil; js-indent-level: 2 -*- */ michael@0: /* michael@0: * This testcase performs 3 requests against the offline cache. They michael@0: * are michael@0: * michael@0: * - start_cache_nonpinned_app1() michael@0: * michael@0: * - Request nsOfflineCacheService to skip pages (4) of app1 on michael@0: * the cache storage. michael@0: * michael@0: * - The offline cache storage is empty at this monent. michael@0: * michael@0: * - start_cache_nonpinned_app2_for_partial() michael@0: * michael@0: * - Request nsOfflineCacheService to skip pages of app2 on the michael@0: * cache storage. michael@0: * michael@0: * - The offline cache storage has only enough space for one more michael@0: * additional page. Only first of pages is really in the cache. michael@0: * michael@0: * - start_cache_pinned_app2_for_success() michael@0: * michael@0: * - Request nsOfflineCacheService to skip pages of app2 on the michael@0: * cache storage. michael@0: * michael@0: * - The offline cache storage has only enough space for one michael@0: * additional page. But, this is a pinned request, michael@0: * nsOfflineCacheService will make more space for this request michael@0: * by discarding app1 (non-pinned) michael@0: * michael@0: */ michael@0: michael@0: Cu.import("resource://testing-common/httpd.js"); michael@0: michael@0: // const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; michael@0: michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: michael@0: const kNS_OFFLINECACHEUPDATESERVICE_CONTRACTID = michael@0: "@mozilla.org/offlinecacheupdate-service;1"; michael@0: michael@0: const kManifest1 = "CACHE MANIFEST\n" + michael@0: "/pages/foo1\n" + michael@0: "/pages/foo2\n" + michael@0: "/pages/foo3\n" + michael@0: "/pages/foo4\n"; michael@0: const kManifest2 = "CACHE MANIFEST\n" + michael@0: "/pages/foo5\n" + michael@0: "/pages/foo6\n" + michael@0: "/pages/foo7\n" + michael@0: "/pages/foo8\n"; michael@0: michael@0: const kDataFileSize = 1024; // file size for each content page michael@0: const kCacheSize = kDataFileSize * 5; // total space for offline cache storage michael@0: michael@0: XPCOMUtils.defineLazyGetter(this, "kHttpLocation", function() { michael@0: return "http://localhost:" + httpServer.identity.primaryPort + "/"; michael@0: }); michael@0: michael@0: XPCOMUtils.defineLazyGetter(this, "kHttpLocation_ip", function() { michael@0: return "http://127.0.0.1:" + httpServer.identity.primaryPort + "/"; michael@0: }); michael@0: michael@0: function manifest1_handler(metadata, response) { michael@0: do_print("manifest1\n"); michael@0: response.setHeader("content-type", "text/cache-manifest"); michael@0: michael@0: response.write(kManifest1); michael@0: } michael@0: michael@0: function manifest2_handler(metadata, response) { michael@0: do_print("manifest2\n"); michael@0: response.setHeader("content-type", "text/cache-manifest"); michael@0: michael@0: response.write(kManifest2); michael@0: } michael@0: michael@0: function app_handler(metadata, response) { michael@0: do_print("app_handler\n"); michael@0: response.setHeader("content-type", "text/html"); michael@0: michael@0: response.write(""); michael@0: } michael@0: michael@0: function datafile_handler(metadata, response) { michael@0: do_print("datafile_handler\n"); michael@0: let data = ""; michael@0: michael@0: while(data.length < kDataFileSize) { michael@0: data = data + Math.random().toString(36).substring(2, 15); michael@0: } michael@0: michael@0: response.setHeader("content-type", "text/plain"); michael@0: response.write(data.substring(0, kDataFileSize)); michael@0: } michael@0: michael@0: var httpServer; michael@0: michael@0: function init_profile() { michael@0: var ps = Cc["@mozilla.org/preferences-service;1"] michael@0: .getService(Ci.nsIPrefBranch); michael@0: dump(ps.getBoolPref("browser.cache.offline.enable")); michael@0: ps.setBoolPref("browser.cache.offline.enable", true); michael@0: ps.setComplexValue("browser.cache.offline.parent_directory", michael@0: Ci.nsILocalFile, do_get_profile()); michael@0: } michael@0: michael@0: function init_http_server() { michael@0: httpServer = new HttpServer(); michael@0: httpServer.registerPathHandler("/app1", app_handler); michael@0: httpServer.registerPathHandler("/app2", app_handler); michael@0: httpServer.registerPathHandler("/app1.appcache", manifest1_handler); michael@0: httpServer.registerPathHandler("/app2.appcache", manifest2_handler); michael@0: for (i = 1; i <= 8; i++) { michael@0: httpServer.registerPathHandler("/pages/foo" + i, datafile_handler); michael@0: } michael@0: httpServer.start(-1); michael@0: } michael@0: michael@0: function init_cache_capacity() { michael@0: let prefs = Cc["@mozilla.org/preferences-service;1"] michael@0: .getService(Components.interfaces.nsIPrefBranch); michael@0: prefs.setIntPref("browser.cache.offline.capacity", kCacheSize / 1024); michael@0: } michael@0: michael@0: function clean_app_cache() { michael@0: evict_cache_entries("appcache"); michael@0: } michael@0: michael@0: function do_app_cache(manifestURL, pageURL, pinned) { michael@0: let update_service = Cc[kNS_OFFLINECACHEUPDATESERVICE_CONTRACTID]. michael@0: getService(Ci.nsIOfflineCacheUpdateService); michael@0: michael@0: Services.perms.add(manifestURL, michael@0: "pin-app", michael@0: pinned ? michael@0: Ci.nsIPermissionManager.ALLOW_ACTION : michael@0: Ci.nsIPermissionManager.DENY_ACTION); michael@0: michael@0: let update = michael@0: update_service.scheduleUpdate(manifestURL, michael@0: pageURL, michael@0: null); /* no window */ michael@0: michael@0: return update; michael@0: } michael@0: michael@0: function watch_update(update, stateChangeHandler, cacheAvailHandler) { michael@0: let observer = { michael@0: QueryInterface: function QueryInterface(iftype) { michael@0: return this; michael@0: }, michael@0: michael@0: updateStateChanged: stateChangeHandler, michael@0: applicationCacheAvailable: cacheAvailHandler michael@0: }; michael@0: update.addObserver(observer, false); michael@0: michael@0: return update; michael@0: } michael@0: michael@0: function start_and_watch_app_cache(manifestURL, michael@0: pageURL, michael@0: pinned, michael@0: stateChangeHandler, michael@0: cacheAvailHandler) { michael@0: let ioService = Cc["@mozilla.org/network/io-service;1"]. michael@0: getService(Ci.nsIIOService); michael@0: let update = do_app_cache(ioService.newURI(manifestURL, null, null), michael@0: ioService.newURI(pageURL, null, null), michael@0: pinned); michael@0: watch_update(update, stateChangeHandler, cacheAvailHandler); michael@0: return update; michael@0: } michael@0: michael@0: const {STATE_FINISHED: STATE_FINISHED, michael@0: STATE_CHECKING: STATE_CHECKING, michael@0: STATE_ERROR: STATE_ERROR } = Ci.nsIOfflineCacheUpdateObserver; michael@0: michael@0: /* michael@0: * Start caching app1 as a non-pinned app. michael@0: */ michael@0: function start_cache_nonpinned_app() { michael@0: do_print("Start non-pinned App1"); michael@0: start_and_watch_app_cache(kHttpLocation + "app1.appcache", michael@0: kHttpLocation + "app1", michael@0: false, michael@0: function (update, state) { michael@0: switch(state) { michael@0: case STATE_FINISHED: michael@0: start_cache_nonpinned_app2_for_partial(); michael@0: break; michael@0: michael@0: case STATE_ERROR: michael@0: do_throw("App1 cache state = " + state); michael@0: break; michael@0: } michael@0: }, michael@0: function (appcahe) { michael@0: do_print("app1 avail " + appcache + "\n"); michael@0: }); michael@0: } michael@0: michael@0: /* michael@0: * Start caching app2 as a non-pinned app. michael@0: * michael@0: * This cache request is supposed to be saved partially in the cache michael@0: * storage for running out of the cache storage. The offline cache michael@0: * storage can hold 5 files at most. (kDataFileSize bytes for each michael@0: * file) michael@0: */ michael@0: function start_cache_nonpinned_app2_for_partial() { michael@0: let error_count = [0]; michael@0: do_print("Start non-pinned App2 for partial\n"); michael@0: start_and_watch_app_cache(kHttpLocation_ip + "app2.appcache", michael@0: kHttpLocation_ip + "app2", michael@0: false, michael@0: function (update, state) { michael@0: switch(state) { michael@0: case STATE_FINISHED: michael@0: start_cache_pinned_app2_for_success(); michael@0: break; michael@0: michael@0: case STATE_ERROR: michael@0: do_throw("App2 cache state = " + state); michael@0: break; michael@0: } michael@0: }, michael@0: function (appcahe) { michael@0: }); michael@0: } michael@0: michael@0: /* michael@0: * Start caching app2 as a pinned app. michael@0: * michael@0: * This request use IP address (127.0.0.1) as the host name instead of michael@0: * the one used by app1. Because, app1 is also pinned when app2 is michael@0: * pinned if they have the same host name (localhost). michael@0: */ michael@0: function start_cache_pinned_app2_for_success() { michael@0: let error_count = [0]; michael@0: do_print("Start pinned App2 for success\n"); michael@0: start_and_watch_app_cache(kHttpLocation_ip + "app2.appcache", michael@0: kHttpLocation_ip + "app2", michael@0: true, michael@0: function (update, state) { michael@0: switch(state) { michael@0: case STATE_FINISHED: michael@0: do_check_true(error_count[0] == 0, michael@0: "Do not discard app1?"); michael@0: httpServer.stop(do_test_finished); michael@0: break; michael@0: michael@0: case STATE_ERROR: michael@0: do_print("STATE_ERROR\n"); michael@0: error_count[0]++; michael@0: break; michael@0: } michael@0: }, michael@0: function (appcahe) { michael@0: do_print("app2 avail " + appcache + "\n"); michael@0: }); michael@0: } michael@0: michael@0: function run_test() { michael@0: if (typeof _XPCSHELL_PROCESS == "undefined" || michael@0: _XPCSHELL_PROCESS != "child") { michael@0: init_profile(); michael@0: init_cache_capacity(); michael@0: clean_app_cache(); michael@0: } michael@0: michael@0: init_http_server(); michael@0: start_cache_nonpinned_app(); michael@0: do_test_pending(); michael@0: }