Bug 1263324 - Part 2: Add test cases to test context id for devtools storage inspector. r?mratcliffe draft
authorTim Huang <tihuang@mozilla.com>
Tue, 24 Jan 2017 14:45:42 +0800
changeset 479199 ba4149fe88edc9ac59b9ad216fc03923f96a8541
parent 479198 6965dc8731fb2c4b5024c77cd204e798142efad8
child 544619 80bba7b28d009a85794d30bf8d4af2ecdf494259
push id44182
push userbmo:tihuang@mozilla.com
push dateMon, 06 Feb 2017 08:41:20 +0000
reviewersmratcliffe
bugs1263324
milestone54.0a1
Bug 1263324 - Part 2: Add test cases to test context id for devtools storage inspector. r?mratcliffe
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>