Bug 1060947 - Listing indexedDB databases throws if any of them have 0 objectStores. r=mratcliffe
authorJarda Snajdr <jsnajdr@gmail.com>
Wed, 13 Apr 2016 02:00:00 -0400
changeset 331336 7cfb8c8a1ba485734c8b350eda85b649551c89ac
parent 331335 63f11876b0b8ed6f49c697e5072da6eaeddbf2e0
child 331337 7cee6f0b47093dfa2bf247e475c38508adfb0811
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmratcliffe
bugs1060947
milestone48.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 1060947 - Listing indexedDB databases throws if any of them have 0 objectStores. r=mratcliffe
devtools/client/storage/test/browser.ini
devtools/client/storage/test/browser_storage_empty_objectstores.js
devtools/client/storage/test/storage-empty-objectstores.html
devtools/server/actors/storage.js
--- a/devtools/client/storage/test/browser.ini
+++ b/devtools/client/storage/test/browser.ini
@@ -1,15 +1,16 @@
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 support-files =
   storage-cache-error.html
   storage-complex-values.html
   storage-cookies.html
+  storage-empty-objectstores.html
   storage-listings.html
   storage-localstorage.html
   storage-overflow.html
   storage-search.html
   storage-secured-iframe.html
   storage-sessionstorage.html
   storage-unsecured-iframe.html
   storage-updates.html
@@ -17,18 +18,19 @@ support-files =
   !/devtools/client/framework/test/shared-head.js
 
 [browser_storage_basic.js]
 [browser_storage_cache_error.js]
 [browser_storage_cookies_delete_all.js]
 [browser_storage_cookies_edit.js]
 [browser_storage_cookies_edit_keyboard.js]
 [browser_storage_cookies_tab_navigation.js]
-[browser_storage_dynamic_updates.js]
-[browser_storage_localstorage_edit.js]
 [browser_storage_delete.js]
 [browser_storage_delete_all.js]
 [browser_storage_delete_tree.js]
+[browser_storage_dynamic_updates.js]
+[browser_storage_empty_objectstores.js]
+[browser_storage_localstorage_edit.js]
 [browser_storage_overflow.js]
 [browser_storage_search.js]
 [browser_storage_sessionstorage_edit.js]
 [browser_storage_sidebar.js]
 [browser_storage_values.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/storage/test/browser_storage_empty_objectstores.js
@@ -0,0 +1,77 @@
+/* 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/. */
+
+// Basic test to assert that the storage tree and table corresponding to each
+// item in the storage tree is correctly displayed.
+
+"use strict";
+
+// 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
+const storeItems = [
+  [["indexedDB", "http://test1.example.org"],
+   ["idb1", "idb2"]],
+  [["indexedDB", "http://test1.example.org", "idb1"],
+   ["obj1", "obj2"]],
+  [["indexedDB", "http://test1.example.org", "idb2"],
+   []],
+  [["indexedDB", "http://test1.example.org", "idb1", "obj1"],
+   [1, 2, 3]],
+  [["indexedDB", "http://test1.example.org", "idb1", "obj2"],
+   [1]]
+];
+
+/**
+ * Test that the desired number of tree items are present
+ */
+function testTree() {
+  let doc = gPanelWindow.document;
+  for (let [item] of storeItems) {
+    ok(doc.querySelector(`[data-id='${JSON.stringify(item)}']`),
+      `Tree item ${item} should be present in the storage tree`);
+  }
+}
+
+/**
+ * Test that correct table entries are shown for each of the tree item
+ */
+let testTables = function* () {
+  let doc = gPanelWindow.document;
+  // Expand all nodes so that the synthesized click event actually works
+  gUI.tree.expandAll();
+
+  // Click the tree items and wait for the table to be updated
+  for (let [item, ids] of storeItems) {
+    yield selectTreeItem(item);
+
+    // Check whether correct number of items are present in the table
+    is(doc.querySelectorAll(
+         ".table-widget-wrapper:first-of-type .table-widget-cell"
+       ).length, ids.length, "Number of items in table is correct");
+
+    // Check if all the desired items are present in the table
+    for (let id of ids) {
+      ok(doc.querySelector(".table-widget-cell[data-id='" + id + "']"),
+        `Table item ${id} should be present`);
+    }
+  }
+};
+
+add_task(function* () {
+  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-empty-objectstores.html");
+
+  testTree();
+  yield testTables();
+  yield finishTests();
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/storage/test/storage-empty-objectstores.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for proper listing indexedDB databases with no object stores</title>
+</head>
+<body>
+<script type="application/javascript;version=1.7">
+
+window.setup = function* () {
+  let request = indexedDB.open("idb1", 1);
+  let db = yield new Promise((resolve, reject) => {
+    request.onerror = e => reject(Error("error opening db connection"));
+    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 = () => resolve(db);
+    };
+  });
+
+  yield new Promise(resolve => request.onsuccess = resolve);
+
+  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"});
+
+  yield new Promise(resolve => transaction.oncomplete = resolve);
+
+  db.close();
+
+  request = indexedDB.open("idb2", 1);
+  let db2 = yield new Promise((resolve, reject) => {
+    request.onerror = e => reject(Error("error opening db2 connection"));
+    request.onupgradeneeded = event => resolve(event.target.result);
+  });
+
+  yield new Promise(resolve => request.onsuccess = resolve);
+
+  db2.close();
+  dump("added indexedDB items from main page\n");
+};
+
+window.clear = function* () {
+  for (let dbName of ["idb1", "idb2"]) {
+    yield new Promise(resolve => {
+      indexedDB.deleteDatabase(dbName).onsuccess = resolve;
+    });
+  }
+  dump("removed indexedDB items from main page\n");
+};
+
+</script>
+</body>
+</html>
--- a/devtools/server/actors/storage.js
+++ b/devtools/server/actors/storage.js
@@ -1531,19 +1531,23 @@ StorageActors.createActor({
    * cannot be performed synchronously. Thus, the preListStores method exists to
    * do the same task asynchronously.
    */
   populateStoresForHosts: function() {},
 
   getNamesForHost: function(host) {
     let names = [];
 
-    for (let [dbName, metaData] of this.hostVsStores.get(host)) {
-      for (let objectStore of metaData.objectStores.keys()) {
-        names.push(JSON.stringify([dbName, objectStore]));
+    for (let [dbName, {objectStores}] of this.hostVsStores.get(host)) {
+      if (objectStores.size) {
+        for (let objectStore of objectStores.keys()) {
+          names.push(JSON.stringify([dbName, objectStore]));
+        }
+      } else {
+        names.push(JSON.stringify([dbName]));
       }
     }
     return names;
   },
 
   /**
    * Returns the total number of entries for various types of requests to
    * getStoreObjects for Indexed DB actor.
@@ -1629,26 +1633,26 @@ StorageActors.createActor({
   /**
    * Returns the over-the-wire implementation of the indexed db entity.
    */
   toStoreObject: function(item) {
     if (!item) {
       return null;
     }
 
-    if (item.indexes) {
+    if ("indexes" in item) {
       // Object store meta data
       return {
         objectStore: item.name,
         keyPath: item.keyPath,
         autoIncrement: item.autoIncrement,
         indexes: item.indexes
       };
     }
-    if (item.objectStores) {
+    if ("objectStores" in item) {
       // DB meta data
       return {
         db: item.name,
         origin: item.origin,
         version: item.version,
         objectStores: item.objectStores
       };
     }