Bug 1264582 - Table headers are not removed when selecting an empty storage. r=mratcliffe
authorFischer.json <fischer.json@gmail.com>
Fri, 01 Jul 2016 17:42:07 +0800
changeset 306650 e6c96d5612d3ae57296734645fdb5a2522dc961d
parent 306649 08e6660bfd34d9ddb7476e7a22290a8693d5f581
child 306651 2e03460fc9272cd1f8141d337c5854e8f0bf8fe6
push id30491
push usercbook@mozilla.com
push dateTue, 26 Jul 2016 14:57:10 +0000
treeherdermozilla-central@67a3a23b722e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmratcliffe
bugs1264582
milestone50.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 1264582 - Table headers are not removed when selecting an empty storage. r=mratcliffe MozReview-Commit-ID: Hcfw7dyrDpV
devtools/client/storage/ui.js
devtools/server/actors/storage.js
devtools/shared/specs/storage.js
--- a/devtools/client/storage/ui.js
+++ b/devtools/client/storage/ui.js
@@ -220,22 +220,25 @@ StorageUI.prototype = {
   },
 
   getCurrentActor: function () {
     let type = this.table.datatype;
 
     return this.storageTypes[type];
   },
 
-  makeFieldsEditable: function* () {
-    let actor = this.getCurrentActor();
-
-    if (typeof actor.getEditableFields !== "undefined") {
-      let fields = yield actor.getEditableFields();
-      this.table.makeFieldsEditable(fields);
+  /**
+   *  Make column fields editable
+   *
+   *  @param {Array} editableFields
+   *         An array of keys of columns to be made editable
+   */
+  makeFieldsEditable: function* (editableFields) {
+    if (editableFields && editableFields.length > 0) {
+      this.table.makeFieldsEditable(editableFields);
     } else if (this.table._editableFieldsEngine) {
       this.table._editableFieldsEngine.destroy();
     }
   },
 
   editItem: function (eventType, data) {
     let actor = this.getCurrentActor();
 
@@ -481,21 +484,35 @@ StorageUI.prototype = {
     if (reason !== REASON.NEXT_50_ITEMS &&
         reason !== REASON.UPDATE &&
         reason !== REASON.NEW_ROW &&
         reason !== REASON.POPULATE) {
       throw new Error("Invalid reason specified");
     }
 
     try {
+      if (reason === REASON.POPULATE) {
+        let subType = null;
+        // The indexedDB type could have sub-type data to fetch.
+        // If having names specified, then it means
+        // we are fetching details of specific database or of object store.
+        if (type == "indexedDB" && names) {
+          let [ dbName, objectStoreName ] = JSON.parse(names[0]);
+          if (dbName) {
+            subType = "database";
+          }
+          if (objectStoreName) {
+            subType = "object store";
+          }
+        }
+        yield this.resetColumns(type, host, subType);
+      }
+
       let {data} = yield storageType.getStoreObjects(host, names, fetchOpts);
       if (data.length) {
-        if (reason === REASON.POPULATE) {
-          yield this.resetColumns(data[0], type, host);
-        }
         this.populateTable(data, reason);
       }
       this.emit("store-objects-updated");
     } catch (ex) {
       console.error(ex);
     }
   }),
 
@@ -726,46 +743,56 @@ StorageUI.prototype = {
     }
     this.fetchStorageObjects(type, host, names, REASON.POPULATE);
     this.itemOffset = 0;
   },
 
   /**
    * Resets the column headers in the storage table with the pased object `data`
    *
-   * @param {object} data
-   *        The object from which key and values will be used for naming the
-   *        headers of the columns
    * @param {string} type
    *        The type of storage corresponding to the after-reset columns in the
    *        table.
    * @param {string} host
    *        The host name corresponding to the table after reset.
+   *
+   * @param {string} [subType]
+   *        The sub type under the given type.
    */
-  resetColumns: function* (data, type, host) {
+  resetColumns: function* (type, host, subtype) {
+    this.table.host = host;
+    this.table.datatype = type;
+
+    let uniqueKey = null;
     let columns = {};
-    let uniqueKey = null;
-    for (let key in data) {
+    let editableFields = [];
+    let fields = yield this.getCurrentActor().getFields(subtype);
+
+    fields.forEach(f => {
       if (!uniqueKey) {
-        this.table.uniqueId = uniqueKey = key;
+        this.table.uniqueId = uniqueKey = f.name;
       }
-      columns[key] = key;
+
+      if (f.editable) {
+        editableFields.push(f.name);
+      }
+
+      columns[f.name] = f.name;
       try {
-        columns[key] = L10N.getStr("table.headers." + type + "." + key);
+        columns[f.name] = L10N.getStr("table.headers." + type + "." + f.name);
       } catch (e) {
         console.error("Unable to localize table header type:" + type +
-                      " key:" + key);
+                      " key:" + f.name);
       }
-    }
+    });
+
     this.table.setColumns(columns, null, HIDDEN_COLUMNS);
-    this.table.datatype = type;
-    this.table.host = host;
     this.hideSidebar();
 
-    yield this.makeFieldsEditable();
+    yield this.makeFieldsEditable(editableFields);
   },
 
   /**
    * Populates or updates the rows in the storage table.
    *
    * @param {array[object]} data
    *        Array of objects to be populated in the storage table
    * @param {Constant} reason
--- a/devtools/server/actors/storage.js
+++ b/devtools/server/actors/storage.js
@@ -76,16 +76,20 @@ var StorageActors = {};
  *               topic.
  *   - getNamesForHost : Given a host, get list of all known store names.
  *   - getValuesForHost : Given a host (and optianally a name) get all known
  *                        store objects.
  *   - toStoreObject : Given a store object, convert it to the required format
  *                     so that it can be transferred over wire.
  *   - populateStoresForHost : Given a host, populate the map of all store
  *                             objects for it
+ *   - getFields: Given a subType(optional), get an array of objects containing
+ *                column field info. The info includes,
+ *                "name" is name of colume key.
+ *                "editable" is 1 means editable field; 0 means uneditable.
  *
  * @param {string} typeName
  *        The typeName of the actor.
  * @param {string} observationTopic
  *        The topic which this actor listens to via Notification Observers.
  */
 StorageActors.defaults = function (typeName, observationTopic) {
   return {
@@ -543,31 +547,27 @@ StorageActors.createActor({
           }
           this.storageActor.update("cleared", "cookies", data);
         }
         break;
     }
     return null;
   },
 
-  /**
-   * This method marks the table as editable.
-   *
-   * @return {Array}
-   *         An array of column header ids.
-   */
-  getEditableFields: Task.async(function* () {
+  getFields: Task.async(function* () {
     return [
-      "name",
-      "path",
-      "host",
-      "expires",
-      "value",
-      "isSecure",
-      "isHttpOnly"
+      { name: "name", editable: 1},
+      { name: "path", editable: 1},
+      { name: "host", editable: 1},
+      { name: "expires", editable: 1},
+      { name: "lastAccessed", editable: 0},
+      { name: "value", editable: 1},
+      { name: "isDomain", editable: 0},
+      { name: "isSecure", editable: 1},
+      { name: "isHttpOnly", editable: 1}
     ];
   }),
 
   /**
    * Pass the editItem command from the content to the chrome process.
    *
    * @param {Object} data
    *        See editCookie() for format details.
@@ -1007,26 +1007,20 @@ function getObjectForLocalOrSessionStora
 
     populateStoresForHosts() {
       this.hostVsStores = new Map();
       for (let window of this.windows) {
         this.populateStoresForHost(this.getHostName(window.location), window);
       }
     },
 
-    /**
-     * This method marks the fields as editable.
-     *
-     * @return {Array}
-     *         An array of field ids.
-     */
-    getEditableFields: Task.async(function* () {
+    getFields: Task.async(function* () {
       return [
-        "name",
-        "value"
+        { name: "name", editable: 1},
+        { name: "value", editable: 1}
       ];
     }),
 
     /**
      * Edit localStorage or sessionStorage fields.
      *
      * @param {Object} data
      *        See editCookie() for format details.
@@ -1194,16 +1188,23 @@ StorageActors.createActor({
 
   processEntry: Task.async(function* (request, response) {
     return {
       url: String(request.url),
       status: String(response.statusText),
     };
   }),
 
+  getFields: Task.async(function* () {
+    return [
+      { name: "url", editable: 0 },
+      { name: "status", editable: 0 }
+    ];
+  }),
+
   getHostName(location) {
     if (!location.host) {
       return location.href;
     }
     return location.protocol + "//" + location.host;
   },
 
   populateStoresForHost: Task.async(function* (host) {
@@ -1648,16 +1649,45 @@ StorageActors.createActor({
       sendAsyncMessage("storage:storage-indexedDB-request-parent", {
         method: methodName,
         args: args
       });
 
       return deferred.promise;
     }
   },
+
+  getFields: Task.async(function* (subType) {
+    switch (subType) {
+      // Detail of database
+      case "database":
+        return [
+          { name: "objectStore", editable: 0 },
+          { name: "keyPath", editable: 0 },
+          { name: "autoIncrement", editable: 0 },
+          { name: "indexes", editable: 0 },
+        ];
+
+      // Detail of object store
+      case "object store":
+        return [
+          { name: "name", editable: 0 },
+          { name: "value", editable: 0 }
+        ];
+
+      // Detail of indexedDB for one origin
+      default:
+        return [
+          { name: "db", editable: 0 },
+          { name: "origin", editable: 0 },
+          { name: "version", editable: 0 },
+          { name: "objectStores", editable: 0 },
+        ];
+    }
+  })
 });
 
 var indexedDBHelpers = {
   backToChild(...args) {
     let mm = Cc["@mozilla.org/globalmessagemanager;1"]
                .getService(Ci.nsIMessageListenerManager);
 
     mm.broadcastAsyncMessage("storage:storage-indexedDB-request-child", {
--- a/devtools/shared/specs/storage.js
+++ b/devtools/shared/specs/storage.js
@@ -13,16 +13,24 @@ function createStorageSpec(options) {
   let methods = {
     getStoreObjects: {
       request: {
         host: Arg(0),
         names: Arg(1, "nullable:array:string"),
         options: Arg(2, "nullable:json")
       },
       response: RetVal(options.storeObjectType)
+    },
+    getFields: {
+      request: {
+        subType: Arg(0, "nullable:string")
+      },
+      response: {
+        value: RetVal("json")
+      }
     }
   };
 
   // extra methods specific for storage type
   Object.assign(methods, options.methods);
 
   childSpecs[options.typeName] = protocol.generateActorSpec({
     typeName: options.typeName,