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 293398 7cfb8c8a1ba485734c8b350eda85b649551c89ac
parent 293397 63f11876b0b8ed6f49c697e5072da6eaeddbf2e0
child 293399 7cee6f0b47093dfa2bf247e475c38508adfb0811
push id18756
push userryanvm@gmail.com
push dateFri, 15 Apr 2016 20:46:01 +0000
treeherderfx-team@6a623b9d4599 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmratcliffe
bugs1060947
milestone48.0a1
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
       };
     }