Bug 1518823 - Replace FakeTree with a real tree and re-enable 31 disabled tests. r=mkmelin DONTBUILD
authorGeoff Lankow <geoff@darktrojan.net>
Wed, 29 May 2019 11:48:03 +1200
changeset 35746 8c7373a378b6ddedbbc8c952c70a5ebfdf62de7e
parent 35745 c104316e6c2120b3bff9498631063a37d54530b6
child 35747 7211eed9164b20c7be4ca855151c58c4eef57c3b
push id392
push userclokep@gmail.com
push dateMon, 02 Sep 2019 20:17:19 +0000
reviewersmkmelin
bugs1518823
Bug 1518823 - Replace FakeTree with a real tree and re-enable 31 disabled tests. r=mkmelin DONTBUILD
mail/base/content/folderDisplay.js
mail/base/content/messageDisplay.js
mail/base/content/messageWindow.js
mail/test/mozmill/folder-display/test-close-window-on-delete.js
mail/test/mozmill/folder-display/test-deletion-from-virtual-folders.js
mail/test/mozmill/folder-display/test-deletion-with-multiple-displays.js
mail/test/mozmill/shared-modules/test-folder-display-helpers.js
--- a/mail/base/content/folderDisplay.js
+++ b/mail/base/content/folderDisplay.js
@@ -140,47 +140,44 @@ function FolderDisplayWidget(aTabInfo, a
   /** the next view index to select once the delete completes */
   this._nextViewIndexAfterDelete = null;
   /**
    * Track when a mass move is in effect (we get told by hintMassMoveStarting,
    *  and hintMassMoveCompleted) so that we can avoid deletion-triggered
    *  moving to _nextViewIndexAfterDelete until the mass move completes.
    */
   this._massMoveActive = false;
+  /**
+   * Track when a message is being deleted so we can respond appropriately.
+   */
+  this._deleteInProgress = false;
 
   /**
    * Used by pushNavigation to queue a navigation request for when we enter the
    *  next folder; onMessagesLoaded(true) is the one that processes it.
    */
   this._pendingNavigation = null;
 
   this._active = false;
   /**
    * A list of methods to call on 'this' object when we are next made active.
    *  This list is populated by calls to |_notifyWhenActive| when we are
    *  not active at the moment.
    */
   this._notificationsPendingActivation = [];
 
-  // Create a DOM node for the fake tree box below.
-  let domNode = document.createXULElement("vbox");
-
-  // We care about onselect events, so add a listener for that.
-  let self = this;
-  domNode.addEventListener("select", function() {
-    self.view.dbView.selectionChanged();
-  });
-
   /**
    * Create a fake tree object for if/when this folder is in the background.
-   * We need to give it a DOM object to send events to, including the onselect
-   * event we care about and for which we added a handler above, and all the
-   * other events we don't care about.
+   * Hide the tree using CSS, because if it's not attached to the document or
+   * is hidden="true", it won't fire select events and stuff will break.
    */
-  this._fakeTree = new FakeTree(domNode);
+  this._fakeTree = document.createXULElement("tree");
+  this._fakeTree.setAttribute("style", "visibility: collapse");
+  this._fakeTree.appendChild(document.createXULElement("treechildren"));
+  document.documentElement.appendChild(this._fakeTree);
 
   /**
    * Create a fake tree selection for cases where we have opened a background
    * tab. We'll get rid of this as soon as we've switched to the tab for the
    * first time, and have a real tree selection.
    */
   this._fakeTreeSelection = new JSTreeSelection(this._fakeTree);
 
@@ -820,16 +817,17 @@ FolderDisplayWidget.prototype = {
     //  anything else because closing the view could theoretically propagate
     //  down to the message display and we don't want it doing anything it
     //  doesn't have to do.
     this.messageDisplay._close();
 
     this.view.close();
     this.messenger.setWindow(null, null);
     this.messenger = null;
+    this._fakeTree.remove();
     this._fakeTree = null;
     this._fakeTreeSelection = null;
   },
   // @}
 
   /*   ===============================   */
   /* ===== IDBViewWrapper Listener ===== */
   /*   ===============================   */
@@ -1195,17 +1193,19 @@ FolderDisplayWidget.prototype = {
    * our message selection. We might know it's coming; if we do then
    * this._nextViewIndexAfterDelete should know what view index to select next.
    * For the imap mark-as-deleted we won't know beforehand.
    */
   onMessagesRemoved() {
     FolderDisplayListenerManager._fireListeners("onMessagesRemoved",
                                                 [this]);
 
-    if (this.messageDisplay.onMessagesRemoved())
+    let handled = this.messageDisplay.onMessagesRemoved();
+    this._deleteInProgress = false;
+    if (handled)
       return;
 
     // - we saw this coming
     let rowCount = this.view.dbView.rowCount;
     if (!this._massMoveActive && (this._nextViewIndexAfterDelete != null)) {
       // adjust the index if it is after the last row...
       // (this can happen if the "mail.delete_matches_sort_order" pref is not
       //  set and the message is the last message in the view.)
@@ -1411,16 +1411,17 @@ FolderDisplayWidget.prototype = {
    *  to pull on right now.
    * We use this hint to figure out the next message to display once the
    *  deletion completes.  We do this before the deletion happens because the
    *  selection is probably going away (except in the IMAP delete model), and it
    *  might be too late to figure this out after the deletion happens.
    * Our automated complement (that calls us) is updateNextMessageAfterDelete.
    */
   hintAboutToDeleteMessages() {
+    this._deleteInProgress = true;
     // save the value, even if it is nsMsgViewIndex_None.
     this._nextViewIndexAfterDelete = this.view.dbView.msgToSelectAfterDelete;
   },
 
   /**
    * The archive code tells us when it is starting to archive messages.  This
    *  is different from hinting about deletion because it will also tell us
    *  when it has completed its mass move.
@@ -1767,18 +1768,18 @@ FolderDisplayWidget.prototype = {
     //  up-to-date even while hidden in the background
     if (aNullRealTreeBoxView && this.tree)
       this.tree.view = null;
     // (and tell the db view about its selection again...)
     this.view.dbView.selection = treeSelection;
 
     // hook the dbview up to the fake tree box
     this._fakeTree.view = this.view.dbView;
-    // this.view.dbView.setTree(this._fakeTree);  // See bug 1518823.
-    // treeSelection.tree = this._fakeTree;
+    this.view.dbView.setTree(this._fakeTree);
+    treeSelection.tree = this._fakeTree;
   },
 
   /**
    * @name Command Support
    */
   // @{
 
   /**
@@ -2340,16 +2341,20 @@ FolderDisplayWidget.prototype = {
     if (this._active)
       this.ensureRowIsVisible(aViewIndex);
 
     // The saved selection is invalidated, since we've got something newer
     this._savedSelection = null;
 
     // Do this here instead of at the beginning to prevent reentrancy issues
     this._aboutToSelectMessage = false;
+
+    let tabmail = document.getElementById("tabmail");
+    if (tabmail)
+      tabmail.setTabTitle(this._tabInfo);
   },
 
   /**
    * For every selected message in the display that is part of a (displayed)
    *  thread and is not the root message, de-select it and ensure that the
    *  root message of the thread is selected.
    * This is primarily intended to be used when collapsing visible threads.
    *
@@ -2559,163 +2564,8 @@ FolderDisplayWidget.prototype = {
       if (maxRow == null || rangeMax > maxRow)
         maxRow = rangeMax;
     }
 
     this.ensureRowRangeIsVisible(minRow, maxRow);
   },
   // @}
 };
-
-/**
- * Implement a fake nsITreeBoxObject so that we can keep the view
- *  nsITreeSelection selections 'live' when they are in the background.  We need
- *  to do this because nsTreeSelection changes its behaviour (and gets ornery)
- *  if it does not have a box object.
- * This does not need to exist once we abandon multiplexed tabbing.
- *
- * Sometimes, nsTreeSelection tries to turn us into an nsIBoxObject and then in
- *  turn get the associated element, and then create DOM events on that. The
- *  only event that we care about is onselect, so we get a DOM node here (with
- *  an event listener for onselect already attached), and pass its boxObject in
- *  whenever nsTreeSelection QIs us to nsIBoxObject.
- */
-function FakeTree(aDOMNode) {
-  this.domNode = aDOMNode;
-  this.view = null;
-}
-FakeTree.prototype = {
-  view: null,
-  ensureRowIsVisible() {
-    // NOP
-  },
-  /**
-   * No need to actually invalidate, as when we re-root the view this will
-   *  happen.
-   */
-  invalidate() {
-    // NOP
-  },
-  invalidateRange() {
-    // NOP
-  },
-  invalidateRow() {
-    // NOP
-  },
-  beginUpdateBatch() {
-
-  },
-  endUpdateBatch() {
-
-  },
-  /**
-   * We're going to make an exception to our NOP rule here, as this is rather
-   * important for us to pass on. The db view calls this if a row's been
-   * inserted or deleted. Without this, the selection's going to be out of sync
-   * with the view.
-   *
-   * @param aIndex the index where the rows have been inserted or deleted
-   * @param aCount the number of rows inserted or deleted (negative for
-   *               deleted)
-   */
-  rowCountChanged(aIndex, aCount) {
-    if (aCount == 0 || !this.view)
-      // Nothing to do
-      return;
-
-    let selection = this.view.selection;
-    if (selection)
-      selection.adjustSelection(aIndex, aCount);
-  },
-  get element() { return this.domNode; },
-  get x() { return this.domNode.getBoundingClientRect().x; },
-  get y() { return this.domNode.getBoundingClientRect().y; },
-  get screenX() { return this.domNode.screenX; },
-  get screenY() { return this.domNode.screenY; },
-  get width() { return this.domNode.getBoundingClientRect().width; },
-  get height() { return this.domNode.getBoundingClientRect().height; },
-  get parentBox() { return this.domNode.boxObject.parentBox; },
-  get firstChild() { return this.domNode.boxObject.firstChild; },
-  get lastChild() { return this.domNode.boxObject.lastChild; },
-  get nextSibling() { return this.domNode.boxObject.nextSibling; },
-  get previousSibling() { return this.domNode.boxObject.previousSibling; },
-  getPropertyAsSupports(propertyName) {
-    return this.domNode.boxObject.getPropertyAsSupports(propertyName);
-  },
-  setPropertyAsSupports(propertyName, value) {
-    this.domNode.boxObject.setPropertyAsSupports(propertyName, value);
-  },
-  getProperty(propertyName) {
-    return this.domNode.boxObject.getProperty(propertyName);
-  },
-  setProperty(propertyName, value) {
-    return this.domNode.boxObject.setProperty(propertyName, value);
-  },
-  removeProperty(propertyName) {
-    return this.domNode.boxObject.removeProperty(propertyName);
-  },
-  QueryInterface: ChromeUtils.generateQI(["nsIBoxObject",
-                                          "XULTreeElement"]),
-};
-/*
- * Provide attribute and function implementations that complain very loudly if
- *  they are used.  Now, XPConnect will return an error to callers if we don't
- *  implement part of the interface signature, but this is unlikely to provide
- *  the visibility we desire.  In fact, since it is a simple nsresult error,
- *  it may make things completely crazy.  So this way we can yell via dump,
- *  throw an exception, etc.
- */
-function FTBO_stubOutAttributes(aObj, aAttribNames) {
-  for (let attrName of aAttribNames) {
-    let myAttrName = attrName;
-    aObj.__defineGetter__(attrName,
-      function() {
-        let msg = "Read access to stubbed attribute " + myAttrName;
-        dump(msg + "\n");
-        throw new Error(msg);
-      });
-    aObj.__defineSetter__(attrName,
-      function() {
-        let msg = "Write access to stubbed attribute " + myAttrName;
-        dump(msg + "\n");
-        throw new Error(msg);
-      });
-  }
-}
-function FTBO_stubOutMethods(aObj, aMethodNames) {
-  for (let methodName of aMethodNames) {
-    let myMethodName = methodName;
-    aObj[myMethodName] = function() {
-      let msg = "Call to stubbed method " + myMethodName;
-      dump(msg + "\n");
-      throw new Error(msg);
-    };
-  }
-}
-FTBO_stubOutAttributes(FakeTree.prototype, [
-  "columns",
-  "focused",
-  "treeBody",
-  "rowHeight",
-  "rowWidth",
-  "horizontalPosition",
-  "selectionRegion",
-  ]);
-FTBO_stubOutMethods(FakeTree.prototype, [
-  "getFirstVisibleRow",
-  "getLastVisibleRow",
-  "getPageLength",
-  "ensureCellIsVisible",
-  "scrollToRow",
-  "scrollByLines",
-  "scrollByPages",
-  "scrollToCell",
-  "scrollToColumn",
-  "scrollToHorizontalPosition",
-  "invalidateColumn",
-  "invalidateCell",
-  "invalidateColumnRange",
-  "getRowAt",
-  "getCellAt",
-  "getCoordsForCellItem",
-  "isCellCropped",
-  "clearStyleAndImageCaches",
-  ]);
--- a/mail/base/content/messageDisplay.js
+++ b/mail/base/content/messageDisplay.js
@@ -500,18 +500,17 @@ MessageTabDisplayWidget.prototype = {
   },
 
   onSelectedMessagesChanged() {
     // Look at the number of messages left in the db view. If there aren't any,
     // close the tab.
     if (this.folderDisplay.view.dbView.rowCount == 0) {
       if (!this.closing) {
         this.closing = true;
-        document.getElementById("tabmail").closeTab(
-            this.folderDisplay._tabInfo, true);
+        document.getElementById("tabmail").closeTab(this.folderDisplay._tabInfo, true);
       }
       return true;
     }
 
     if (!this.closing)
       document.getElementById("tabmail").setTabTitle(this.folderDisplay._tabInfo);
 
     // The db view shouldn't do anything if we're inactive or about to close
@@ -522,22 +521,23 @@ MessageTabDisplayWidget.prototype = {
     this.singleMessageDisplay = true;
     return false;
   },
 
   onMessagesRemoved() {
     if (!this.folderDisplay.treeSelection)
       return true;
 
-    if (this.folderDisplay.treeSelection.count == 0 &&
+    if (this.folderDisplay._deleteInProgress &&
         Services.prefs.getBoolPref("mail.close_message_window.on_delete")) {
       document.getElementById("tabmail").closeTab(this.folderDisplay._tabInfo,
                                                   true);
       return true;
     }
+
     return false;
   },
 
   /**
    * A message tab should never ever be blank.  Close the tab if we become
    *  blank.
    */
   clearDisplay() {
--- a/mail/base/content/messageWindow.js
+++ b/mail/base/content/messageWindow.js
@@ -97,17 +97,17 @@ StandaloneFolderDisplayWidget.prototype 
 
     // only if we're not dealing with a dummy message (from .eml file /
     //  attachment should we try and hook up the selection object.)  Otherwise
     //  the view will not operate in stand alone message mode.
     // XXX the sequencing here may break re-using a message window that is
     //  showing an .eml file to go to a real message, at least in terms of
     //  having the selection object properly associated with the tree.
     if (!this.messageDisplay.isDummy) {
-      // this.view.dbView.setTree(this._fakeTree);  // See bug 1518823.
+      this.view.dbView.setTree(this._fakeTree);
       this.view.dbView.selection = this._magicTreeSelection;
       // This lets the dbView know we don't really have a tree, so it can
       // avoid operating on messages in collapsed threads.
       this._magicTreeSelection.tree = null;
     }
     this.__proto__.__proto__.onCreatedView.call(this);
   },
 
@@ -274,17 +274,18 @@ StandaloneMessageDisplayWidget.prototype
       return true;
     }
     return false;
   },
 
   onMessagesRemoved() {
     if (!this.folderDisplay.treeSelection)
       return true;
-    if (this.folderDisplay.treeSelection.count == 0 &&
+
+    if (this.folderDisplay._deleteInProgress &&
         Services.prefs.getBoolPref("mail.close_message_window.on_delete")) {
       window.close();
       return true;
     }
     return false;
   },
 };
 
--- a/mail/test/mozmill/folder-display/test-close-window-on-delete.js
+++ b/mail/test/mozmill/folder-display/test-close-window-on-delete.js
@@ -27,17 +27,17 @@ function setupModule(module) {
 
   make_new_sets_in_folder(folder, [{count: 10}]);
 }
 
 /**
  * Delete a message and check that the message window is closed
  * where appropriate.
  */
-function disabled_test_close_message_window_on_delete_from_message_window() {
+function test_close_message_window_on_delete_from_message_window() {
   set_close_message_on_delete(true);
   be_in_folder(folder);
 
   // select the first message
   select_click_row(0);
   // display it
   let msgc = open_selected_message_in_new_window();
 
@@ -59,17 +59,17 @@ function disabled_test_close_message_win
 
   reset_close_message_on_delete();
 }
 
 /**
  * Delete a message when multiple windows are open to the message, and the
  * message is deleted from one of them.
  */
-function disabled_test_close_multiple_message_windows_on_delete_from_message_window() {
+function test_close_multiple_message_windows_on_delete_from_message_window() {
   set_close_message_on_delete(true);
   be_in_folder(folder);
 
   // select the first message
   select_click_row(0);
   // display it
   let msgc = open_selected_message_in_new_window();
   let msgcA = open_selected_message_in_new_window();
@@ -95,17 +95,17 @@ function disabled_test_close_multiple_me
 
   reset_close_message_on_delete();
 }
 
 /**
  * Delete a message when multiple windows are open to the message, and the
  * message is deleted from the 3-pane window.
  */
-function disabled_test_close_multiple_message_windows_on_delete_from_3pane_window() {
+function test_close_multiple_message_windows_on_delete_from_3pane_window() {
   set_close_message_on_delete(true);
   be_in_folder(folder);
 
   // select the first message
   select_click_row(0);
   // display it
   let msgc = open_selected_message_in_new_window();
   let msgcA = open_selected_message_in_new_window();
@@ -165,17 +165,17 @@ function test_close_message_tab_on_delet
 
   reset_close_message_on_delete();
 }
 
 /**
  * Delete a message when multiple windows are open to the message, and the
  * message is deleted from one of them.
  */
-function disabled_test_close_multiple_message_tabs_on_delete_from_message_tab() {
+function test_close_multiple_message_tabs_on_delete_from_message_tab() {
   set_close_message_on_delete(true);
   be_in_folder(folder);
 
   // select the first message
   select_click_row(0);
   // display it
   let msgc = open_selected_message_in_new_tab(true);
   open_selected_message_in_new_tab(true);
@@ -199,17 +199,17 @@ function disabled_test_close_multiple_me
 
   reset_close_message_on_delete();
 }
 
 /**
  * Delete a message when multiple tabs are open to the message, and the
  * message is deleted from the 3-pane window.
  */
-function disabled_test_close_multiple_message_tabs_on_delete_from_3pane_window() {
+function test_close_multiple_message_tabs_on_delete_from_3pane_window() {
   set_close_message_on_delete(true);
   be_in_folder(folder);
 
   // select the first message
   select_click_row(0);
   // display it
   open_selected_message_in_new_tab(true);
   open_selected_message_in_new_tab(true);
@@ -234,17 +234,17 @@ function disabled_test_close_multiple_me
 
   reset_close_message_on_delete();
 }
 
 /**
  * Delete a message when multiple windows and tabs are open to the message, and
  * the message is deleted from the 3-pane window.
  */
-function disabled_test_close_multiple_windows_tabs_on_delete_from_3pane_window() {
+function test_close_multiple_windows_tabs_on_delete_from_3pane_window() {
   set_close_message_on_delete(true);
   be_in_folder(folder);
 
   // select the first message
   select_click_row(0);
   // display it
   open_selected_message_in_new_tab(true);
   let msgcA = open_selected_message_in_new_window();
--- a/mail/test/mozmill/folder-display/test-deletion-from-virtual-folders.js
+++ b/mail/test/mozmill/folder-display/test-deletion-from-virtual-folders.js
@@ -142,33 +142,33 @@ function test_open_first_message_in_virt
 
   _open_first_message();
 }
 
 /**
  * Perform a deletion from the folder tab, verify the others update correctly
  * (advancing to the next message).
  */
-function disabled_test_delete_from_virtual_folder_in_folder_tab() {
+function test_delete_from_virtual_folder_in_folder_tab() {
   // - plan to end up on the guy who is currently at index 1
   curMessage = mc.dbView.getMsgHdrAt(1);
   // while we're at it, figure out who is at 2 for the next step
   nextMessage = mc.dbView.getMsgHdrAt(2);
   // - delete the message
   press_delete();
 
   // - verify all displays
   _verify_message_is_displayed_in(VERIFY_ALL, curMessage, 0);
 }
 
 /**
  * Perform a deletion from the message tab, verify the others update correctly
  *  (advancing to the next message).
  */
-function disabled_test_delete_from_virtual_folder_in_message_tab() {
+function test_delete_from_virtual_folder_in_message_tab() {
   switch_tab(tabMessage);
   // nextMessage is the guy we want to see once the delete completes.
   press_delete();
   curMessage = nextMessage;
 
   // - verify all displays
   _verify_message_is_displayed_in(VERIFY_ALL, curMessage, 0);
 
@@ -177,29 +177,29 @@ function disabled_test_delete_from_virtu
   if (!nextMessage)
     throw new Error("We ran out of messages early?");
 }
 
 /**
  * Perform a deletion from the message window, verify the others update
  *  correctly (advancing to the next message).
  */
-function disabled_test_delete_from_virtual_folder_in_message_window() {
+function test_delete_from_virtual_folder_in_message_window() {
   // - delete
   press_delete(msgc);
   curMessage = nextMessage;
   // - verify all displays
   _verify_message_is_displayed_in(VERIFY_ALL, curMessage, 0);
 }
 
 /**
  * Delete the last message in that folder, which should close all message
  *  displays.
  */
-function disabled_test_delete_last_message_from_virtual_folder_closes_message_displays() {
+function test_delete_last_message_from_virtual_folder_closes_message_displays() {
   // - since we have both foreground and background message tabs, we don't need
   // to open yet another tab to test
 
   // - prep for the message window disappearing
   plan_for_window_close(msgc);
 
   // - let's arbitrarily perform the deletion on this message tab
   switch_tab(tabMessage);
@@ -232,33 +232,33 @@ function test_open_first_message_in_smar
   // Open the first message
   _open_first_message();
 }
 
 /**
  * Perform a deletion from the folder tab, verify the others update correctly
  * (advancing to the next message).
  */
-function disabled_test_delete_from_smart_inbox_in_folder_tab() {
+function test_delete_from_smart_inbox_in_folder_tab() {
   // - plan to end up on the guy who is currently at index 1
   curMessage = mc.dbView.getMsgHdrAt(1);
   // while we're at it, figure out who is at 2 for the next step
   nextMessage = mc.dbView.getMsgHdrAt(2);
   // - delete the message
   press_delete();
 
   // - verify all displays
   _verify_message_is_displayed_in(VERIFY_ALL, curMessage, 0);
 }
 
 /**
  * Perform a deletion from the message tab, verify the others update correctly
  *  (advancing to the next message).
  */
-function disabled_test_delete_from_smart_inbox_in_message_tab() {
+function test_delete_from_smart_inbox_in_message_tab() {
   switch_tab(tabMessage);
   // nextMessage is the guy we want to see once the delete completes.
   press_delete();
   curMessage = nextMessage;
 
   // - verify all displays
   _verify_message_is_displayed_in(VERIFY_ALL, curMessage, 0);
 
@@ -267,29 +267,29 @@ function disabled_test_delete_from_smart
   if (!nextMessage)
     throw new Error("We ran out of messages early?");
 }
 
 /**
  * Perform a deletion from the message window, verify the others update
  *  correctly (advancing to the next message).
  */
-function disabled_test_delete_from_smart_inbox_in_message_window() {
+function test_delete_from_smart_inbox_in_message_window() {
   // - delete
   press_delete(msgc);
   curMessage = nextMessage;
   // - verify all displays
   _verify_message_is_displayed_in(VERIFY_ALL, curMessage, 0);
 }
 
 /**
  * Delete the last message in that folder, which should close all message
  *  displays.
  */
-function disabled_test_delete_last_message_from_smart_inbox_closes_message_displays() {
+function test_delete_last_message_from_smart_inbox_closes_message_displays() {
   // - since we have both foreground and background message tabs, we don't need
   // to open yet another tab to test
 
   // - prep for the message window disappearing
   plan_for_window_close(msgc);
 
   // - let's arbitrarily perform the deletion on this message tab
   switch_tab(tabMessage);
--- a/mail/test/mozmill/folder-display/test-deletion-with-multiple-displays.js
+++ b/mail/test/mozmill/folder-display/test-deletion-with-multiple-displays.js
@@ -50,17 +50,16 @@ function setupModule(module) {
   make_new_sets_in_folder(multipleDeletionFolder1, [{count: 30}]);
 
   // We're depending on selecting the last message here, so these do matter
   make_new_sets_in_folder(multipleDeletionFolder2, [{count: 10}]);
   make_new_sets_in_folder(multipleDeletionFolder3, [{count: 10}]);
   make_new_sets_in_folder(multipleDeletionFolder4, [{count: 10}]);
 }
 
-
 var tabFolder, tabMessage, tabMessageBackground, curMessage, nextMessage;
 
 /**
  * The message window controller.  Short names because controllers get used a
  *  lot.
  */
 var msgc;
 
@@ -145,33 +144,33 @@ function _verify_message_is_displayed_in
 function test_open_first_message_in_all_four_display_mechanisms() {
   _open_message_in_all_four_display_mechanisms_helper(folder, 0);
 }
 
 /**
  * Perform a deletion from the folder tab, verify the others update correctly
  *  (advancing to the next message).
  */
-function disabled_test_delete_in_folder_tab() {
+function test_delete_in_folder_tab() {
   // - plan to end up on the guy who is currently at index 1
   curMessage = mc.dbView.getMsgHdrAt(1);
   // while we're at it, figure out who is at 2 for the next step
   nextMessage = mc.dbView.getMsgHdrAt(2);
   // - delete the message
   press_delete();
 
   // - verify all displays
   _verify_message_is_displayed_in(VERIFY_ALL, curMessage, 0);
 }
 
 /**
  * Perform a deletion from the message tab, verify the others update correctly
  *  (advancing to the next message).
  */
-function disabled_test_delete_in_message_tab() {
+function test_delete_in_message_tab() {
   switch_tab(tabMessage);
   // nextMessage is the guy we want to see once the delete completes.
   press_delete();
   curMessage = nextMessage;
 
   // - verify all displays
   _verify_message_is_displayed_in(VERIFY_ALL, curMessage, 0);
 
@@ -180,29 +179,29 @@ function disabled_test_delete_in_message
   if (!nextMessage)
     throw new Error("We ran out of messages early?");
 }
 
 /**
  * Perform a deletion from the message window, verify the others update
  *  correctly (advancing to the next message).
  */
-function disabled_test_delete_in_message_window() {
+function test_delete_in_message_window() {
   // - delete
   press_delete(msgc);
   curMessage = nextMessage;
   // - verify all displays
   _verify_message_is_displayed_in(VERIFY_ALL, curMessage, 0);
 }
 
 /**
  * Delete the last message in that folder, which should close all message
  *  displays.
  */
-function disabled_test_delete_last_message_closes_message_displays() {
+function test_delete_last_message_closes_message_displays() {
   // - since we have both foreground and background message tabs, we don't need
   // to open yet another tab to test
 
   // - prep for the message window disappearing
   plan_for_window_close(msgc);
 
   // - let's arbitrarily perform the deletion on this message tab
   switch_tab(tabMessage);
@@ -239,33 +238,33 @@ function test_open_last_message_in_all_f
   // since we have four messages, index 3 is the last message.
   _open_message_in_all_four_display_mechanisms_helper(lastMessageFolder, 3);
 }
 
 /**
  * Perform a deletion from the folder tab, verify the others update correctly
  * (advancing to the next message).
  */
-function disabled_test_delete_last_message_in_folder_tab() {
+function test_delete_last_message_in_folder_tab() {
   // - plan to end up on the guy who is currently at index 2
   curMessage = mc.dbView.getMsgHdrAt(2);
   // while we're at it, figure out who is at 1 for the next step
   nextMessage = mc.dbView.getMsgHdrAt(1);
   // - delete the message
   press_delete();
 
   // - verify all displays
   _verify_message_is_displayed_in(VERIFY_ALL, curMessage, 2);
 }
 
 /**
  * Perform a deletion from the message tab, verify the others update correctly
  * (advancing to the next message).
  */
-function disabled_test_delete_last_message_in_message_tab() {
+function test_delete_last_message_in_message_tab() {
   // (we're still on the message tab, and nextMessage is the guy we want to see
   //  once the delete completes.)
   press_delete();
   curMessage = nextMessage;
 
   // - verify all displays
   _verify_message_is_displayed_in(VERIFY_ALL, curMessage, 1);
   // figure out the next guy...
@@ -274,17 +273,17 @@ function disabled_test_delete_last_messa
   if (!nextMessage)
     throw new Error("We ran out of messages early?");
 }
 
 /**
  * Perform a deletion from the message window, verify the others update
  * correctly (advancing to the next message).
  */
-function disabled_test_delete_last_message_in_message_window() {
+function test_delete_last_message_in_message_window() {
   // Vary this up. Switch to the folder tab instead of staying on the message
   // tab
   switch_tab(tabFolder);
   // - delete
   press_delete(msgc);
   curMessage = nextMessage;
   // - verify all displays
   _verify_message_is_displayed_in(VERIFY_ALL, curMessage, 0);
@@ -299,17 +298,17 @@ function disabled_test_delete_last_messa
 /*
  * Our next job is to open up a message, then delete the message one before it
  * in another view. The other selections shouldn't be affected.
  */
 
 /**
  * Test "one before" deletion in the folder tab.
  */
-function disabled_test_delete_one_before_message_in_folder_tab() {
+function test_delete_one_before_message_in_folder_tab() {
   // Open up message 4 in message tabs and a window (we'll delete message 3).
   _open_message_in_all_four_display_mechanisms_helper(oneBeforeFolder, 4);
 
   let expectedMessage = mc.dbView.getMsgHdrAt(4);
   select_click_row(3);
   press_delete();
 
   // The message tab, background message tab and window shouldn't have changed
@@ -322,17 +321,17 @@ function disabled_test_delete_one_before
   close_tab(tabMessage);
   close_tab(tabMessageBackground);
   switch_tab(tabFolder);
 }
 
 /**
  * Test "one before" deletion in the message tab.
  */
-function disabled_test_delete_one_before_message_in_message_tab() {
+function test_delete_one_before_message_in_message_tab() {
   // Open up 3 in a message tab, then select and open up 4 in a background tab
   // and window.
   select_click_row(3);
   tabMessage = open_selected_message_in_new_tab(true);
   let expectedMessage = select_click_row(4);
   tabMessageBackground = open_selected_message_in_new_tab(true);
   msgc = open_selected_message_in_new_window(true);
 
@@ -350,17 +349,17 @@ function disabled_test_delete_one_before
   close_tab(tabMessage);
   close_tab(tabMessageBackground);
   switch_tab(tabFolder);
 }
 
 /**
  * Test "one before" deletion in the message window.
  */
-function disabled_test_delete_one_before_message_in_message_window() {
+function test_delete_one_before_message_in_message_window() {
   // Open up 3 in a message window, then select and open up 4 in a background
   // and a foreground tab.
   select_click_row(3);
   msgc = open_selected_message_in_new_window();
   let expectedMessage = select_click_row(4);
   tabMessage = open_selected_message_in_new_tab();
   switch_tab(tabFolder);
   tabMessageBackground = open_selected_message_in_new_tab(true);
@@ -407,17 +406,17 @@ function test_delete_one_after_message_i
   close_tab(tabMessage);
   close_tab(tabMessageBackground);
   switch_tab(tabFolder);
 }
 
 /**
  * Test "one after" deletion in the message tab.
  */
-function disabled_test_delete_one_after_message_in_message_tab() {
+function test_delete_one_after_message_in_message_tab() {
   // Open up 5 in a message tab, then select and open up 4 in a background tab
   // and window.
   select_click_row(5);
   tabMessage = open_selected_message_in_new_tab(true);
   let expectedMessage = select_click_row(4);
   tabMessageBackground = open_selected_message_in_new_tab(true);
   msgc = open_selected_message_in_new_window(true);
 
@@ -471,17 +470,17 @@ function test_delete_one_after_message_i
  * Delete multiple messages in a folder tab. Make sure message displays at the
  * beginning, middle and end of a selection work out.
  */
 
 /**
  * Test deleting multiple messages in a folder tab, with message displays open
  * to the beginning of a selection.
  */
-function disabled_test_delete_multiple_messages_with_first_selected_message_open() {
+function test_delete_multiple_messages_with_first_selected_message_open() {
   // Open up 2 in a message tab, background tab, and message window.
   _open_message_in_all_four_display_mechanisms_helper(multipleDeletionFolder1,
                                                       2);
 
   // We'll select 2-5, 8, 9 and 10. We expect 6 to be the next displayed
   // message.
   select_click_row(2);
   select_shift_click_row(5);
@@ -502,17 +501,17 @@ function disabled_test_delete_multiple_m
   close_tab(tabMessageBackground);
   switch_tab(tabFolder);
 }
 
 /**
  * Test deleting multiple messages in a folder tab, with message displays open
  * to somewhere in the middle of a selection.
  */
-function disabled_test_delete_multiple_messages_with_nth_selected_message_open() {
+function test_delete_multiple_messages_with_nth_selected_message_open() {
   // Open up 9 in a message tab, background tab, and message window.
   _open_message_in_all_four_display_mechanisms_helper(multipleDeletionFolder1,
                                                       9);
 
   // We'll select 2-5, 8, 9 and 10. We expect 11 to be the next displayed
   // message.
   select_click_row(2);
   select_shift_click_row(5);
@@ -538,17 +537,17 @@ function disabled_test_delete_multiple_m
   close_tab(tabMessageBackground);
   switch_tab(tabFolder);
 }
 
 /**
  * Test deleting multiple messages in a folder tab, with message displays open
  * to the end of a selection.
  */
-function disabled_test_delete_multiple_messages_with_last_selected_message_open() {
+function test_delete_multiple_messages_with_last_selected_message_open() {
   // Open up 10 in a message tab, background tab, and message window.
   _open_message_in_all_four_display_mechanisms_helper(multipleDeletionFolder1,
                                                       9);
 
   // We'll select 2-5, 8, 9 and 10. We expect 11 to be the next displayed
   // message.
   select_click_row(2);
   select_shift_click_row(5);
@@ -573,17 +572,17 @@ function disabled_test_delete_multiple_m
   close_tab(tabMessageBackground);
   switch_tab(tabFolder);
 }
 
 /**
  * Test deleting multiple messages in a folder tab (including the last one!),
  * with message displays open to the beginning of a selection.
  */
-function disabled_test_delete_multiple_messages_including_the_last_one_with_first_open() {
+function test_delete_multiple_messages_including_the_last_one_with_first_open() {
   // 10 messages in this folder. Open up message 1 everywhere.
   _open_message_in_all_four_display_mechanisms_helper(multipleDeletionFolder2,
                                                       1);
 
   // We'll select 1-4, 7, 8 and 9. We expect 5 to be the next displayed message.
   select_click_row(1);
   select_shift_click_row(4);
   select_control_click_row(7);
@@ -603,17 +602,17 @@ function disabled_test_delete_multiple_m
   close_tab(tabMessageBackground);
   switch_tab(tabFolder);
 }
 
 /**
  * Test deleting multiple messages in a folder tab (including the last one!),
  * with message displays open to the middle of a selection.
  */
-function disabled_test_delete_multiple_messages_including_the_last_one_with_nth_open() {
+function test_delete_multiple_messages_including_the_last_one_with_nth_open() {
   // 10 messages in this folder. Open up message 7 everywhere.
   _open_message_in_all_four_display_mechanisms_helper(multipleDeletionFolder3,
                                                       7);
 
   // We'll select 1-4, 7, 8 and 9. We expect 6 to be the next displayed message.
   select_click_row(1);
   select_shift_click_row(4);
   select_control_click_row(7);
@@ -638,17 +637,17 @@ function disabled_test_delete_multiple_m
   close_tab(tabMessageBackground);
   switch_tab(tabFolder);
 }
 
 /**
  * Test deleting multiple messages in a folder tab (including the last one!),
  * with message displays open to the end of a selection.
  */
-function disabled_test_delete_multiple_messages_including_the_last_one_with_last_open() {
+function test_delete_multiple_messages_including_the_last_one_with_last_open() {
   // 10 messages in this folder. Open up message 9 everywhere.
   _open_message_in_all_four_display_mechanisms_helper(multipleDeletionFolder4,
                                                       9);
 
   // We'll select 1-4, 7, 8 and 9. We expect 6 to be the next displayed message.
   select_click_row(1);
   select_shift_click_row(4);
   select_control_click_row(7);
--- a/mail/test/mozmill/shared-modules/test-folder-display-helpers.js
+++ b/mail/test/mozmill/shared-modules/test-folder-display-helpers.js
@@ -805,20 +805,20 @@ function assert_number_of_tabs_open(aNum
  */
 function assert_tab_titled_from(aTab, aWhat) {
   let text;
   if (aWhat instanceof Ci.nsIMsgFolder)
     text = aWhat.prettyName;
   else if (aWhat instanceof Ci.nsIMsgDBHdr)
     text = aWhat.mime2DecodedSubject;
 
-  if (!aTab.title.includes(text))
-    mark_failure(["Tab title of tab", aTab,
-                  "should include '" + text + "' but does not." +
-                  " (Current title: '" + aTab.title + "')"]);
+  mc.waitFor(
+    () => aTab.title.includes(text),
+    `Tab title should include '${text}' but does not. (Current title: '${aTab.title}')`
+  );
 }
 
 /**
  * Assert that the given tab's title is what is given.
  *
  * @param aTab The tab to check.
  * @param aTitle The title to check.
  */