Bug 1264582 - Table headers are not removed when selecting an empty storage
MozReview-Commit-ID: Hcfw7dyrDpV
--- a/devtools/client/storage/ui.js
+++ b/devtools/client/storage/ui.js
@@ -219,22 +219,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();
@@ -480,21 +483,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);
}
}),
@@ -725,46 +742,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,