Bug 1302989 - Make storage inspector work with file:// when # is in the URL r?nchevobbe draft
authorMichael Ratcliffe <mratcliffe@mozilla.com>
Fri, 06 Jan 2017 10:14:55 +0000
changeset 460669 1a0d711757c31419e59888ca55a51999ede1bed0
parent 460618 91f5293e9a89056565493ed5073c3842b0ee9fdc
child 542109 7a1e0345606508de147dda19bed943e2ef199223
push id41456
push userbmo:mratcliffe@mozilla.com
push dateFri, 13 Jan 2017 15:37:42 +0000
reviewersnchevobbe
bugs1302989
milestone53.0a1
Bug 1302989 - Make storage inspector work with file:// when # is in the URL r?nchevobbe Changes: - Added storage-listings-with-fragment.html and browser_storage-listings-with-fragment.js. The only difference between these and storage-listings.html and browser_storage-listings.js is that they contain url fragments wherever URLs are loaded e.g. #abc, #def etc. - When referencing cookies in tests we used to only use the hostname but a full URL was needed for other storage types. For consistency we now use the full URL for all storage types. - storage.js used to contain various getHostName() methods depending on which storage type was needed. This has been changed to a single method that acts according to which protocol is in use. Cookies are the only storage type that requires just a hostname for the http and https protocols so we strip them inside the cookies actor where required. null is returned when storage types are not available for a particular protocol e.g. data:// URLs. - browser_storage_dynamic_windows.js and browser_storage_listings.js now detect cookies that were previously missed. This is a result of the getHostName() improvements. MozReview-Commit-ID: 8ZzM1Xz5hwU
devtools/client/storage/test/browser.ini
devtools/client/storage/test/browser_storage_basic.js
devtools/client/storage/test/browser_storage_basic_with_fragment.js
devtools/client/storage/test/browser_storage_cookies_delete_all.js
devtools/client/storage/test/browser_storage_cookies_domain.js
devtools/client/storage/test/browser_storage_delete.js
devtools/client/storage/test/browser_storage_delete_tree.js
devtools/client/storage/test/browser_storage_dynamic_updates_cookies.js
devtools/client/storage/test/browser_storage_sidebar.js
devtools/client/storage/test/storage-listings-with-fragment.html
devtools/client/storage/test/storage-listings.html
devtools/server/actors/storage.js
devtools/server/tests/browser/browser_storage_cookies-duplicate-names.js
devtools/server/tests/browser/browser_storage_dynamic_windows.js
devtools/server/tests/browser/browser_storage_listings.js
devtools/server/tests/browser/browser_storage_updates.js
--- a/devtools/client/storage/test/browser.ini
+++ b/devtools/client/storage/test/browser.ini
@@ -4,27 +4,29 @@ 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-with-fragment.html
   storage-localstorage.html
   storage-overflow.html
   storage-search.html
   storage-secured-iframe.html
   storage-sessionstorage.html
   storage-unsecured-iframe.html
   storage-updates.html
   head.js
   !/devtools/client/framework/test/shared-head.js
 
 [browser_storage_basic.js]
