Bug 1003860 - Service worker cache for storage actor. r=mratcliffe
☠☠ backed out by 2b9d095eccfd ☠ ☠
authorAlexandre Poirot <poirot.alex@gmail.com>
Wed, 20 Jan 2016 14:09:25 -0800
changeset 317904 8d93e84979b502359d728845813f13224378a201
parent 317903 22d80ccb4626eaf220e7d00abe713893f88fe26c
child 317905 8737105685f1b5feeb592791aa4ea498f491da96
push id1079
push userjlund@mozilla.com
push dateFri, 15 Apr 2016 21:02:33 +0000
treeherdermozilla-release@575fbf6786d5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmratcliffe
bugs1003860
milestone46.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1003860 - Service worker cache for storage actor. r=mratcliffe
devtools/client/locales/en-US/storage.properties
devtools/client/storage/test/browser_storage_basic.js
devtools/client/storage/test/head.js
devtools/client/storage/test/storage-listings.html
devtools/server/actors/storage.js
--- a/devtools/client/locales/en-US/storage.properties
+++ b/devtools/client/locales/en-US/storage.properties
@@ -49,16 +49,17 @@ table.emptyText=No data present for sele
 
 # LOCALIZATION NOTE (tree.labels.*):
 # These strings are the labels for Storage type groups present in the Storage
 # Tree, like cookies, local storage etc.
 tree.labels.cookies=Cookies
 tree.labels.localStorage=Local Storage
 tree.labels.sessionStorage=Session Storage
 tree.labels.indexedDB=Indexed DB
+tree.labels.Cache=Cache Storage
 
 # LOCALIZATION NOTE (table.headers.*.*):
 # These strings are the header names of the columns in the Storage Table for
 # each type of storage available through the Storage Tree to the side.
 table.headers.cookies.name=Name
 table.headers.cookies.path=Path
 table.headers.cookies.host=Domain
 table.headers.cookies.expires=Expires on
@@ -79,16 +80,19 @@ table.headers.cookies.isSecure=isSecure
 table.headers.cookies.isDomain=isDomain
 
 table.headers.localStorage.name=Key
 table.headers.localStorage.value=Value
 
 table.headers.sessionStorage.name=Key
 table.headers.sessionStorage.value=Value
 
+table.headers.Cache.url=URL
+table.headers.Cache.status=Status
+
 table.headers.indexedDB.name=Key
 table.headers.indexedDB.db=Database Name
 table.headers.indexedDB.objectStore=Object Store Name
 table.headers.indexedDB.value=Value
 table.headers.indexedDB.origin=Origin
 table.headers.indexedDB.version=Version
 table.headers.indexedDB.objectStores=Object Stores
 table.headers.indexedDB.keyPath=Key
