Bug 1263324 - Part 2: Add test cases to test context id for devtools storage inspector. r=miker
authorTim Huang <tihuang@mozilla.com>
Tue, 24 Jan 2017 14:45:42 +0800
changeset 340934 2c9ed54ef2e183336c4903c84d853dc0ff0a38ee
parent 340933 3f25a442f75bbbc270d84dd7f3aff402919358ff
child 340935 589a01fb47900ae737c74f6b288d3245cece847e
push id36958
push userryanvm@gmail.com
push dateMon, 06 Feb 2017 16:49:55 +0000
treeherderautoland@2c9ed54ef2e1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmiker
bugs1263324
milestone54.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 1263324 - Part 2: Add test cases to test context id for devtools storage inspector. r=miker
devtools/client/framework/test/shared-head.js
devtools/client/storage/test/browser.ini
devtools/client/storage/test/browser_storage_basic_usercontextid.js
devtools/client/storage/test/browser_storage_delete_usercontextid.js
devtools/client/storage/test/head.js
devtools/client/storage/test/storage-listings-usercontextid.html
devtools/client/storage/test/storage-secured-iframe-usercontextid.html
devtools/client/storage/test/storage-unsecured-iframe-usercontextid.html
--- a/devtools/client/framework/test/shared-head.js
+++ b/devtools/client/framework/test/shared-head.js
@@ -106,25 +106,27 @@ registerCleanupFunction(function* cleanu
 });
 
 /**
  * Add a new test tab in the browser and load the given url.
  * @param {String} url The url to be loaded in the new tab
  * @param {Object} options Object with various optional fields:
  *   - {Boolean} background If true, open the tab in background
  *   - {ChromeWindow} window Firefox top level window we should use to open the tab
+ *   - {Number} userContextId The userContextId of the tab.
  * @return a promise that resolves to the tab object when the url is loaded
  */
 var addTab = Task.async(function* (url, options = { background: false, window: window }) {
   info("Adding a new tab with URL: " + url);
 
   let { background } = options;
   let { gBrowser } = options.window ? options.window : window;
+  let { userContextId } = options;
 
-  let tab = gBrowser.addTab(url);
+  let tab = gBrowser.addTab(url, {userContextId});
   if (!background) {
     gBrowser.selectedTab = tab;
   }
   yield BrowserTestUtils.browserLoaded(tab.linkedBrowser);
 
   info("Tab added and finished loading");
 
   return tab;
--- a/devtools/client/storage/test/browser.ini
+++ b/devtools/client/storage/test/browser.ini
@@ -4,39 +4,46 @@ subsuite = devtools
 support-files =
   storage-cache-error.html
   storage-complex-values.html
   storage-cookies.html
   storage-empty-objectstores.html
   storage-idb-delete-blocked.html
   storage-indexeddb-duplicate-names.html
   storage-listings.html
+  storage-listings-usercontextid.html
   storage-listings-with-fragment.html
   storage-localstorage.html
   storage-overflow.html
   storage-search.html
   storage-secured-iframe.html
+  storage-secured-iframe-usercontextid.html
   storage-sessionstorage.html
   storage-unsecured-iframe.html
+  storage-unsecured-iframe-usercontextid.html
   storage-updates.html
   head.js
   !/devtools/client/framework/test/shared-head.js
 
 [browser_storage_basic.js]
+[browser_storage_basic_usercontextid.js]
+tags = usercontextid
 [browser_storage_basic_with_fragment.js]
 [browser_storage_cache_delete.js]
 [browser_storage_cache_error.js]
 [browser_storage_cookies_delete_all.js]
 [browser_storage_cookies_domain.js]
 [browser_storage_cookies_edit.js]
 [browser_storage_cookies_edit_keyboard.js]
 [browser_storage_cookies_tab_navigation.js]
 [browser_storage_delete.js]
 [browser_storage_delete_all.js]
 [browser_storage_delete_tree.js]
+[browser_storage_delete_usercontextid.js]
+tags = usercontextid
 [browser_storage_dom_cache_disabled.js]
 [browser_storage_dynamic_updates_cookies.js]
 [browser_storage_dynamic_updates_localStorage.js]
 [browser_storage_dynamic_updates_sessionStorage.js]
 [browser_storage_empty_objectstores.js]
 [browser_storage_indexeddb_delete.js]
 [browser_storage_indexeddb_delete_blocked.js]
 [browser_storage_indexeddb_duplicate_names.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/storage/test/browser_storage_basic_usercontextid.js
@@ -0,0 +1,184 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// A test to check that the storage inspector is working correctly with
+// userContextId.
+
+"use strict";
+
+const testCases = [
+  [
+    ["cookies", "http://test1.example.org"],
+    [
+      getCookieId("c1", "test1.example.org", "/browser"),
+      getCookieId("cs2", ".example.org", "/"),
+      getCookieId("c3", "test1.example.org", "/"),
+      getCookieId("uc1", ".example.org", "/")
+    ]
+  ],
+  [
+    ["cookies", "https://sectest1.example.org"],
+    [
+      getCookieId("uc1", ".example.org", "/"),
+      getCookieId("cs2", ".example.org", "/"),
+      getCookieId("sc1", "sectest1.example.org", "/browser/devtools/client/storage/test/")
+    ]
+  ],
+  [["localStorage", "http://test1.example.org"],
+   ["ls1", "ls2"]],
+  [["localStorage", "http://sectest1.example.org"],
+   ["iframe-u-ls1"]],
+  [["localStorage", "https://sectest1.example.org"],
+   ["iframe-s-ls1"]],
+  [["sessionStorage", "http://test1.example.org"],
+   ["ss1"]],
+  [["sessionStorage", "http://sectest1.example.org"],
+   ["iframe-u-ss1", "iframe-u-ss2"]],
+  [["sessionStorage", "https://sectest1.example.org"],
+   ["iframe-s-ss1"]],
+  [["indexedDB", "http://test1.example.org"],
+   ["idb1 (default)", "idb2 (default)"]],
+  [["indexedDB", "http://test1.example.org", "idb1 (default)"],
+   ["obj1", "obj2"]],
+  [["indexedDB", "http://test1.example.org", "idb2 (default)"],
+   ["obj3"]],
+  [["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"],
+   [1, 2, 3]],
+  [["indexedDB", "http://test1.example.org", "idb1 (default)", "obj2"],
+   [1]],
+  [["indexedDB", "http://test1.example.org", "idb2 (default)", "obj3"],
+   []],
+  [["indexedDB", "http://sectest1.example.org"],
+   []],
+  [["indexedDB", "https://sectest1.example.org"],
+   ["idb-s1 (default)", "idb-s2 (default)"]],
+  [["indexedDB", "https://sectest1.example.org", "idb-s1 (default)"],
+   ["obj-s1"]],
+  [["indexedDB", "https://sectest1.example.org", "idb-s2 (default)"],
+   ["obj-s2"]],
+  [["indexedDB", "https://sectest1.example.org", "idb-s1 (default)", "obj-s1"],
+   [6, 7]],
+  [["indexedDB", "https://sectest1.example.org", "idb-s2 (default)", "obj-s2"],
+   [16]],
+  [["Cache", "http://test1.example.org", "plop"],
+   [MAIN_DOMAIN + "404_cached_file.js",
+    MAIN_DOMAIN + "browser_storage_basic.js"]],
+];
+
+const testCasesUserContextId = [
+  [
+    ["cookies", "http://test1.example.org"],
+    [
+      getCookieId("c1uc1", "test1.example.org", "/browser"),
+      getCookieId("cs2uc1", ".example.org", "/"),
+      getCookieId("c3uc1", "test1.example.org", "/"),
+      getCookieId("uc1uc1", ".example.org", "/")
+    ]
+  ],
+  [
+    ["cookies", "https://sectest1.example.org"],
+    [
+      getCookieId("uc1uc1", ".example.org", "/"),
+      getCookieId("cs2uc1", ".example.org", "/"),
+      getCookieId("sc1uc1", "sectest1.example.org",
+        "/browser/devtools/client/storage/test/")
+    ]
+  ],
+  [["localStorage", "http://test1.example.org"],
+   ["ls1uc1", "ls2uc1"]],
+  [["localStorage", "http://sectest1.example.org"],
+   ["iframe-u-ls1uc1"]],
+  [["localStorage", "https://sectest1.example.org"],
+   ["iframe-s-ls1uc1"]],
+  [["sessionStorage", "http://test1.example.org"],
+   ["ss1uc1"]],
+  [["sessionStorage", "http://sectest1.example.org"],
+   ["iframe-u-ss1uc1", "iframe-u-ss2uc1"]],
+  [["sessionStorage", "https://sectest1.example.org"],
+   ["iframe-s-ss1uc1"]],
+  [["indexedDB", "http://test1.example.org"],
+   ["idb1uc1 (default)", "idb2uc1 (default)"]],
+  [["indexedDB", "http://test1.example.org", "idb1uc1 (default)"],
+   ["obj1uc1", "obj2uc1"]],
+  [["indexedDB", "http://test1.example.org", "idb2uc1 (default)"],
+   ["obj3uc1"]],
+  [["indexedDB", "http://test1.example.org", "idb1uc1 (default)", "obj1uc1"],
+   [1, 2, 3]],
+  [["indexedDB", "http://test1.example.org", "idb1uc1 (default)", "obj2uc1"],
+   [1]],
+  [["indexedDB", "http://test1.example.org", "idb2uc1 (default)", "obj3uc1"],
+   []],
+  [["indexedDB", "http://sectest1.example.org"],
+   []],
+  [["indexedDB", "https://sectest1.example.org"],
+   ["idb-s1uc1 (default)", "idb-s2uc1 (default)"]],
+  [["indexedDB", "https://sectest1.example.org", "idb-s1uc1 (default)"],
+   ["obj-s1uc1"]],
+  [["indexedDB", "https://sectest1.example.org", "idb-s2uc1 (default)"],
+   ["obj-s2uc1"]],
+  [["indexedDB", "https://sectest1.example.org", "idb-s1uc1 (default)", "obj-s1uc1"],
+   [6, 7]],
+  [["indexedDB", "https://sectest1.example.org", "idb-s2uc1 (default)", "obj-s2uc1"],
+   [16]],
+  [["Cache", "http://test1.example.org", "plopuc1"],
+   [MAIN_DOMAIN + "404_cached_file.js",
+    MAIN_DOMAIN + "browser_storage_basic.js"]],
+];
+
+/**
+ * Test that the desired number of tree items are present
+ */
+function testTree(tests) {
+  let doc = gPanelWindow.document;
+  for (let [item] of tests) {
+    ok(doc.querySelector("[data-id='" + JSON.stringify(item) + "']"),
+       "Tree item " + item[0] + " should be present in the storage tree");
+  }
+}
+
+/**
+ * Test that correct table entries are shown for each of the tree item
+ */
+function* testTables(tests) {
+  let doc = gPanelWindow.document;
+  // Expand all nodes so that the synthesized click event actually works
+  gUI.tree.expandAll();
+
+  // First tree item is already selected so no clicking and waiting for update
+  for (let id of tests[0][1]) {
+    ok(doc.querySelector(".table-widget-cell[data-id='" + id + "']"),
+       "Table item " + id + " should be present");
+  }
+
+  // Click rest of the tree items and wait for the table to be updated
+  for (let [treeItem, items] of tests.slice(1)) {
+    yield selectTreeItem(treeItem);
+
+    // Check whether correct number of items are present in the table
+    is(doc.querySelectorAll(
+         ".table-widget-wrapper:first-of-type .table-widget-cell"
+       ).length, items.length, "Number of items in table is correct");
+
+    // Check if all the desired items are present in the table
+    for (let id of items) {
+      ok(doc.querySelector(".table-widget-cell[data-id='" + id + "']"),
+         "Table item " + id + " should be present");
+    }
+  }
+}
+
+add_task(function* () {
+  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html");
+
+  testTree(testCases);
+  yield testTables(testCases);
+
+  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings-usercontextid.html",
+                               {userContextId: 1});
+
+  testTree(testCasesUserContextId);
+  yield testTables(testCasesUserContextId);
+
+  yield finishTests();
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/storage/test/browser_storage_delete_usercontextid.js
@@ -0,0 +1,174 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* import-globals-from ../../framework/test/shared-head.js */
+
+"use strict";
+
+// Test deleting storage items with userContextId.
+
+// The items that will be deleted.
+const TEST_CASES = [
+  [["localStorage", "http://test1.example.org"],
+   "ls1", "name"],
+  [["sessionStorage", "http://test1.example.org"],
+   "ss1", "name"],
+  [
+    ["cookies", "http://test1.example.org"],
+    getCookieId("c1", "test1.example.org", "/browser"), "name"
+  ],
+  [["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"],
+   1, "name"],
+  [["Cache", "http://test1.example.org", "plop"],
+   MAIN_DOMAIN + "404_cached_file.js", "url"],
+];
+
+// The storage items that should exist for default userContextId
+const storageItemsForDefault = [
+  [
+    ["cookies", "http://test1.example.org"],
+    [
+      getCookieId("c1", "test1.example.org", "/browser"),
+      getCookieId("cs2", ".example.org", "/"),
+      getCookieId("c3", "test1.example.org", "/"),
+      getCookieId("uc1", ".example.org", "/")
+    ]
+  ],
+  [
+    ["cookies", "https://sectest1.example.org"],
+    [
+      getCookieId("uc1", ".example.org", "/"),
+      getCookieId("cs2", ".example.org", "/"),
+      getCookieId("sc1", "sectest1.example.org", "/browser/devtools/client/storage/test/")
+    ]
+  ],
+  [["localStorage", "http://test1.example.org"],
+   ["ls1", "ls2"]],
+  [["localStorage", "http://sectest1.example.org"],
+   ["iframe-u-ls1"]],
+  [["localStorage", "https://sectest1.example.org"],
+   ["iframe-s-ls1"]],
+  [["sessionStorage", "http://test1.example.org"],
+   ["ss1"]],
+  [["sessionStorage", "http://sectest1.example.org"],
+   ["iframe-u-ss1", "iframe-u-ss2"]],
+  [["sessionStorage", "https://sectest1.example.org"],
+   ["iframe-s-ss1"]],
+  [["indexedDB", "http://test1.example.org"],
+   ["idb1 (default)", "idb2 (default)"]],
+  [["indexedDB", "http://test1.example.org", "idb1 (default)"],
+   ["obj1", "obj2"]],
+  [["indexedDB", "http://test1.example.org", "idb2 (default)"],
+   ["obj3"]],
+  [["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"],
+   [1, 2, 3]],
+  [["indexedDB", "http://test1.example.org", "idb1 (default)", "obj2"],
+   [1]],
+  [["indexedDB", "http://test1.example.org", "idb2 (default)", "obj3"],
+   []],
+  [["indexedDB", "http://sectest1.example.org"],
+   []],
+  [["indexedDB", "https://sectest1.example.org"],
+   ["idb-s1 (default)", "idb-s2 (default)"]],
+  [["indexedDB", "https://sectest1.example.org", "idb-s1 (default)"],
+   ["obj-s1"]],
+  [["indexedDB", "https://sectest1.example.org", "idb-s2 (default)"],
+   ["obj-s2"]],
+  [["indexedDB", "https://sectest1.example.org", "idb-s1 (default)", "obj-s1"],
+   [6, 7]],
+  [["indexedDB", "https://sectest1.example.org", "idb-s2 (default)", "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(tests) {
+  let doc = gPanelWindow.document;
+  for (let [item] of tests) {
+    ok(doc.querySelector("[data-id='" + JSON.stringify(item) + "']"),
+       "Tree item " + item[0] + " should be present in the storage tree");
+  }
+}
+
+/**
+ * Test that correct table entries are shown for each of the tree item
+ */
+function* testTables(tests) {
+  let doc = gPanelWindow.document;
+  // Expand all nodes so that the synthesized click event actually works
+  gUI.tree.expandAll();
+
+  // First tree item is already selected so no clicking and waiting for update
+  for (let id of tests[0][1]) {
+    ok(doc.querySelector(".table-widget-cell[data-id='" + id + "']"),
+       "Table item " + id + " should be present");
+  }
+
+  // Click rest of the tree items and wait for the table to be updated
+  for (let [treeItem, items] of tests.slice(1)) {
+    yield selectTreeItem(treeItem);
+
+    // Check whether correct number of items are present in the table
+    is(doc.querySelectorAll(
+         ".table-widget-wrapper:first-of-type .table-widget-cell"
+       ).length, items.length, "Number of items in table is correct");
+
+    // Check if all the desired items are present in the table
+    for (let id of items) {
+      ok(doc.querySelector(".table-widget-cell[data-id='" + id + "']"),
+         "Table item " + id + " should be present");
+    }
+  }
+}
+
+add_task(function* () {
+  // First, open a tab with the default userContextId and setup its storages.
+  let tabDefault = yield openTab(MAIN_DOMAIN + "storage-listings.html");
+
+  // Second, start testing for userContextId 1.
+  // We use the same item name as the default page has to see deleting items
+  // from userContextId 1 will affect default one or not.
+  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html", {userContextId: 1});
+
+  let contextMenu = gPanelWindow.document.getElementById("storage-table-popup");
+  let menuDeleteItem = contextMenu.querySelector("#storage-table-popup-delete");
+
+  for (let [ treeItem, rowName, cellToClick] of TEST_CASES) {
+    let treeItemName = treeItem.join(" > ");
+
+    info(`Selecting tree item ${treeItemName}`);
+    yield selectTreeItem(treeItem);
+
+    let row = getRowCells(rowName);
+    ok(gUI.table.items.has(rowName), `There is a row '${rowName}' in ${treeItemName}`);
+
+    let eventWait = gUI.once("store-objects-updated");
+
+    yield waitForContextMenu(contextMenu, row[cellToClick], () => {
+      info(`Opened context menu in ${treeItemName}, row '${rowName}'`);
+      menuDeleteItem.click();
+      let truncatedRowName = String(rowName).replace(SEPARATOR_GUID, "-").substr(0, 16);
+      ok(menuDeleteItem.getAttribute("label").includes(truncatedRowName),
+        `Context menu item label contains '${rowName}' (maybe truncated)`);
+    });
+
+    yield eventWait;
+
+    ok(!gUI.table.items.has(rowName),
+      `There is no row '${rowName}' in ${treeItemName} after deletion`);
+  }
+
+  // Final, we see that the default tab is intact or not.
+  yield BrowserTestUtils.switchTab(gBrowser, tabDefault);
+  yield openStoragePanel();
+
+  testTree(storageItemsForDefault);
+  yield testTables(storageItemsForDefault);
+
+  yield finishTests();
+});
--- a/devtools/client/storage/test/head.js
+++ b/devtools/client/storage/test/head.js
@@ -44,26 +44,25 @@ registerCleanupFunction(() => {
   Services.prefs.clearUserPref(DOM_CACHE);
   Services.prefs.clearUserPref(DUMPEMIT_PREF);
   Services.prefs.clearUserPref(SPLIT_CONSOLE_PREF);
   Services.prefs.clearUserPref(STORAGE_PREF);
 });
 
 /**
  * This generator function opens the given url in a new tab, then sets up the
- * page by waiting for all cookies, indexedDB items etc. to be created; Then
- * opens the storage inspector and waits for the storage tree and table to be
- * populated.
+ * page by waiting for all cookies, indexedDB items etc.
  *
  * @param url {String} The url to be opened in the new tab
+ * @param options {Object} The tab options for the new tab
  *
- * @return {Promise} A promise that resolves after storage inspector is ready
+ * @return {Promise} A promise that resolves after the tab is ready
  */
-function* openTabAndSetupStorage(url) {
-  let tab = yield addTab(url);
+function* openTab(url, options = {}) {
+  let tab = yield addTab(url, options);
   let content = tab.linkedBrowser.contentWindow;
 
   gWindow = content.wrappedJSObject;
 
   // Setup the async storages in main window and for all its iframes
   yield ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
     /**
      * Get all windows including frames recursively.
@@ -103,16 +102,34 @@ function* openTabAndSetupStorage(url) {
         });
       }
       if (win.setup) {
         yield win.setup();
       }
     }
   });
 
+  return tab;
+}
+
+/**
+ * This generator function opens the given url in a new tab, then sets up the
+ * page by waiting for all cookies, indexedDB items etc. to be created; Then
+ * opens the storage inspector and waits for the storage tree and table to be
+ * populated.
+ *
+ * @param url {String} The url to be opened in the new tab
+ * @param options {Object} The tab options for the new tab
+ *
+ * @return {Promise} A promise that resolves after storage inspector is ready
+ */
+function* openTabAndSetupStorage(url, options = {}) {
+  // open tab
+  yield openTab(url, options);
+
   // open storage inspector
   return yield openStoragePanel();
 }
 
 /**
  * Open the toolbox, with the storage tool visible.
  *
  * @param cb {Function} Optional callback, if you don't want to use the returned
@@ -199,56 +216,60 @@ function forceCollections() {
 }
 
 /**
  * Cleans up and finishes the test
  */
 function* finishTests() {
   // Bug 1233497 makes it so that we can no longer yield CPOWs from Tasks.
   // We work around this by calling clear() via a ContentTask instead.
-  yield ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
-    /**
-     * Get all windows including frames recursively.
-     *
-     * @param {Window} [baseWindow]
-     *        The base window at which to start looking for child windows
-     *        (optional).
-     * @return {Set}
-     *         A set of windows.
-     */
-    function getAllWindows(baseWindow) {
-      let windows = new Set();
-
-      let _getAllWindows = function (win) {
-        windows.add(win.wrappedJSObject);
+  while (gBrowser.tabs.length > 1) {
+    yield ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
+      /**
+       * Get all windows including frames recursively.
+       *
+       * @param {Window} [baseWindow]
+       *        The base window at which to start looking for child windows
+       *        (optional).
+       * @return {Set}
+       *         A set of windows.
+       */
+      function getAllWindows(baseWindow) {
+        let windows = new Set();
 
-        for (let i = 0; i < win.length; i++) {
-          _getAllWindows(win[i]);
-        }
-      };
-      _getAllWindows(baseWindow);
-
-      return windows;
-    }
+        let _getAllWindows = function (win) {
+          windows.add(win.wrappedJSObject);
 
-    let windows = getAllWindows(content);
-    for (let win of windows) {
-      // Some windows (e.g., about: URLs) don't have storage available
-      try {
-        win.localStorage.clear();
-        win.sessionStorage.clear();
-      } catch (ex) {
-        // ignore
+          for (let i = 0; i < win.length; i++) {
+            _getAllWindows(win[i]);
+          }
+        };
+        _getAllWindows(baseWindow);
+
+        return windows;
       }
 
-      if (win.clear) {
-        yield win.clear();
+      let windows = getAllWindows(content);
+      for (let win of windows) {
+        // Some windows (e.g., about: URLs) don't have storage available
+        try {
+          win.localStorage.clear();
+          win.sessionStorage.clear();
+        } catch (ex) {
+          // ignore
+        }
+
+        if (win.clear) {
+          yield win.clear();
+        }
       }
-    }
-  });
+    });
+
+    yield closeTabAndToolbox(gBrowser.selectedTab);
+  }
 
   Services.cookies.removeAll();
   forceCollections();
   finish();
 }
 
 // Sends a click event on the passed DOM node in an async manner
 function* click(node) {
new file mode 100644
--- /dev/null
+++ b/devtools/client/storage/test/storage-listings-usercontextid.html
@@ -0,0 +1,131 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Storage inspector front end for userContextId - tests
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Storage inspector test for listing hosts and storages</title>
+</head>
+<body>
+<iframe src="http://sectest1.example.org/browser/devtools/client/storage/test/storage-unsecured-iframe-usercontextid.html"></iframe>
+<iframe src="https://sectest1.example.org:443/browser/devtools/client/storage/test/storage-secured-iframe-usercontextid.html"></iframe>
+<script type="application/javascript;version=1.7">
+"use strict";
+let partialHostname = location.hostname.match(/^[^.]+(\..*)$/)[1];
+let cookieExpiresTime1 = 2000000000000;
+let cookieExpiresTime2 = 2000000001000;
+// Setting up some cookies to eat.
+document.cookie = "c1uc1=foobar; expires=" +
+  new Date(cookieExpiresTime1).toGMTString() + "; path=/browser";
+document.cookie = "cs2uc1=sessionCookie; path=/; domain=" + partialHostname;
+document.cookie = "c3uc1=foobar-2; expires=" +
+  new Date(cookieExpiresTime2).toGMTString() + "; path=/";
+// ... and some local storage items ..
+localStorage.setItem("ls1uc1", "foobar");
+localStorage.setItem("ls2uc1", "foobar-2");
+// ... and finally some session storage items too
+sessionStorage.setItem("ss1uc1", "foobar-3");
+dump("added cookies and storage from main page\n");
+
+let idbGenerator = async function() {
+  let request = indexedDB.open("idb1uc1", 1);
+  request.onerror = function() {
+    throw new Error("error opening db connection");
+  };
+  let db = await new Promise(done => {
+    request.onupgradeneeded = event => {
+      let db = event.target.result;
+      let store1 = db.createObjectStore("obj1uc1", { keyPath: "id" });
+      store1.createIndex("name", "name", { unique: false });
+      store1.createIndex("email", "email", { unique: true });
+      let store2 = db.createObjectStore("obj2uc1", { keyPath: "id2" });
+      store1.transaction.oncomplete = () => {
+        done(db);
+      };
+    };
+  });
+
+  // Prevents AbortError
+  await new Promise(done => {
+    request.onsuccess = done;
+  });
+
+  let transaction = db.transaction(["obj1uc1", "obj2uc1"], "readwrite");
+  let store1 = transaction.objectStore("obj1uc1");
+  let store2 = transaction.objectStore("obj2uc1");
+  store1.add({id: 1, name: "foo", email: "foo@bar.com"});
+  store1.add({id: 2, name: "foo2", email: "foo2@bar.com"});
+  store1.add({id: 3, name: "foo2", email: "foo3@bar.com"});
+  store2.add({
+    id2: 1,
+    name: "foo",
+    email: "foo@bar.com",
+    extra: "baz"
+  });
+  // Prevents AbortError during close()
+  await new Promise(success => {
+    transaction.oncomplete = success;
+  });
+
+  db.close();
+
+  request = indexedDB.open("idb2uc1", 1);
+  let db2 = await new Promise(done => {
+    request.onupgradeneeded = event => {
+      let db2 = event.target.result;
+      let store3 = db2.createObjectStore("obj3uc1", { keyPath: "id3" });
+      store3.createIndex("name2", "name2", { unique: true });
+      store3.transaction.oncomplete = () => {
+        done(db2);
+      }
+    };
+  });
+  // Prevents AbortError during close()
+  await new Promise(done => {
+    request.onsuccess = done;
+  });
+  db2.close();
+
+  dump("added indexedDB from main page\n");
+};
+
+function deleteDB(dbName) {
+  return new Promise(resolve => {
+    dump("removing database " + dbName + " from " + document.location + "\n");
+    indexedDB.deleteDatabase(dbName).onsuccess = resolve;
+  });
+}
+
+async function fetchPut(cache, url) {
+  let response = await fetch(url);
+  await cache.put(url, response);
+}
+
+let cacheGenerator = async function() {
+  let cache = await caches.open("plopuc1");
+  await fetchPut(cache, "404_cached_file.js");
+  await fetchPut(cache, "browser_storage_basic.js");
+};
+
+window.setup = function*() {
+  yield idbGenerator();
+
+  if (window.caches) {
+    yield cacheGenerator();
+  }
+};
+
+window.clear = function*() {
+  yield deleteDB("idb1uc1");
+  yield deleteDB("idb2uc1");
+
+  if (window.caches) {
+    yield caches.delete("plopuc1");
+  }
+
+  dump("removed indexedDB and cache data from " + document.location + "\n");
+};
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/storage/test/storage-secured-iframe-usercontextid.html
@@ -0,0 +1,91 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Iframe for testing multiple host detetion in storage actor
+-->
+<head>
+  <meta charset="utf-8">
+</head>
+<body>
+<script type="application/javascript;version=1.7">
+"use strict";
+document.cookie = "sc1uc1=foobar;";
+localStorage.setItem("iframe-s-ls1uc1", "foobar");
+sessionStorage.setItem("iframe-s-ss1uc1", "foobar-2");
+dump("added cookies and storage from secured iframe\n");
+
+let idbGenerator = function*() {
+  let request = indexedDB.open("idb-s1uc1", 1);
+  request.onerror = function() {
+    throw new Error("error opening db connection");
+  };
+  let db = yield new Promise(done => {
+    request.onupgradeneeded = event => {
+      let db = event.target.result;
+      let store1 = db.createObjectStore("obj-s1uc1", { keyPath: "id" });
+      store1.transaction.oncomplete = () => {
+        done(db);
+      };
+    };
+  });
+  yield new Promise(done => {
+    request.onsuccess = done;
+  });
+
+  let transaction = db.transaction(["obj-s1uc1"], "readwrite");
+  let store1 = transaction.objectStore("obj-s1uc1");
+  store1.add({id: 6, name: "foo", email: "foo@bar.com"});
+  store1.add({id: 7, name: "foo2", email: "foo2@bar.com"});
+  yield new Promise(success => {
+    transaction.oncomplete = success;
+  });
+
+  db.close();
+
+  request = indexedDB.open("idb-s2uc1", 1);
+  let db2 = yield new Promise(done => {
+    request.onupgradeneeded = event => {
+      let db2 = event.target.result;
+      let store3 =
+        db2.createObjectStore("obj-s2uc1", { keyPath: "id3", autoIncrement: true });
+      store3.createIndex("name2", "name2", { unique: true });
+      store3.transaction.oncomplete = () => {
+        done(db2);
+      };
+    };
+  });
+  yield new Promise(done => {
+    request.onsuccess = done;
+  });
+
+  transaction = db2.transaction(["obj-s2uc1"], "readwrite");
+  let store3 = transaction.objectStore("obj-s2uc1");
+  store3.add({id3: 16, name2: "foo", email: "foo@bar.com"});
+  yield new Promise(success => {
+    transaction.oncomplete = success;
+  });
+
+  db2.close();
+  dump("added indexedDB from secured iframe\n");
+};
+
+function deleteDB(dbName) {
+  return new Promise(resolve => {
+    dump("removing database " + dbName + " from " + document.location + "\n");
+    indexedDB.deleteDatabase(dbName).onsuccess = resolve;
+  });
+}
+
+window.setup = function*() {
+  yield idbGenerator();
+};
+
+window.clear = function*() {
+  yield deleteDB("idb-s1uc1");
+  yield deleteDB("idb-s2uc1");
+
+  dump("removed indexedDB data from " + document.location + "\n");
+};
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/storage/test/storage-unsecured-iframe-usercontextid.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Iframe for testing multiple host detetion in storage actor
+-->
+<head>
+  <meta charset="utf-8">
+</head>
+<body>
+<script>
+"use strict";
+document.cookie = "uc1uc1=foobar; domain=.example.org; path=/";
+localStorage.setItem("iframe-u-ls1uc1", "foobar");
+sessionStorage.setItem("iframe-u-ss1uc1", "foobar1");
+sessionStorage.setItem("iframe-u-ss2uc1", "foobar2");
+dump("added cookies and storage from unsecured iframe\n");
+</script>
+</body>
+</html>