--- a/devtools/client/storage/ui.js
+++ b/devtools/client/storage/ui.js
@@ -294,17 +294,17 @@ StorageUI.prototype = {
},
/**
* Make column fields editable
*
* @param {Array} editableFields
* An array of keys of columns to be made editable
*/
- makeFieldsEditable: function* (editableFields) {
+ 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) {
@@ -313,28 +313,28 @@ StorageUI.prototype = {
front.editItem(data);
},
/**
* Removes the given item from the storage table. Reselects the next item in
* the table and repopulates the sidebar with that item's data if the item
* being removed was selected.
*/
- removeItemFromTable: function (name) {
+ removeItemFromTable: Task.async(function* (name) {
if (this.table.isSelected(name) && this.table.items.size > 1) {
if (this.table.selectedIndex == 0) {
this.table.selectNextRow();
} else {
this.table.selectPreviousRow();
}
}
this.table.remove(name);
- this.updateObjectSidebar();
- },
+ yield this.updateObjectSidebar();
+ }),
/**
* Event handler for "stores-cleared" event coming from the storage actor.
*
* @param {object} response
* An object containing which storage types were cleared
*/
onCleared: function (response) {
@@ -396,74 +396,80 @@ StorageUI.prototype = {
* }, ...
* }
* Where store_type1 and store_type2 is one of cookies, indexedDB,
* sessionStorage and localStorage; host1, host2 are the host in which
* this change happened; and [<store_namesX] is an array of the names
* of the changed store objects. This array is empty for deleted object
* if the host was completely removed.
*/
- onUpdate: function ({ changed, added, deleted }) {
- if (deleted) {
- this.handleDeletedItems(deleted);
- }
-
+ onUpdate: Task.async(function* ({ changed, added, deleted }) {
if (added) {
- this.handleAddedItems(added);
+ yield this.handleAddedItems(added);
}
if (changed) {
- this.handleChangedItems(changed);
+ yield this.handleChangedItems(changed);
+ }
+
+ // We are dealing with batches of changes here. Deleted **MUST** come last in case it
+ // is in the same batch as added and changed events e.g.
+ // - An item is changed then deleted in the same batch: deleted then changed will
+ // display an item that has been deleted.
+ // - An item is added then deleted in the same batch: deleted then added will
+ // display an item that has been deleted.
+ if (deleted) {
+ this.handleDeletedItems(deleted);
}
if (added || deleted || changed) {
this.emit("store-objects-updated");
}
- },
+ }),
/**
* Handle added items received by onUpdate
*
* @param {object} See onUpdate docs
*/
- handleAddedItems: function (added) {
+ handleAddedItems: Task.async(function* (added) {
for (let type in added) {
for (let host in added[type]) {
this.tree.add([type, {id: host, type: "url"}]);
for (let name of added[type][host]) {
try {
name = JSON.parse(name);
if (name.length == 3) {
name.splice(2, 1);
}
this.tree.add([type, host, ...name]);
if (!this.tree.selectedItem) {
this.tree.selectedItem = [type, host, name[0], name[1]];
- this.fetchStorageObjects(type, host, [JSON.stringify(name)],
- REASON.NEW_ROW);
+ yield this.fetchStorageObjects(type, host, [JSON.stringify(name)],
+ REASON.NEW_ROW);
}
} catch (ex) {
// Do nothing
}
}
if (this.tree.isSelected([type, host])) {
- this.fetchStorageObjects(type, host, added[type][host],
- REASON.NEW_ROW);
+ yield this.fetchStorageObjects(type, host, added[type][host],
+ REASON.NEW_ROW);
}
}
}
- },
+ }),
/**
* Handle deleted items received by onUpdate
*
* @param {object} See onUpdate docs
*/
- handleDeletedItems: function (deleted) {
+ handleDeletedItems: Task.async(function* (deleted) {
for (let type in deleted) {
for (let host in deleted[type]) {
if (!deleted[type][host].length) {
// This means that the whole host is deleted, thus the item should
// be removed from the storage tree
if (this.tree.isSelected([type, host])) {
this.table.clear();
this.hideSidebar();
@@ -486,54 +492,54 @@ StorageUI.prototype = {
}
this.tree.remove([type, host, ...names]);
}
// Remove the item from table if currently displayed.
if (names.length > 0) {
let tableItemName = names.pop();
if (this.tree.isSelected([type, host, ...names])) {
- this.removeItemFromTable(tableItemName);
+ yield this.removeItemFromTable(tableItemName);
}
}
} catch (ex) {
if (this.tree.isSelected([type, host])) {
- this.removeItemFromTable(name);
+ yield this.removeItemFromTable(name);
}
}
}
}
}
}
- },
+ }),
/**
* Handle changed items received by onUpdate
*
* @param {object} See onUpdate docs
*/
- handleChangedItems: function (changed) {
+ handleChangedItems: Task.async(function* (changed) {
let [type, host, db, objectStore] = this.tree.selectedItem;
if (!changed[type] || !changed[type][host] ||
changed[type][host].length == 0) {
return;
}
try {
let toUpdate = [];
for (let name of changed[type][host]) {
let names = JSON.parse(name);
if (names[0] == db && names[1] == objectStore && names[2]) {
toUpdate.push(name);
}
}
- this.fetchStorageObjects(type, host, toUpdate, REASON.UPDATE);
+ yield this.fetchStorageObjects(type, host, toUpdate, REASON.UPDATE);
} catch (ex) {
- this.fetchStorageObjects(type, host, changed[type][host], REASON.UPDATE);
+ yield this.fetchStorageObjects(type, host, changed[type][host], REASON.UPDATE);
}
- },
+ }),
/**
* Fetches the storage objects from the storage actor and populates the
* storage table with the returned data.
*
* @param {string} type
* The type of storage. Ex. "cookies"
* @param {string} host
@@ -579,45 +585,45 @@ StorageUI.prototype = {
this.actorSupportsRemoveAll =
yield this._target.actorHasMethod(type, "removeAll");
yield this.resetColumns(type, host, subType);
}
let {data} = yield storageType.getStoreObjects(host, names, fetchOpts);
if (data.length) {
- this.populateTable(data, reason);
+ yield this.populateTable(data, reason);
}
- yield this.updateToolbar();
+ this.updateToolbar();
this.emit("store-objects-updated");
} catch (ex) {
console.error(ex);
}
}),
/**
* Updates the toolbar hiding and showing buttons as appropriate.
*/
- updateToolbar: Task.async(function* () {
+ updateToolbar: function () {
let item = this.tree.selectedItem;
let howManyNodesIn = item ? item.length : 0;
// The first node is just a title e.g. "Cookies" so we need to be at least
// 2 nodes in to show the add button.
let canAdd = this.actorSupportsAddItem && howManyNodesIn > 1;
if (canAdd) {
this._addButton.hidden = false;
this._addButton.setAttribute("tooltiptext",
L10N.getFormatStr("storage.popupMenu.addItemLabel"));
} else {
this._addButton.hidden = true;
this._addButton.removeAttribute("tooltiptext");
}
- }),
+ },
/**
* Populates the storage tree which displays the list of storages present for
* the page.
*
* @param {object} storageTypes
* List of storages and their corresponding hosts returned by the
* StorageFront.listStores call.
@@ -852,17 +858,17 @@ StorageUI.prototype = {
* from the storage details and populates the storage tree.
*
* @param {string} event
* The name of the event fired
* @param {array} item
* An array of ids which represent the location of the selected item in
* the storage tree
*/
- onHostSelect: function (event, item) {
+ onHostSelect: Task.async(function* (event, item) {
this.table.clear();
this.hideSidebar();
this.searchBox.value = "";
let [type, host] = item;
this.table.host = host;
this.table.datatype = type;
@@ -870,33 +876,33 @@ StorageUI.prototype = {
let names = null;
if (!host) {
return;
}
if (item.length > 2) {
names = [JSON.stringify(item.slice(2))];
}
- this.fetchStorageObjects(type, host, names, REASON.POPULATE);
+ yield this.fetchStorageObjects(type, host, names, REASON.POPULATE);
this.itemOffset = 0;
- },
+ }),
/**
* Resets the column headers in the storage table with the pased object `data`
*
* @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* (type, host, subtype) {
+ resetColumns: Task.async(function* (type, host, subtype) {
this.table.host = host;
this.table.datatype = type;
let uniqueKey = null;
let columns = {};
let editableFields = [];
let hiddenFields = [];
let privateFields = [];
@@ -935,28 +941,28 @@ StorageUI.prototype = {
} else {
columns[f.name] = columnName;
}
});
this.table.setColumns(columns, null, hiddenFields, privateFields);
this.hideSidebar();
- yield this.makeFieldsEditable(editableFields);
- },
+ 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
* See REASON constant at top of file.
*/
- populateTable: function (data, reason) {
+ populateTable: Task.async(function* (data, reason) {
for (let item of data) {
if (item.value) {
item.valueActor = item.value;
item.value = item.value.initial || "";
}
if (item.expires != null) {
item.expires = item.expires
? new Date(item.expires).toUTCString()
@@ -977,24 +983,24 @@ StorageUI.prototype = {
case REASON.NEW_ROW:
case REASON.NEXT_50_ITEMS:
// Update and flash the row.
this.table.push(item, false);
break;
case REASON.UPDATE:
this.table.update(item);
if (item == this.table.selectedRow && !this.sidebar.hidden) {
- this.updateObjectSidebar();
+ yield this.updateObjectSidebar();
}
break;
}
this.shouldLoadMoreItems = true;
}
- },
+ }),
/**
* Handles keypress event on the body table to close the sidebar when open
*
* @param {DOMEvent} event
* The event passed by the keypress event.
*/
handleKeypress: function (event) {
@@ -1014,31 +1020,31 @@ StorageUI.prototype = {
let value = this.searchBox.value;
this.table.filterItems(value, ["valueActor"]);
this._panelDoc.documentElement.classList.toggle("filtering", !!value);
},
/**
* Handles endless scrolling for the table
*/
- handleScrollEnd: function () {
+ handleScrollEnd: Task.async(function* () {
if (!this.shouldLoadMoreItems) {
return;
}
this.shouldLoadMoreItems = false;
this.itemOffset += 50;
let item = this.tree.selectedItem;
let [type, host] = item;
let names = null;
if (item.length > 2) {
names = [JSON.stringify(item.slice(2))];
}
- this.fetchStorageObjects(type, host, names, REASON.NEXT_50_ITEMS);
- },
+ yield this.fetchStorageObjects(type, host, names, REASON.NEXT_50_ITEMS);
+ }),
/**
* Fires before a cell context menu with the "Add" or "Delete" action is
* shown. If the currently selected storage object doesn't support adding or
* removing items, prevent showing the menu.
*/
onTablePopupShowing: function (event) {
let selectedItem = this.tree.selectedItem;