+[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]
--- a/devtools/client/storage/test/browser_storage_basic.js
+++ b/devtools/client/storage/test/browser_storage_basic.js
@@ -19,26 +19,26 @@
 // These entries are formed by the cookies, local storage, session storage and
 // indexedDB entries created in storage-listings.html,
 // storage-secured-iframe.html and storage-unsecured-iframe.html
 
 "use strict";
 
 const testCases = [
   [
-    ["cookies", "test1.example.org"],
+    ["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", "sectest1.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"]],
@@ -81,18 +81,18 @@ const testCases = [
     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) {
-    ok(doc.querySelector("[data-id='" + JSON.stringify(item[0]) + "']"),
+  for (let [item] of testCases) {
+    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() {
@@ -102,26 +102,26 @@ function* testTables() {
 
   // First tree item is already selected so no clicking and waiting for update
   for (let id of testCases[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 item of testCases.slice(1)) {
-    yield selectTreeItem(item[0]);
+  for (let [treeItem, items] of testCases.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, item[1].length, "Number of items in table is correct");
+       ).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 item[1]) {
+    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");
new file mode 100644
--- /dev/null
+++ b/devtools/client/storage/test/browser_storage_basic_with_fragment.js
@@ -0,0 +1,139 @@
+/* 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 head.js */
+
+// A second basic test to assert that the storage tree and table corresponding
+// to each item in the storage tree is correctly displayed.
+
+// This test differs from browser_storage_basic.js because the URLs we load
+// include fragments e.g. http://example.com/test.js#abcdefg
+//                                                  ^^^^^^^^
+//                                                  fragment
+
+// Entries that should be present in the tree for this test
+// Format for each entry in the array :
+// [
+//   ["path", "to", "tree", "item"], - The path to the tree item to click formed
+//                                     by id of each item
+//   ["key_value1", "key_value2", ...] - The value of the first (unique) column
+//                                       for each row in the table corresponding
+//                                       to the tree item selected.
+// ]
+// These entries are formed by the cookies, local storage, session storage and
+// indexedDB entries created in storage-listings.html,
+// storage-secured-iframe.html and storage-unsecured-iframe.html
+
+"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"]],
+];
+
+/**
+ * Test that the desired number of tree items are present
+ */
+function testTree() {
+  let doc = gPanelWindow.document;
+  for (let [item] of testCases) {
+    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() {
+  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 testCases[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 testCases.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-with-fragment.html#abc");
+
+  testTree();
+  yield testTables();
+
+  yield finishTests();
+});
--- a/devtools/client/storage/test/browser_storage_cookies_delete_all.js
+++ b/devtools/client/storage/test/browser_storage_cookies_delete_all.js
@@ -39,66 +39,66 @@ function* performDelete(store, rowName, 
 }
 
 add_task(function* () {
   yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html");
 
   info("test state before delete");
   yield checkState([
     [
-      ["cookies", "test1.example.org"], [
+      ["cookies", "http://test1.example.org"], [
         getCookieId("c1", "test1.example.org", "/browser"),
         getCookieId("c3", "test1.example.org", "/"),
         getCookieId("cs2", ".example.org", "/"),
         getCookieId("uc1", ".example.org", "/")
       ]
     ],
     [
-      ["cookies", "sectest1.example.org"], [
+      ["cookies", "https://sectest1.example.org"], [
         getCookieId("cs2", ".example.org", "/"),
         getCookieId("sc1", "sectest1.example.org",
                     "/browser/devtools/client/storage/test/"),
         getCookieId("uc1", ".example.org", "/")
       ]
     ],
   ]);
 
   info("delete all from domain");
   // delete only cookies that match the host exactly
   let id = getCookieId("c1", "test1.example.org", "/browser");
-  yield performDelete(["cookies", "test1.example.org"], id, false);
+  yield performDelete(["cookies", "http://test1.example.org"], id, false);
 
   info("test state after delete all from domain");
   yield checkState([
     // Domain cookies (.example.org) must not be deleted.
     [
-      ["cookies", "test1.example.org"],
+      ["cookies", "http://test1.example.org"],
       [
         getCookieId("cs2", ".example.org", "/"),
         getCookieId("uc1", ".example.org", "/")
       ]
     ],
     [
-      ["cookies", "sectest1.example.org"],
+      ["cookies", "https://sectest1.example.org"],
       [
         getCookieId("cs2", ".example.org", "/"),
         getCookieId("uc1", ".example.org", "/"),
         getCookieId("sc1", "sectest1.example.org",
                     "/browser/devtools/client/storage/test/"),
       ]
     ],
   ]);
 
   info("delete all");
   // delete all cookies for host, including domain cookies
   id = getCookieId("uc1", ".example.org", "/");
-  yield performDelete(["cookies", "sectest1.example.org"], id, true);
+  yield performDelete(["cookies", "http://sectest1.example.org"], id, true);
 
   info("test state after delete all");
   yield checkState([
     // Domain cookies (.example.org) are deleted too, so deleting in sectest1
     // also removes stuff from test1.
-    [["cookies", "test1.example.org"], []],
-    [["cookies", "sectest1.example.org"], []],
+    [["cookies", "http://test1.example.org"], []],
+    [["cookies", "https://sectest1.example.org"], []],
   ]);
 
   yield finishTests();
 });
--- a/devtools/client/storage/test/browser_storage_cookies_domain.js
+++ b/devtools/client/storage/test/browser_storage_cookies_domain.js
@@ -9,17 +9,17 @@
 // Test that cookies with domain equal to full host name are listed.
 // E.g., ".example.org" vs. example.org). Bug 1149497.
 
 add_task(function* () {
   yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-cookies.html");
 
   yield checkState([
     [
-      ["cookies", "test1.example.org"],
+      ["cookies", "http://test1.example.org"],
       [
         getCookieId("test1", ".test1.example.org", "/browser"),
         getCookieId("test2", "test1.example.org", "/browser"),
         getCookieId("test3", ".test1.example.org", "/browser"),
         getCookieId("test4", "test1.example.org", "/browser"),
         getCookieId("test5", ".test1.example.org", "/browser")
       ]
     ],
--- a/devtools/client/storage/test/browser_storage_delete.js
+++ b/devtools/client/storage/test/browser_storage_delete.js
@@ -9,17 +9,17 @@
 // Test deleting storage items
 
 const TEST_CASES = [
   [["localStorage", "http://test1.example.org"],
    "ls1", "name"],
   [["sessionStorage", "http://test1.example.org"],
    "ss1", "name"],
   [
-    ["cookies", "test1.example.org"],
+    ["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"],
 ];
 
--- a/devtools/client/storage/test/browser_storage_delete_tree.js
+++ b/devtools/client/storage/test/browser_storage_delete_tree.js
@@ -13,34 +13,34 @@ add_task(function* () {
 
   let contextMenu = gPanelWindow.document.getElementById("storage-tree-popup");
   let menuDeleteAllItem = contextMenu.querySelector(
     "#storage-tree-popup-delete-all");
 
   info("test state before delete");
   yield checkState([
     [
-      ["cookies", "test1.example.org"],
+      ["cookies", "http://test1.example.org"],
       [
         getCookieId("c1", "test1.example.org", "/browser"),
         getCookieId("cs2", ".example.org", "/"),
         getCookieId("c3", "test1.example.org", "/"),
         getCookieId("uc1", ".example.org", "/")
       ]
     ],
     [["localStorage", "http://test1.example.org"], ["ls1", "ls2"]],
     [["sessionStorage", "http://test1.example.org"], ["ss1"]],
     [["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"], [1, 2, 3]],
     [["Cache", "http://test1.example.org", "plop"],
       [MAIN_DOMAIN + "404_cached_file.js", MAIN_DOMAIN + "browser_storage_basic.js"]],
   ]);
 
   info("do the delete");
   const deleteHosts = [
-    ["cookies", "test1.example.org"],
+    ["cookies", "http://test1.example.org"],
     ["localStorage", "http://test1.example.org"],
     ["sessionStorage", "http://test1.example.org"],
     ["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"],
     ["Cache", "http://test1.example.org", "plop"],
   ];
 
   for (let store of deleteHosts) {
     let storeName = store.join(" > ");
@@ -59,17 +59,17 @@ add_task(function* () {
       menuDeleteAllItem.click();
     });
 
     yield eventWait;
   }
 
   info("test state after delete");
   yield checkState([
-    [["cookies", "test1.example.org"], []],
+    [["cookies", "http://test1.example.org"], []],
     [["localStorage", "http://test1.example.org"], []],
     [["sessionStorage", "http://test1.example.org"], []],
     [["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"], []],
     [["Cache", "http://test1.example.org", "plop"], []],
   ]);
 
   yield finishTests();
 });
--- a/devtools/client/storage/test/browser_storage_dynamic_updates_cookies.js
+++ b/devtools/client/storage/test/browser_storage_dynamic_updates_cookies.js
@@ -38,34 +38,34 @@ add_task(function* () {
   // Check that sidebar shows correct initial value
   yield findVariableViewProperties(initialValue[0], false);
 
   yield findVariableViewProperties(initialValue[1], true);
   // Check if table shows correct initial value
 
   yield checkState([
     [
-      ["cookies", "test1.example.org"],
+      ["cookies", "http://test1.example.org"],
       [
         getCookieId("c1", "test1.example.org", "/browser"),
         getCookieId("c2", "test1.example.org", "/browser")
       ]
     ],
   ]);
   checkCell(c1id, "value", "1.2.3.4.5.6.7");
 
   gWindow.addCookie("c1", '{"foo": 4,"bar":6}', "/browser");
   yield gUI.once("sidebar-updated");
 
   yield findVariableViewProperties(finalValue[0], false);
   yield findVariableViewProperties(finalValue[1], true);
 
   yield checkState([
     [
-      ["cookies", "test1.example.org"],
+      ["cookies", "http://test1.example.org"],
       [
         getCookieId("c1", "test1.example.org", "/browser"),
         getCookieId("c2", "test1.example.org", "/browser")
       ]
     ],
   ]);
   checkCell(c1id, "value", '{"foo": 4,"bar":6}');
 
@@ -73,17 +73,17 @@ add_task(function* () {
   gWindow.addCookie("c3", "booyeah");
 
   // Wait once for update and another time for value fetching
   yield gUI.once("store-objects-updated");
   yield gUI.once("store-objects-updated");
 
   yield checkState([
     [
-      ["cookies", "test1.example.org"],
+      ["cookies", "http://test1.example.org"],
       [
         getCookieId("c1", "test1.example.org", "/browser"),
         getCookieId("c2", "test1.example.org", "/browser"),
         getCookieId("c3", "test1.example.org",
                     "/browser/devtools/client/storage/test/")
       ]
     ],
   ]);
@@ -95,17 +95,17 @@ add_task(function* () {
   gWindow.addCookie("c4", "booyeah");
 
   // Wait once for update and another time for value fetching
   yield gUI.once("store-objects-updated");
   yield gUI.once("store-objects-updated");
 
   yield checkState([
     [
-      ["cookies", "test1.example.org"],
+      ["cookies", "http://test1.example.org"],
       [
         getCookieId("c1", "test1.example.org", "/browser"),
         getCookieId("c2", "test1.example.org", "/browser"),
         getCookieId("c3", "test1.example.org",
                     "/browser/devtools/client/storage/test/"),
         getCookieId("c4", "test1.example.org",
                     "/browser/devtools/client/storage/test/")
       ]
@@ -117,17 +117,17 @@ add_task(function* () {
 
   // Removing cookies
   gWindow.removeCookie("c1", "/browser");
 
   yield gUI.once("sidebar-updated");
 
   yield checkState([
     [
-      ["cookies", "test1.example.org"],
+      ["cookies", "http://test1.example.org"],
       [
         getCookieId("c2", "test1.example.org", "/browser"),
         getCookieId("c3", "test1.example.org",
                     "/browser/devtools/client/storage/test/"),
         getCookieId("c4", "test1.example.org",
                     "/browser/devtools/client/storage/test/")
       ]
     ],
@@ -140,17 +140,17 @@ add_task(function* () {
 
   // Keep deleting till no rows
   gWindow.removeCookie("c3");
 
   yield gUI.once("store-objects-updated");
 
   yield checkState([
     [
-      ["cookies", "test1.example.org"],
+      ["cookies", "http://test1.example.org"],
       [
         getCookieId("c2", "test1.example.org", "/browser"),
         getCookieId("c4", "test1.example.org",
                     "/browser/devtools/client/storage/test/")
       ]
     ],
   ]);
 
@@ -158,31 +158,31 @@ add_task(function* () {
   yield findVariableViewProperties([{name: "c2", value: "foobar"}]);
 
   gWindow.removeCookie("c2", "/browser");
 
   yield gUI.once("sidebar-updated");
 
   yield checkState([
     [
-      ["cookies", "test1.example.org"],
+      ["cookies", "http://test1.example.org"],
       [
         getCookieId("c4", "test1.example.org",
                     "/browser/devtools/client/storage/test/")
       ]
     ],
   ]);
 
   // Check if next element's value is visible in sidebar
   yield findVariableViewProperties([{name: "c4", value: "booyeah"}]);
 
   gWindow.removeCookie("c4");
 
   yield gUI.once("store-objects-updated");
 
   yield checkState([
-    [["cookies", "test1.example.org"], [ ]],
+    [["cookies", "http://test1.example.org"], [ ]],
   ]);
 
   ok(gUI.sidebar.hidden, "Sidebar is hidden when no rows");
 
   yield finishTests();
 });
--- a/devtools/client/storage/test/browser_storage_sidebar.js
+++ b/devtools/client/storage/test/browser_storage_sidebar.js
@@ -11,17 +11,17 @@
 //   <do we wait for the async "sidebar-updated" event>,
 //   <is the sidebar open>
 // ]
 
 "use strict";
 
 const testCases = [
   {
-    location: ["cookies", "sectest1.example.org"],
+    location: ["cookies", "https://sectest1.example.org"],
     sidebarHidden: true
   },
   {
     location: getCookieId("cs2", ".example.org", "/"),
     sidebarHidden: false
   },
   {
     sendEscape: true
new file mode 100644
--- /dev/null
+++ b/devtools/client/storage/test/storage-listings-with-fragment.html
@@ -0,0 +1,134 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+This test differs from browser_storage_listings.html only because the URLs we load
+include fragments e.g. http://example.com/test.js#abcdefg
+                                                 ^^^^^^^^
+                                                 fragment
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Storage inspector test for listing hosts and storages with URL fragments</title>
+</head>
+<body>
+<iframe src="http://sectest1.example.org/browser/devtools/client/storage/test/storage-unsecured-iframe.html#def"></iframe>
+<iframe src="https://sectest1.example.org:443/browser/devtools/client/storage/test/storage-secured-iframe.html#ghi"></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 = "c1=foobar; expires=" +
+  new Date(cookieExpiresTime1).toGMTString() + "; path=/browser";
+document.cookie = "cs2=sessionCookie; path=/; domain=" + partialHostname;
+document.cookie = "c3=foobar-2; expires=" +
+  new Date(cookieExpiresTime2).toGMTString() + "; path=/";
+// ... and some local storage items ..
+localStorage.setItem("ls1", "foobar");
+localStorage.setItem("ls2", "foobar-2");
+// ... and finally some session storage items too
+sessionStorage.setItem("ss1", "foobar-3");
+dump("added cookies and storage from main page\n");
+
+let idbGenerator = async function() {
+  let request = indexedDB.open("idb1", 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("obj1", { keyPath: "id" });
+      store1.createIndex("name", "name", { unique: false });
+      store1.createIndex("email", "email", { unique: true });
+      let store2 = db.createObjectStore("obj2", { keyPath: "id2" });
+      store1.transaction.oncomplete = () => {
+        done(db);
+      };
+    };
+  });
+
+  // Prevents AbortError
+  await new Promise(done => {
+    request.onsuccess = done;
+  });
+
+  let transaction = db.transaction(["obj1", "obj2"], "readwrite");
+  let store1 = transaction.objectStore("obj1");
+  let store2 = transaction.objectStore("obj2");
+  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("idb2", 1);
+  let db2 = await new Promise(done => {
+    request.onupgradeneeded = event => {
+      let db2 = event.target.result;
+      let store3 = db2.createObjectStore("obj3", { 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("plop");
+  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("idb1");
+  yield deleteDB("idb2");
+
+  if (window.caches) {
+    yield caches.delete("plop");
+  }
+
+  dump("removed indexedDB and cache data from " + document.location + "\n");
+};
+</script>
+</body>
+</html>
--- a/devtools/client/storage/test/storage-listings.html
+++ b/devtools/client/storage/test/storage-listings.html
@@ -1,12 +1,12 @@
 <!DOCTYPE HTML>
 <html>
 <!--
-Bug 970517 - Storage inspector front end - tests
+Storage inspector front end - 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.html"></iframe>
 <iframe src="https://sectest1.example.org:443/browser/devtools/client/storage/test/storage-secured-iframe.html"></iframe>
--- a/devtools/server/actors/storage.js
+++ b/devtools/server/actors/storage.js
@@ -121,37 +121,63 @@ StorageActors.defaults = function (typeN
 
     /**
      * Returns a list of currently knwon hosts for the target window. This list
      * contains unique hosts from the window + all inner windows.
      */
     get hosts() {
       let hosts = new Set();
       for (let {location} of this.storageActor.windows) {
-        hosts.add(this.getHostName(location));
+        let host = this.getHostName(location);
+
+        if (host) {
+          hosts.add(host);
+        }
       }
       return hosts;
     },
 
     /**
      * Returns all the windows present on the page. Includes main window + inner
      * iframe windows.
      */
     get windows() {
       return this.storageActor.windows;
     },
 
     /**
-     * Converts the window.location object into host.
+     * Converts the window.location object into a URL (e.g. http://domain.com).
      */
     getHostName(location) {
-      if (location.protocol === "chrome:") {
-        return location.href;
+      if (!location) {
+        // Debugging a legacy Firefox extension... no hostname available and no
+        // storage possible.
+        return null;
       }
-      return location.hostname || location.href;
+
+      switch (location.protocol) {
+        case "data:":
+          // data: URLs do not support storage of any type.
+          return null;
+        case "about:":
+          // Fallthrough.
+        case "chrome:":
+          // Fallthrough.
+        case "file:":
+          return location.protocol + location.pathname;
+        case "resource:":
+          return location.origin + location.pathname;
+        case "moz-extension:":
+          return location.origin;
+        case "javascript:":
+          return location.href;
+        default:
+          // http: or unknown protocol.
+          return `${location.protocol}//${location.host}`;
+      }
     },
 
     initialize(storageActor) {
       protocol.Actor.prototype.initialize.call(this, null);
 
       this.storageActor = storageActor;
 
       this.populateStoresForHosts();
@@ -201,17 +227,17 @@ StorageActors.defaults = function (typeN
      * When a new window is added to the page. This generally means that a new
      * iframe is created, or the current window is completely reloaded.
      *
      * @param {window} window
      *        The window which was added.
      */
     onWindowReady: Task.async(function* (window) {
       let host = this.getHostName(window.location);
-      if (!this.hostVsStores.has(host)) {
+      if (host && !this.hostVsStores.has(host)) {
         yield this.populateStoresForHost(host, window);
         let data = {};
         data[host] = this.getNamesForHost(host);
         this.storageActor.update("added", typeName, data);
       }
     }),
 
     /**
@@ -222,17 +248,17 @@ StorageActors.defaults = function (typeN
      *        The window which was removed.
      */
     onWindowDestroyed(window) {
       if (!window.location) {
         // Nothing can be done if location object is null
         return;
       }
       let host = this.getHostName(window.location);
-      if (!this.hosts.has(host)) {
+      if (host && !this.hosts.has(host)) {
         this.hostVsStores.delete(host);
         let data = {};
         data[host] = [];
         this.storageActor.update("deleted", typeName, data);
       }
     },
 
     form(form, detail) {
@@ -462,22 +488,26 @@ StorageActors.createActor({
   /**
    * Given a cookie object and a host, figure out if the cookie is valid for
    * that host.
    */
   isCookieAtHost(cookie, host) {
     if (cookie.host == null) {
       return host == null;
     }
+
+    host = trimHttpHttps(host);
+
     if (cookie.host.startsWith(".")) {
       return ("." + host).endsWith(cookie.host);
     }
     if (cookie.host === "") {
       return host.startsWith("file://" + cookie.path);
     }
+
     return cookie.host == host;
   },
 
   toStoreObject(cookie) {
     if (!cookie) {
       return null;
     }
 
@@ -709,16 +739,18 @@ StorageActors.createActor({
 
 var cookieHelpers = {
   getCookiesFromHost(host, originAttributes) {
     // Local files have no host.
     if (host.startsWith("file:///")) {
       host = "";
     }
 
+    host = trimHttpHttps(host);
+
     let cookies = Services.cookies.getCookiesFromHost(host, originAttributes);
     let store = [];
 
     while (cookies.hasMoreElements()) {
       let cookie = cookies.getNext().QueryInterface(Ci.nsICookie2);
 
       store.push(cookie);
     }
@@ -843,16 +875,18 @@ var cookieHelpers = {
     // extract the cookie name to remove the correct cookie.
     if (opts.name) {
       let split = opts.name.split(SEPARATOR_GUID);
 
       opts.name = split[0];
       opts.path = split[2];
     }
 
+    host = trimHttpHttps(host);
+
     function hostMatches(cookieHost, matchHost) {
       if (cookieHost == null) {
         return matchHost == null;
       }
       if (cookieHost.startsWith(".")) {
         return ("." + matchHost).endsWith(cookieHost);
       }
       return cookieHost == host;
@@ -1049,38 +1083,31 @@ function getObjectForLocalOrSessionStora
         return [];
       }
       return Object.keys(storage).map(key => ({
         name: key,
         value: storage.getItem(key)
       }));
     },
 
-    getHostName(location) {
-      if (!location.host) {
-        return location.href;
-      }
-      if (location.protocol === "chrome:") {
-        return location.href;
-      }
-      return location.protocol + "//" + location.host;
-    },
-
     populateStoresForHost(host, window) {
       try {
         this.hostVsStores.set(host, window[type]);
       } catch (ex) {
         console.warn(`Failed to enumerate ${type} for host ${host}: ${ex}`);
       }
     },
 
     populateStoresForHosts() {
       this.hostVsStores = new Map();
       for (let window of this.windows) {
-        this.populateStoresForHost(this.getHostName(window.location), window);
+        let host = this.getHostName(window.location);
+        if (host) {
+          this.populateStoresForHost(host, window);
+        }
       }
     },
 
     getFields: Task.async(function* () {
       return [
         { name: "name", editable: true },
         { name: "value", editable: true }
       ];
@@ -1269,26 +1296,16 @@ StorageActors.createActor({
 
   getFields: Task.async(function* () {
     return [
       { name: "url", editable: false },
       { name: "status", editable: false }
     ];
   }),
 
-  getHostName(location) {
-    if (!location.host) {
-      return location.href;
-    }
-    if (location.protocol === "chrome:") {
-      return location.href;
-    }
-    return location.protocol + "//" + location.host;
-  },
-
   populateStoresForHost: Task.async(function* (host) {
     let storeMap = new Map();
     let caches = yield this.getCachesForHost(host);
     try {
       for (let name of (yield caches.keys())) {
         storeMap.set(name, (yield caches.open(name)));
       }
     } catch (ex) {
@@ -1554,26 +1571,16 @@ StorageActors.createActor({
     if (!win) {
       return;
     }
 
     let principal = win.document.nodePrincipal;
     this.removeDBRecord(host, principal, db, store, id);
   }),
 
-  getHostName(location) {
-    if (!location.host) {
-      return location.href;
-    }
-    if (location.protocol === "chrome:") {
-      return location.href;
-    }
-    return location.protocol + "//" + location.host;
-  },
-
   /**
    * This method is overriden and left blank as for indexedDB, this operation
    * cannot be performed synchronously. Thus, the preListStores method exists to
    * do the same task asynchronously.
    */
   populateStoresForHosts() {},
 
   getNamesForHost(host) {
@@ -2405,16 +2412,29 @@ exports.setupParentProcessForIndexedDB =
 
   return {
     onBrowserSwap: setMessageManager,
     onDisconnected: () => setMessageManager(null),
   };
 };
 
 /**
+ * General helpers
+ */
+function trimHttpHttps(url) {
+  if (url.startsWith("http://")) {
+    return url.substr(7);
+  }
+  if (url.startsWith("https://")) {
+    return url.substr(8);
+  }
+  return url;
+}
+
+/**
  * The main Storage Actor.
  */
 let StorageActor = protocol.ActorClassWithSpec(specs.storageSpec, {
   typeName: "storage",
 
   get window() {
     return this.parentActor.window;
   },
--- a/devtools/server/tests/browser/browser_storage_cookies-duplicate-names.js
+++ b/devtools/server/tests/browser/browser_storage_cookies-duplicate-names.js
@@ -6,17 +6,17 @@
 
 // Test that the storage panel is able to display multiple cookies with the same
 // name (and different paths).
 
 const {StorageFront} = require("devtools/shared/fronts/storage");
 Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/server/tests/browser/storage-helpers.js", this);
 
 const TESTDATA = {
-  "test1.example.org": [
+  "http://test1.example.org": [
     {
       name: "name",
       value: "value1",
       expires: 0,
       path: "/",
       host: "test1.example.org",
       isDomain: false,
       isSecure: false,
--- a/devtools/server/tests/browser/browser_storage_dynamic_windows.js
+++ b/devtools/server/tests/browser/browser_storage_dynamic_windows.js
@@ -4,18 +4,18 @@
 
 "use strict";
 
 const {StorageFront} = require("devtools/shared/fronts/storage");
 Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/server/tests/browser/storage-helpers.js", this);
 
 const beforeReload = {
   cookies: {
-    "test1.example.org": ["c1", "cs2", "c3", "uc1"],
-    "sectest1.example.org": ["uc1", "cs2"]
+    "http://test1.example.org": ["c1", "cs2", "c3", "uc1"],
+    "http://sectest1.example.org": ["uc1", "cs2"]
   },
   localStorage: {
     "http://test1.example.org": ["ls1", "ls2"],
     "http://sectest1.example.org": ["iframe-u-ls1"]
   },
   sessionStorage: {
     "http://test1.example.org": ["ss1"],
     "http://sectest1.example.org": ["iframe-u-ss1", "iframe-u-ss2"]
@@ -94,17 +94,22 @@ function testAddIframe(front) {
     let shouldBeEmpty = {
       localStorage: {
         "https://sectest1.example.org": ["iframe-s-ls1"]
       },
       sessionStorage: {
         "https://sectest1.example.org": ["iframe-s-ss1"]
       },
       cookies: {
-        "sectest1.example.org": [
+        "https://sectest1.example.org": [
+          getCookieId("cs2", ".example.org", "/"),
+          getCookieId("sc1", "sectest1.example.org",
+                      "/browser/devtools/server/tests/browser/")
+        ],
+        "http://sectest1.example.org": [
           getCookieId("sc1", "sectest1.example.org",
                       "/browser/devtools/server/tests/browser/")
         ]
       },
       indexedDB: {
         // empty because indexed db creation happens after the page load, so at
         // the time of window-ready, there was no indexed db present.
         "https://sectest1.example.org": []
--- a/devtools/server/tests/browser/browser_storage_listings.js
+++ b/devtools/server/tests/browser/browser_storage_listings.js
@@ -4,17 +4,17 @@
 
 "use strict";
 
 const {StorageFront} = require("devtools/shared/fronts/storage");
 Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/server/tests/browser/storage-helpers.js", this);
 
 const storeMap = {
   cookies: {
-    "test1.example.org": [
+    "http://test1.example.org": [
       {
         name: "c1",
         value: "foobar",
         expires: 2000000000000,
         path: "/browser",
         host: "test1.example.org",
         isDomain: false,
         isSecure: false,
@@ -33,17 +33,39 @@ const storeMap = {
         value: "foobar-2",
         expires: 2000000001000,
         path: "/",
         host: "test1.example.org",
         isDomain: false,
         isSecure: true,
       }
     ],
-    "sectest1.example.org": [
+
+    "http://sectest1.example.org": [
+      {
+        name: "cs2",
+        value: "sessionCookie",
+        path: "/",
+        host: ".example.org",
+        expires: 0,
+        isDomain: true,
+        isSecure: false,
+      },
+      {
+        name: "sc1",
+        value: "foobar",
+        path: "/browser/devtools/server/tests/browser/",
+        host: "sectest1.example.org",
+        expires: 0,
+        isDomain: false,
+        isSecure: false,
+      }
+    ],
+
+    "https://sectest1.example.org": [
       {
         name: "uc1",
         value: "foobar",
         host: ".example.org",
         path: "/",
         expires: 0,
         isDomain: true,
         isSecure: true,
@@ -323,17 +345,17 @@ function* testStores(data) {
   ok(data.indexedDB, "Indexed DB storage actor is present");
   yield testCookies(data.cookies);
   yield testLocalStorage(data.localStorage);
   yield testSessionStorage(data.sessionStorage);
   yield testIndexedDB(data.indexedDB);
 }
 
 function testCookies(cookiesActor) {
-  is(Object.keys(cookiesActor.hosts).length, 2,
+  is(Object.keys(cookiesActor.hosts).length, 3,
                  "Correct number of host entries for cookies");
   return testCookiesObjects(0, cookiesActor.hosts, cookiesActor);
 }
 
 var testCookiesObjects = Task.async(function* (index, hosts, cookiesActor) {
   let host = Object.keys(hosts)[index];
   let matchItems = data => {
     let cookiesLength = 0;
--- a/devtools/server/tests/browser/browser_storage_updates.js
+++ b/devtools/server/tests/browser/browser_storage_updates.js
@@ -1,17 +1,17 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const {StorageFront} = require("devtools/shared/fronts/storage");
 const beforeReload = {
-  cookies: ["test1.example.org", "sectest1.example.org"],
+  cookies: ["http://test1.example.org", "https://sectest1.example.org"],
   localStorage: ["http://test1.example.org", "http://sectest1.example.org"],
   sessionStorage: ["http://test1.example.org", "http://sectest1.example.org"],
 };
 
 const TESTS = [
   // index 0
   {
     action: function (win) {
@@ -22,17 +22,17 @@ const TESTS = [
       win.addCookie("c2", "foobar2");
 
       info('win.localStorage.setItem("l1", "foobar1")');
       win.localStorage.setItem("l1", "foobar1");
     },
     expected: {
       added: {
         cookies: {
-          "test1.example.org": [
+          "http://test1.example.org": [
             getCookieId("c1", "test1.example.org",
                         "/browser/devtools/server/tests/browser/"),
             getCookieId("c2", "test1.example.org",
                         "/browser/devtools/server/tests/browser/")
           ]
         },
         localStorage: {
           "http://test1.example.org": ["l1"]
@@ -48,17 +48,17 @@ const TESTS = [
       win.addCookie("c1", "new_foobar1");
 
       info('win.localStorage.setItem("l2", "foobar2")');
       win.localStorage.setItem("l2", "foobar2");
     },
     expected: {
       changed: {
         cookies: {
-          "test1.example.org": [
+          "http://test1.example.org": [
             getCookieId("c1", "test1.example.org",
                         "/browser/devtools/server/tests/browser/"),
           ]
         }
       },
       added: {
         localStorage: {
           "http://test1.example.org": ["l2"]
@@ -77,17 +77,17 @@ const TESTS = [
       win.localStorage.removeItem("l1");
 
       info('win.localStorage.setItem("l3", "foobar3")');
       win.localStorage.setItem("l3", "foobar3");
     },
     expected: {
       deleted: {
         cookies: {
-          "test1.example.org": [
+          "http://test1.example.org": [
             getCookieId("c2", "test1.example.org",
                         "/browser/devtools/server/tests/browser/"),
           ]
         },
         localStorage: {
           "http://test1.example.org": ["l1"]
         }
       },
@@ -118,33 +118,33 @@ const TESTS = [
       win.sessionStorage.setItem("s2", "foobar2");
 
       info('win.localStorage.setItem("l3", "new_foobar3")');
       win.localStorage.setItem("l3", "new_foobar3");
     },
     expected: {
       added: {
         cookies: {
-          "test1.example.org": [
+          "http://test1.example.org": [
             getCookieId("c3", "test1.example.org",
                         "/browser/devtools/server/tests/browser/"),
           ]
         },
         sessionStorage: {
           "http://test1.example.org": ["s1", "s2"]
         }
       },
       changed: {
         localStorage: {
           "http://test1.example.org": ["l3"]
         }
       },
       deleted: {
         cookies: {
-          "test1.example.org": [
+          "http://test1.example.org": [
             getCookieId("c1", "test1.example.org",
                         "/browser/devtools/server/tests/browser/"),
           ]
         },
         localStorage: {
           "http://test1.example.org": ["l2"]
         }
       }
@@ -170,17 +170,17 @@ const TESTS = [
   {
     action: function (win) {
       info("win.clearCookies()");
       win.clearCookies();
     },
     expected: {
       deleted: {
         cookies: {
-          "test1.example.org": [
+          "http://test1.example.org": [
             getCookieId("c3", "test1.example.org",
                         "/browser/devtools/server/tests/browser/"),
           ]
         }
       }
     }
   }
 ];