Bug 1280548 - Don't display storage-sidebar after deleting all cookies r?jdescottes draft
authorMichael Ratcliffe <mratcliffe@mozilla.com>
Tue, 13 Jun 2017 10:42:43 +0100
changeset 596599 cd04e7d361b98fa576f18c5631cacad17f5c683e
parent 595489 fe809f57bf2287bb937c3422ed03a63740b3448b
child 634007 c80b920cf6d1a947435fdae2cff8b52478498a43
push id64691
push userbmo:mratcliffe@mozilla.com
push dateMon, 19 Jun 2017 14:16:52 +0000
reviewersjdescottes
bugs1280548
milestone56.0a1
Bug 1280548 - Don't display storage-sidebar after deleting all cookies r?jdescottes MozReview-Commit-ID: 6trAoFZtsTS
devtools/client/shared/widgets/TableWidget.js
devtools/client/storage/test/browser_storage_sidebar_update.js
devtools/client/storage/ui.js
--- a/devtools/client/shared/widgets/TableWidget.js
+++ b/devtools/client/shared/widgets/TableWidget.js
@@ -138,17 +138,22 @@ TableWidget.prototype = {
     return null;
   },
 
   /**
    * Select the row corresponding to the json object `id`
    */
   set selectedRow(id) {
     for (let column of this.columns.values()) {
-      column.selectRow(id[this.uniqueId] || id);
+      if (id) {
+        column.selectRow(id[this.uniqueId] || id);
+      } else {
+        column.selectedRow = null;
+        column.selectRow(null);
+      }
     }
   },
 
 /**
  * Is a row currently selected?
  *
  * @return {Boolean}
  *         true or false.
@@ -848,17 +853,18 @@ TableWidget.prototype = {
 
     if (!removed) {
       return;
     }
     for (let column of this.columns.values()) {
       column.remove(item);
       column.updateZebra();
     }
-    if (this.items.size == 0) {
+    if (this.items.size === 0) {
+      this.selectedRow = null;
       this.tbody.setAttribute("empty", "empty");
     }
 
     this.emit(EVENTS.ROW_REMOVED, item);
   },
 
   /**
    * Updates the items in the row corresponding to the `item` object previously
@@ -891,16 +897,18 @@ TableWidget.prototype = {
   clear: function () {
     this.items.clear();
     for (let column of this.columns.values()) {
       column.clear();
     }
     this.tbody.setAttribute("empty", "empty");
     this.setPlaceholderText(this.emptyText);
 
+    this.selectedRow = null;
+
     this.emit(EVENTS.TABLE_CLEARED, this);
   },
 
   /**
    * Sorts the table by a given column.
    *
    * @param {string} column
    *        The id of the column on which the table should be sorted.
@@ -1242,25 +1250,26 @@ Column.prototype = {
     this.selectedRow = null;
   },
 
   /**
    * Selects the row at the `index` index
    */
   selectRowAt: function (index) {
     if (this.selectedRow != null) {
-      this.cells[this.items[this.selectedRow]].toggleClass("theme-selected");
+      this.cells[this.items[this.selectedRow]].classList.remove("theme-selected");
     }
-    if (index < 0) {
+
+    let cell = this.cells[index];
+    if (cell) {
+      cell.classList.add("theme-selected");
+      this.selectedRow = cell.id;
+    } else {
       this.selectedRow = null;
-      return;
     }
-    let cell = this.cells[index];
-    cell.toggleClass("theme-selected");
-    this.selectedRow = cell.id;
   },
 
   /**
    * Selects the row with the object having the `uniqueId` value as `id`
    */
   selectRow: function (id) {
     this._updateItems();
     this.selectRowAt(this.items[id]);
@@ -1414,17 +1423,16 @@ Column.prototype = {
 
   /**
    * Clears the current column
    */
   clear: function () {
     this.cells = [];
     this.items = {};
     this._itemsDirty = false;
-    this.selectedRow = null;
     while (this.header.nextSibling) {
       this.header.nextSibling.remove();
     }
   },
 
   /**
    * Sorts the given items and returns the sorted list if the table was sorted
    * by this column.
@@ -1445,41 +1453,43 @@ Column.prototype = {
             a[this.id].textContent : a[this.id];
         let val2 = (b[this.id] instanceof Node) ?
             b[this.id].textContent : b[this.id];
         return naturalSortCaseInsensitive(val2, val1);
       });
     }
 
     if (this.selectedRow) {
-      this.cells[this.items[this.selectedRow]].toggleClass("theme-selected");
+      this.cells[this.items[this.selectedRow]].classList.remove("theme-selected");
     }
     this.items = {};
     // Otherwise, just use the sorted array passed to update the cells value.
     items.forEach((item, i) => {
       this.items[item[this.uniqueId]] = i;
       this.cells[i].value = item[this.id];
       this.cells[i].id = item[this.uniqueId];
     });
     if (this.selectedRow) {
-      this.cells[this.items[this.selectedRow]].toggleClass("theme-selected");
+      this.cells[this.items[this.selectedRow]].classList.add("theme-selected");
     }
     this._itemsDirty = false;
     this.updateZebra();
     return items;
   },
 
   updateZebra() {
     this._updateItems();
     let i = 0;
     for (let cell of this.cells) {
       if (!cell.hidden) {
         i++;
       }
-      cell.toggleClass("even", !(i % 2));
+
+      let even = !(i % 2);
+      cell.classList.toggle("even", even);
     }
   },
 
   /**
    * Click event handler for the column. Used to detect click on header for
    * for sorting.
    */
   onClick: function (event) {
@@ -1605,18 +1615,18 @@ Cell.prototype = {
       this.label.setAttribute("value", value + "");
     }
   },
 
   get value() {
     return this._value;
   },
 
-  toggleClass: function (className, condition) {
-    this.label.classList.toggle(className, condition);
+  get classList() {
+    return this.label.classList;
   },
 
   /**
    * Flashes the cell for a brief time. This when done for with cells in all
    * columns, makes it look like the row is being highlighted/flashed.
    */
   flash: function () {
     if (!this.label.parentNode) {
--- a/devtools/client/storage/test/browser_storage_sidebar_update.js
+++ b/devtools/client/storage/test/browser_storage_sidebar_update.js
@@ -21,17 +21,17 @@ add_task(function* () {
 
   is(gUI.sidebar.hidden, false, "sidebar is visible");
 
   // do several updates in a row and wait for them to finish
   let updates = [];
   for (let i = 0; i < UPDATE_COUNT; i++) {
     info(`Performing update #${i}`);
     updates.push(gUI.once("sidebar-updated"));
-    gUI.displayObjectSidebar();
+    gUI.updateObjectSidebar();
   }
   yield promise.all(updates);
 
   info("Updates performed, going to verify result");
   let parsedScope = gUI.view.getScopeAtIndex(1);
   let elements = parsedScope.target.querySelectorAll(
     `.name[value="${ITEM_NAME}"]`);
   is(elements.length, 1,
--- a/devtools/client/storage/ui.js
+++ b/devtools/client/storage/ui.js
@@ -108,18 +108,18 @@ function StorageUI(front, target, panelW
 
   let tableNode = this._panelDoc.getElementById("storage-table");
   this.table = new TableWidget(tableNode, {
     emptyText: L10N.getStr("table.emptyText"),
     highlightUpdated: true,
     cellContextMenuId: "storage-table-popup"
   });
 
-  this.displayObjectSidebar = this.displayObjectSidebar.bind(this);
-  this.table.on(TableWidget.EVENTS.ROW_SELECTED, this.displayObjectSidebar);
+  this.updateObjectSidebar = this.updateObjectSidebar.bind(this);
+  this.table.on(TableWidget.EVENTS.ROW_SELECTED, this.updateObjectSidebar);
 
   this.handleScrollEnd = this.handleScrollEnd.bind(this);
   this.table.on(TableWidget.EVENTS.SCROLL_END, this.handleScrollEnd);
 
   this.editItem = this.editItem.bind(this);
   this.table.on(TableWidget.EVENTS.CELL_EDIT, this.editItem);
 
   this.sidebar = this._panelDoc.getElementById("storage-sidebar");
@@ -211,17 +211,17 @@ StorageUI.prototype = {
   sidebarToggledOpen: null,
   shouldLoadMoreItems: true,
 
   set animationsEnabled(value) {
     this._panelDoc.documentElement.classList.toggle("no-animate", !value);
   },
 
   destroy: function () {
-    this.table.off(TableWidget.EVENTS.ROW_SELECTED, this.displayObjectSidebar);
+    this.table.off(TableWidget.EVENTS.ROW_SELECTED, this.updateObjectSidebar);
     this.table.off(TableWidget.EVENTS.SCROLL_END, this.handleScrollEnd);
     this.table.off(TableWidget.EVENTS.CELL_EDIT, this.editItem);
     this.table.destroy();
 
     this.front.off("stores-update", this.onUpdate);
     this.front.off("stores-cleared", this.onCleared);
     this._panelDoc.removeEventListener("keypress", this.handleKeypress);
     this.searchBox.removeEventListener("input", this.filterItems);
@@ -314,27 +314,26 @@ StorageUI.prototype = {
   },
 
   /**
    * 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) {
-    if (this.table.isSelected(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.displayObjectSidebar();
-    } else {
-      this.table.remove(name);
     }
+
+    this.table.remove(name);
+    this.updateObjectSidebar();
   },
 
   /**
    * Event handler for "stores-cleared" event coming from the storage actor.
    *
    * @param {object} response
    *        An object containing which storage types were cleared
    */
@@ -661,29 +660,33 @@ StorageUI.prototype = {
       }
     }
   },
 
   /**
    * Populates the selected entry from the table in the sidebar for a more
    * detailed view.
    */
-  displayObjectSidebar: Task.async(function* () {
+  updateObjectSidebar: Task.async(function* () {
     let item = this.table.selectedRow;
-    if (!item) {
-      // Make sure that sidebar is hidden and return
-      this.sidebar.hidden = true;
-      this.updateSidebarToggleButton();
-      return;
+    let value;
+
+    // Get the string value (async action) and the update the UI synchronously.
+    if (item && item.name && item.valueActor) {
+      value = yield item.valueActor.string();
     }
 
-    // Get the string value (async action) and the update the UI synchronously.
-    let value;
-    if (item.name && item.valueActor) {
-      value = yield item.valueActor.string();
+    // Bail if the selectedRow is no longer selected, the item doesn't exist or the state
+    // changed in another way during the above yield.
+    if (this.table.items.size === 0 ||
+        !item ||
+        !this.table.selectedRow ||
+        item.uniqueKey !== this.table.selectedRow.uniqueKey) {
+      this.hideSidebar();
+      return;
     }
 
     // Start updating the UI. Everything is sync beyond this point.
     if (this.sidebarToggledOpen === null || this.sidebarToggledOpen === true) {
       this.sidebar.hidden = false;
     }
 
     this.updateSidebarToggleButton();
@@ -974,17 +977,17 @@ 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.displayObjectSidebar();
+            this.updateObjectSidebar();
           }
           break;
       }
 
       this.shouldLoadMoreItems = true;
     }
   },