Bug 507601 - Port |Bug 414038 - Replace rdf-driven folder pane with a js-driven/non-rdf treeview| to SeaMonkey - Update and tidy the moved code in gFolderTreeController r=frg a=frg CLOSED TREE
authorIan Neal <iann_cvs@blueyonder.co.uk>
Thu, 12 Dec 2019 19:33:00 +0100
changeset 32384 cf178c975a6a8d5227beb730289f7a5325e657aa
parent 32383 4a152649b5d834a2459d62346607e178ab11fbe2
child 32385 f103e65e5a6e2a02fe1b8b1f3bd9df9d025e6ef8
push id232
push userfrgrahl@gmx.net
push dateThu, 12 Dec 2019 18:52:58 +0000
treeherdercomm-esr60@6f606bccc252 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfrg, frg
bugs507601, 414038
Bug 507601 - Port |Bug 414038 - Replace rdf-driven folder pane with a js-driven/non-rdf treeview| to SeaMonkey - Update and tidy the moved code in gFolderTreeController r=frg a=frg CLOSED TREE
suite/mailnews/content/folderPane.js
suite/mailnews/content/mailWindowOverlay.js
suite/mailnews/content/mailWindowOverlay.xul
--- a/suite/mailnews/content/folderPane.js
+++ b/suite/mailnews/content/folderPane.js
@@ -18,40 +18,39 @@ ChromeUtils.import("resource://gre/modul
 var gFolderTreeController = {
   /**
    * Opens the dialog to create a new sub-folder, and creates it if the user
    * accepts
    *
    * @param aParent (optional)  the parent for the new subfolder
    */
   newFolder(aParent) {
-    var preselectedFolder = aParent || GetFirstSelectedMsgFolder();
-    var dualUseFolders = true;
-    var server = null;
-    var folder = null;
+    let folder = aParent || GetSelectedMsgFolders()[0];
 
-    if (preselectedFolder) {
-      try {
-        server = preselectedFolder.server;
-        if (server) {
-         folder = getDestinationFolder(preselectedFolder, server);
-
-          var imapServer = server.QueryInterface(Ci.nsIImapIncomingServer);
-          if (imapServer)
-            dualUseFolders = imapServer.dualUseFolders;
-        }
-      } catch (e) {
-        dump ("Exception: dualUseFolders = true\n");
-      }
+    // Make sure we actually can create subfolders.
+    if (!folder.canCreateSubfolders) {
+      // Check if we can create them at the root.
+      let rootMsgFolder = folder.server.rootMsgFolder;
+      if (rootMsgFolder.canCreateSubfolders)
+        folder = rootMsgFolder;
+      else // just use the default account
+        folder = GetDefaultAccountRootFolder();
     }
 
-    //xxx useless param
+    let dualUseFolders = true;
+    if (folder.server instanceof Ci.nsIImapIncomingServer)
+      dualUseFolders = folder.server.dualUseFolders;
+
     function newFolderCallback(aName, aFolder) {
+      // createSubfolder can throw an exception, causing the newFolder dialog
+      // to not close and wait for another input.
+      // TODO: Rewrite this logic and move the opening of alert dialogs from
+      // nsMsgLocalMailFolder::CreateSubfolderInternal to here (bug 831190#c16).
       if (aName)
-        folder.createSubfolder(aName, msgWindow);
+        aFolder.createSubfolder(aName, msgWindow);
     }
 
     window.openDialog("chrome://messenger/content/newFolderDialog.xul",
                       "",
                       "chrome,modal,centerscreen",
                       {folder: folder,
                        dualUseFolders: dualUseFolders,
                        okCallback: newFolderCallback});
@@ -86,18 +85,24 @@ var gFolderTreeController = {
         folder.rename(aNewName, msgWindow);
     }
 
     function rebuildSummary(msgFolder) {
       if (msgFolder.locked) {
         msgFolder.throwAlertMsg("operationFailedFolderBusy", msgWindow);
         return;
       }
-      let msgDB = msgFolder.msgDatabase;
-      msgDB.summaryValid = false;
+      if (msgFolder.supportsOffline) {
+        // Remove the offline store, if any.
+        let offlineStore = msgFolder.filePath;
+        if (offlineStore.exists())
+          offlineStore.remove(false);
+      }
+      msgFolder.msgDatabase.summaryValid = false;
+
       try {
         msgFolder.closeAndBackupFolderDB("");
       }
       catch(e) {
         // In a failure, proceed anyway since we're dealing with problems
         msgFolder.ForceDBClosed();
       }
       // these two lines will cause the thread pane to get reloaded
@@ -125,176 +130,168 @@ var gFolderTreeController = {
    * the user clicks OK in that dialog
    *
    * @param aFolder (optional)  the folder to rename, if different than the
    *                            currently selected one
    */
   renameFolder(aFolder) {
     let folder = aFolder || GetSelectedMsgFolders()[0];
 
+    let controller = this;
     function renameCallback(aName, aUri) {
+      if (aUri != folder.URI)
+        Cu.reportError("got back a different folder to rename!");
+
+      controller._resetThreadPane();
       let folderTree = GetFolderTree();
-      if (gDBView)
-        gCurrentlyDisplayedMessage = gDBView.currentlyDisplayedMessage;
-
-      ClearThreadPane();
-      ClearMessagePane();
       folderTree.view.selection.clearSelection();
 
-      try {
-        folder.rename(aName, msgWindow);
-      }
-      catch(e) {
-        SelectFolder(folder.URI);  //restore selection
-        throw(e); // so that the dialog does not automatically close
-        dump ("Exception : RenameFolder \n");
-      }
+      folder.rename(aName, msgWindow);
     }
 
     window.openDialog("chrome://messenger/content/renameFolderDialog.xul",
                       "", "chrome,modal,centerscreen",
                       {preselectedURI: folder.URI,
                        okCallback: renameCallback, name: folder.prettyName});
   },
 
   /**
    * Deletes a folder from its parent. Also handles unsubscribe from newsgroups
    * if the selected folder/s happen to be nntp.
    *
    * @param aFolder (optional) the folder to delete, if not the selected one
    */
   deleteFolder(aFolder) {
     let folders = aFolder ? [aFolder] : GetSelectedMsgFolders();
-    const NS_MSG_ERROR_COPY_FOLDER_ABORTED = 0x8055001a;
     let prompt = Services.prompt;
     for (let folder of folders) {
-      let specialFolder = getSpecialFolderString(folder);
-      if (specialFolder == "Inbox" || specialFolder == "Trash")
+      let canDelete = folder.isSpecialFolder(Ci.nsMsgFolderFlags.Junk, false) ?
+        CanRenameDeleteJunkMail(folder.URI) : folder.deletable;
+      if (!canDelete)
         continue;
 
       if (folder.flags & Ci.nsMsgFolderFlags.Virtual) {
         let confirmation = gMessengerBundle.getString("confirmSavedSearchDeleteMessage");
         let title = gMessengerBundle.getString("confirmSavedSearchDeleteTitle");
         let buttonTitle = gMessengerBundle.getString("confirmSavedSearchDeleteButton");
         let buttonFlags = prompt.BUTTON_TITLE_IS_STRING * prompt.BUTTON_POS_0 +
                           prompt.BUTTON_TITLE_CANCEL * prompt.BUTTON_POS_1;
         if (prompt.confirmEx(window, title, confirmation, buttonFlags, buttonTitle,
                              "", "", "", {}) != 0) /* the yes button is in position 0 */
           continue;
         if (gCurrentVirtualFolderUri == folder.URI)
           gCurrentVirtualFolderUri = null;
-        let array = Cc["@mozilla.org/array;1"]
-                      .createInstance(Ci.nsIMutableArray);
-        array.appendElement(folder);
-        folder.parent.deleteSubFolders(array, msgWindow);
-        continue;
       }
 
       if (isNewsURI(folder.URI)) {
-        let unsubscribe = ConfirmUnsubscribe(folder);
-        if (unsubscribe)
+        if (ConfirmUnsubscribe(folder))
           UnSubscribe(folder);
+        continue;
       }
-      else if (specialFolder == "Junk" ?
-               CanRenameDeleteJunkMail(folder.URI) : folder.deletable) {
-        // We can delete this folder.
-        let array = Cc["@mozilla.org/array;1"]
-                      .createInstance(Ci.nsIMutableArray);
-        array.appendElement(folder);
-        try {
-          folder.parent.deleteSubFolders(array, msgWindow);
+
+      // We can delete this folder.
+      let array = toXPCOMArray([folder], Ci.nsIMutableArray);
+      try {
+        folder.parent.deleteSubFolders(array, msgWindow);
+      }
+      // Ignore known errors from canceled warning dialogs.
+      catch (ex) {
+        const NS_MSG_ERROR_COPY_FOLDER_ABORTED = 0x8055001a;
+        if (ex.result != NS_MSG_ERROR_COPY_FOLDER_ABORTED) {
+          throw ex;
         }
-        // Ignore known errors from canceled warning dialogs.
-        catch (ex if (ex.result == NS_MSG_ERROR_COPY_FOLDER_ABORTED)) {}
       }
     }
   },
 
   /**
-   * Prompts the user to confirm and empties the trash for the selected folder
+   * Prompts the user to confirm and empties the trash for the selected folder.
+   * The folder and its children are only emptied if it has the proper Trash
+   * flag.
    *
-   * @param aFolder (optional)  the trash folder to empty
-   * @note Calling this function on a non-trash folder will result in strange
-   *       behavior!
+   * @param aFolder (optional)  The trash folder to empty. If unspecified or not
+   *                            a trash folder, the currently selected server's
+   *                            trash folder is used.
    */
   emptyTrash(aFolder) {
     let folder = aFolder || GetSelectedMsgFolders()[0];
+    if (!folder.getFlag(Ci.nsMsgFolderFlags.Trash))
+      folder = folder.rootFolder.getFolderWithFlags(Ci.nsMsgFolderFlags.Trash);
+    if (!folder)
+      return;
+
     if (this._checkConfirmationPrompt("emptyTrash"))
       folder.emptyTrash(msgWindow, null);
   },
 
   /**
-   * Deletes everything (folders and messages) in this folder
+   * Deletes everything (folders and messages) in the selected folder.
+   * The folder is only emptied if it has the proper Junk flag.
    *
-   * @param aFolder (optional)  the folder to empty
+   * @param aFolder (optional)  The folder to empty. If unspecified, the
+   *                            currently selected folder is used, if it
+   *                            is junk.
    */
   emptyJunk(aFolder) {
     let folder = aFolder || GetSelectedMsgFolders()[0];
 
+    if (!folder || !folder.getFlag(Ci.nsMsgFolderFlags.Junk))
+      return;
+
     if (!this._checkConfirmationPrompt("emptyJunk"))
       return;
 
     // Delete any sub-folders this folder might have.
     let iter = folder.subFolders;
     while (iter.hasMoreElements())
       folder.propagateDelete(iter.getNext(), true, msgWindow);
 
-    let children = Cc["@mozilla.org/array;1"]
-                     .createInstance(Ci.nsIMutableArray);
-
     // Now delete the messages.
-    iter = folder.messages;
-    while (iter.hasMoreElements()) {
-      children.appendElement(iter.getNext());
-    }
+    let messages = Array.from(fixIterator(folder.messages));
+    let children = toXPCOMArray(messages, Ci.nsIMutableArray);
     folder.deleteMessages(children, msgWindow, true, false, null, false);
-    children.clear();
   },
 
   /**
    * Compacts either a particular folder, or all folders
    *
    * @param aCompactAll - whether we should compact all folders
    * @param aFolder (optional) the folder to compact, if different than the
    *                           currently selected one
    */
   compactFolder(aCompactAll, aFolder) {
     let folder = aFolder || GetSelectedMsgFolders()[0];
     let isImapFolder = folder.server.type == "imap";
-    if (!isImapFolder) {
-      if (folder.expungedBytes > 0) {
-        if (gDBView) {
-          gCurrentlyDisplayedMessage = gDBView.currentlyDisplayedMessage;
-          if (gDBView.msgFolder == folder || aCompactAll) {
-            ClearThreadPaneSelection();
-            ClearThreadPane();
-            ClearMessagePane();
-          }
-        }
-      }
-      else {
-        if (!aCompactAll) // you have one local folder with no room to compact
-          return;
-      }
+    // Can't compact folders that have just been compacted
+    if (!isImapFolder && !folder.expungedBytes && !aCompactAll)
+      return;
+
+    // Reset thread pane for non-imap folders.
+    if (!isImapFolder && gDBView &&
+        (gDBView.msgFolder == folder || aCompactAll)) {
+      this._resetThreadPane();
     }
     if (aCompactAll)
       folder.compactAll(null, msgWindow, isImapFolder || folder.server.type == "nntp");
     else
       folder.compact(null, msgWindow);
   },
 
   /**
    * Opens the dialog to create a new virtual folder
    *
    * @param aName - the default name for the new folder
    * @param aSearchTerms - the search terms associated with the folder
    * @param aParent - the folder to run the search terms on
    */
   newVirtualFolder(aName, aSearchTerms, aParent) {
     let folder = aParent || GetSelectedMsgFolders()[0];
+    if (!folder)
+      folder = GetDefaultAccountRootFolder();
+
     let name = folder.prettyName;
     if (aName)
       name += "-" + aName;
 
     window.openDialog("chrome://messenger/content/virtualFolderProperties.xul",
                       "", "chrome,modal,centerscreen",
                       {folder: folder, searchTerms: aSearchTems,
                        newFolderName: name});
@@ -319,16 +316,40 @@ var gFolderTreeController = {
     window.openDialog("chrome://messenger/content/virtualFolderProperties.xul",
                       "", "chrome,modal,centerscreen",
                       {folder: folder, editExistingFolder: true,
                        onOKCallback: editVirtualCallback,
                        msgWindow:msgWindow});
   },
 
   /**
+   * Opens a search window with the given folder, or the selected one if none
+   * is given.
+   *
+   * @param [aFolder] the folder to open the search window for, if different
+   *                  from the selected one
+   */
+  searchMessages(aFolder) {
+    MsgSearchMessages(aFolder || GetSelectedMsgFolders()[0]);
+  },
+
+  /**
+   * For certain folder commands, the thread pane needs to be invalidated, this
+   * takes care of doing so.
+   */
+  _resetThreadPane() {
+    if (gDBView)
+      gCurrentlyDisplayedMessage = gDBView.currentlyDisplayedMessage;
+
+    ClearThreadPaneSelection();
+    ClearThreadPane();
+    ClearMessagePane();
+  },
+
+  /**
    * Prompts for confirmation, if the user hasn't already chosen the "don't ask
    * again" option.
    *
    * @param aCommand - the command to prompt for
    */
   _checkConfirmationPrompt(aCommand) {
     const kDontAskAgainPref = "mailnews." + aCommand + ".dontAskAgain";
     // default to ask user if the pref is not set
--- a/suite/mailnews/content/mailWindowOverlay.js
+++ b/suite/mailnews/content/mailWindowOverlay.js
@@ -1432,53 +1432,16 @@ function MsgCreateFilter()
   }
   if (!folder)
     folder = GetFirstSelectedMsgFolder();
 
     if (emailAddress)
      top.MsgFilters(emailAddress, folder);
 }
 
-function getDestinationFolder(preselectedFolder, server)
-{
-    var destinationFolder = null;
-
-    var isCreateSubfolders = preselectedFolder.canCreateSubfolders;
-    if (!isCreateSubfolders)
-    {
-        destinationFolder = server.rootMsgFolder;
-
-        var verifyCreateSubfolders = null;
-        if (destinationFolder)
-            verifyCreateSubfolders = destinationFolder.canCreateSubfolders;
-
-        // in case the server cannot have subfolders,
-        // get default account and set its incoming server as parent folder
-        if (!verifyCreateSubfolders)
-        {
-            try {
-                var defaultFolder = GetDefaultAccountRootFolder();
-                var checkCreateSubfolders = null;
-                if (defaultFolder)
-                    checkCreateSubfolders = defaultFolder.canCreateSubfolders;
-
-                if (checkCreateSubfolders)
-                    destinationFolder = defaultFolder;
-
-            } catch (e) {
-                dump ("Exception: defaultAccount Not Available\n");
-            }
-        }
-    }
-    else
-        destinationFolder = preselectedFolder;
-
-    return destinationFolder;
-}
-
 function MsgSubscribe()
 {
   var preselectedFolder = GetFirstSelectedMsgFolder();
 
   if (preselectedFolder && preselectedFolder.server.type == "rss")
     openSubscriptionsDialog(preselectedFolder); // open feed subscription dialog
   else
     Subscribe(preselectedFolder); // open imap/nntp subscription dialog
--- a/suite/mailnews/content/mailWindowOverlay.xul
+++ b/suite/mailnews/content/mailWindowOverlay.xul
@@ -410,17 +410,17 @@
               oncommand="MsgOpenNewTabForFolder();"/>
     <menuitem id="folderPaneContext-openNewWindow"
               label="&folderContextOpenNewWindow.label;"
               accesskey="&folderContextOpenNewWindow.accesskey;"
               oncommand="MsgOpenNewWindowForFolder(null,-1);"/>
     <menuitem id="folderPaneContext-searchMessages"
               label="&folderContextSearchMessages.label;"
               accesskey="&folderContextSearchMessages.accesskey;"
-              command="cmd_search"/>
+              oncommand="gFolderTreeController.searchMessages();"/>
     <menuitem id="folderPaneContext-subscribe"
         label="&folderContextSubscribe.label;"
         accesskey="&folderContextSubscribe.accesskey;"
         oncommand="MsgSubscribe();"/>
     <menuitem id="folderPaneContext-newsUnsubscribe"
         label="&folderContextUnsubscribe.label;"
         accesskey="&folderContextUnsubscribe.accesskey;"
         oncommand="MsgUnsubscribe();"/>