--- a/devtools/client/storage/test/browser_storage_basic.js
+++ b/devtools/client/storage/test/browser_storage_basic.js
@@ -56,16 +56,19 @@ const testCases = [
   [["indexedDB", "https://sectest1.example.org", "idb-s1"],
    ["obj-s1"]],
   [["indexedDB", "https://sectest1.example.org", "idb-s2"],
    ["obj-s2"]],
   [["indexedDB", "https://sectest1.example.org", "idb-s1", "obj-s1"],
    [6, 7]],
   [["indexedDB", "https://sectest1.example.org", "idb-s2", "obj-s2"],
    [16]],
+  [["Cache", "http://test1.example.org", "plop"],
+   [MAIN_DOMAIN + "404_cached_file.js", MAIN_DOMAIN + "browser_storage_basic.js"]],
+
 ];
 
 /**
  * Test that the desired number of tree items are present
  */
 function testTree() {
   let doc = gPanelWindow.document;
   for (let item of testCases) {
--- a/devtools/client/storage/test/head.js
+++ b/devtools/client/storage/test/head.js
@@ -9,36 +9,40 @@ var { require } = Cu.import("resource://
 var { TargetFactory } = require("devtools/client/framework/target");
 var promise = require("promise");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 
 const SPLIT_CONSOLE_PREF = "devtools.toolbox.splitconsoleEnabled";
 const STORAGE_PREF = "devtools.storage.enabled";
 const DUMPEMIT_PREF = "devtools.dump.emit";
 const DEBUGGERLOG_PREF = "devtools.debugger.log";
+// Allows Cache API to be working on usage `http` test page
+const CACHES_ON_HTTP_PREF = "dom.caches.testing.enabled";
 const PATH = "browser/devtools/client/storage/test/";
 const MAIN_DOMAIN = "http://test1.example.org/" + PATH;
 const ALT_DOMAIN = "http://sectest1.example.org/" + PATH;
 const ALT_DOMAIN_SECURED = "https://sectest1.example.org:443/" + PATH;
 
 waitForExplicitFinish();
 
 var gToolbox, gPanelWindow, gWindow, gUI;
 
 // Services.prefs.setBoolPref(DUMPEMIT_PREF, true);
 // Services.prefs.setBoolPref(DEBUGGERLOG_PREF, true);
 
 Services.prefs.setBoolPref(STORAGE_PREF, true);
+Services.prefs.setBoolPref(CACHES_ON_HTTP_PREF, true);
 DevToolsUtils.testing = true;
 registerCleanupFunction(() => {
   gToolbox = gPanelWindow = gWindow = gUI = null;
   Services.prefs.clearUserPref(STORAGE_PREF);
   Services.prefs.clearUserPref(SPLIT_CONSOLE_PREF);
   Services.prefs.clearUserPref(DUMPEMIT_PREF);
   Services.prefs.clearUserPref(DEBUGGERLOG_PREF);
+  Services.prefs.clearUserPref(CACHES_ON_HTTP_PREF);
   DevToolsUtils.testing = false;
   while (gBrowser.tabs.length > 1) {
     gBrowser.removeCurrentTab();
   }
 });
 
 /**
  * Add a new test tab in the browser and load the given url.
--- a/devtools/client/storage/test/storage-listings.html
+++ b/devtools/client/storage/test/storage-listings.html
@@ -92,32 +92,41 @@ let idbGenerator = function*() {
 
 function deleteDB(dbName) {
   return new Promise(resolve => {
     dump("removing database " + dbName + " from " + document.location + "\n");
     indexedDB.deleteDatabase(dbName).onsuccess = resolve;
   });
 }
 
+let cacheGenerator = function*() {
+  let cache = yield caches.open("plop");
+  yield cache.add("404_cached_file.js");
+  yield cache.add("browser_storage_basic.js");
+};
+
 window.setup = function*() {
   yield idbGenerator();
+  yield cacheGenerator();
 };
 
 window.clear = function*() {
   document.cookie = "c1=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/browser";
   document.cookie =
     "c3=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; secure=true";
   document.cookie =
     "cs2=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain=" +
     partialHostname;
 
   localStorage.clear();
   sessionStorage.clear();
 
   yield deleteDB("idb1");
   yield deleteDB("idb2");
 
+  yield caches.delete("plop");
+
   dump("removed cookies, localStorage, sessionStorage and indexedDB data " +
        "from " + document.location + "\n");
 };
 </script>
 </body>
 </html>
--- a/devtools/server/actors/storage.js
+++ b/devtools/server/actors/storage.js
@@ -917,16 +917,135 @@ StorageActors.createActor({
  * The Session Storage actor and front.
  */
 StorageActors.createActor({
   typeName: "sessionStorage",
   observationTopic: "dom-storage2-changed",
   storeObjectType: "storagestoreobject"
 }, getObjectForLocalOrSessionStorage("sessionStorage"));
 
+
+let CacheAttributes = [
+  "url",
+  "status",
+];
+types.addDictType("cacheobject", {
+  "url": "string",
+  "status": "string"
+});
+
+// Array of Cache store objects
+types.addDictType("cachestoreobject", {
+  total: "number",
+  offset: "number",
+  data: "array:nullable:cacheobject"
+});
+
+StorageActors.createActor({
+  typeName: "Cache",
+  storeObjectType: "cachestoreobject"
+}, {
+  getCachesForHost: Task.async(function*(host) {
+    let uri = Services.io.newURI(host, null, null);
+    let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
+
+    // The first argument tells if you want to get |content| cache or |chrome| cache.
+    // The |content| cache is the cache explicitely named by the web content
+    // (service worker or web page).
+    // The |chrome| cache is the cache implicitely cached by the platform, hosting the
+    // source file of the service worker.
+    let { CacheStorage } = this.storageActor.window;
+    let cache = new CacheStorage("content", principal);
+    return cache;
+  }),
+
+  preListStores: Task.async(function*() {
+    this.hostVsStores = new Map();
+    for (let host of this.hosts) {
+      yield this.populateStoresForHost(host);
+    }
+  }),
+
+  form: function(form, detail) {
+    if (detail === "actorid") {
+      return this.actorID;
+    }
+
+    let hosts = {};
+    for (let host of this.hosts) {
+      hosts[host] = this.getNamesForHost(host);
+    }
+
+    return {
+      actor: this.actorID,
+      hosts: hosts
+    };
+  },
+
+  getNamesForHost: function(host) {
+    // UI code expect each name to be a JSON string of an array :/
+    return [...this.hostVsStores.get(host).keys()].map(a => JSON.stringify([a]));
+  },
+
+  getValuesForHost: Task.async(function*(host, name) {
+    if (!name) return [];
+    // UI is weird and expect a JSON stringified array... and pass it back :/
+    name = JSON.parse(name)[0];
+
+    let cache = this.hostVsStores.get(host).get(name);
+    let requests = yield cache.keys();
+    let results = [];
+    for(let request of requests) {
+      let response = yield cache.match(request);
+      // Unwrap the response to get access to all its properties if the
+      // response happen to be 'opaque', when it is a Cross Origin Request.
+      response = response.cloneUnfiltered();
+      results.push(yield this.processEntry(request, response));
+    }
+    return results;
+  }),
+
+  processEntry: Task.async(function*(request, response) {
+    return {
+      url: String(request.url),
+      status: String(response.statusText),
+    };
+  }),
+
+  getHostName: function(location) {
+    if (!location.host) {
+      return location.href;
+    }
+    return location.protocol + "//" + location.host;
+  },
+
+  populateStoresForHost: Task.async(function*(host, window) {
+    let storeMap = new Map();
+    let caches = yield this.getCachesForHost(host);
+    for (let name of (yield caches.keys())) {
+      storeMap.set(name, (yield caches.open(name)));
+    }
+    this.hostVsStores.set(host, storeMap);
+  }),
+
+  populateStoresForHosts: function() {},
+
+  /**
+   * Given a url, correctly determine its protocol + hostname part.
+   */
+  getSchemaAndHost: function(url) {
+    let uri = Services.io.newURI(url, null, null);
+    return uri.scheme + "://" + uri.hostPort;
+  },
+
+  toStoreObject: function(item) {
+    return item;
+  },
+});
+
 /**
  * Code related to the Indexed DB actor and front
  */
 
 // Metadata holder objects for various components of Indexed DB
 
 /**
  * Meta data object for a particular index in an object store