Bug 453908 - Convert RDF consumers to use the folder-lookup service in JavaScript code. r=darktrojan,aceman
authorBen Campbell <benc@thunderbird.net>
Tue, 20 Nov 2018 10:13:37 +1300
changeset 33760 2c8a0efaccfe8e8ce3249001f0c60bb5f876c0aa
parent 33759 cd6d89475b76f61404553532c8e8ca87d0307b5c
child 33761 46fb423d7ced6a68bcc7bbb9b3a8e788a8a695cf
push id388
push userclokep@gmail.com
push dateMon, 28 Jan 2019 20:54:56 +0000
reviewersdarktrojan, aceman
bugs453908
Bug 453908 - Convert RDF consumers to use the folder-lookup service in JavaScript code. r=darktrojan,aceman Based originally on patches from jcranmer and aceman. All folder lookups in JS now go through the MailUtil.jsm functions getExistingFolder() and getOrCreateFolder() instead of directly using the RDF service. This is designed to make it really explicit when calling code might be expected to be dealing with a detached folder, so we can go through and remove all such cases.
mail/base/content/SearchDialog.js
mail/base/content/folderDisplay.js
mail/base/content/mail3PaneWindowCommands.js
mail/base/content/mailCommands.js
mail/base/content/mailTabs.js
mail/base/content/mailWindow.js
mail/base/content/mailWindowOverlay.js
mail/base/content/messageWindow.js
mail/base/content/msgMail3PaneWindow.js
mail/base/modules/MailUtils.jsm
mail/components/compose/content/MsgComposeCommands.js
mail/test/mozmill/folder-display/test-message-commands.js
mail/test/mozmill/folder-pane/test-folder-pane.js
mailnews/base/content/junkCommands.js
mailnews/base/content/msgFolderPickerOverlay.js
mailnews/base/content/msgSynchronize.js
mailnews/base/content/subscribe.js
mailnews/base/content/virtualFolderListEdit.js
mailnews/base/content/virtualFolderProperties.js
mailnews/base/prefs/content/am-copies.js
mailnews/base/prefs/content/am-junk.js
mailnews/base/prefs/content/am-server.js
mailnews/base/prefs/content/amUtils.js
mailnews/base/public/nsIFolderLookupService.idl
mailnews/base/search/content/FilterEditor.js
mailnews/base/search/content/searchWidgets.js
mailnews/base/search/content/searchWidgets.xml
mailnews/base/src/folderLookupService.js
mailnews/base/src/nsMailNewsCommandLineHandler.js
mailnews/base/src/virtualFolderWrapper.js
mailnews/db/gloda/modules/datamodel.js
mailnews/db/gloda/modules/index_msg.js
mailnews/db/gloda/test/unit/base_index_messages.js
mailnews/extensions/newsblog/content/FeedUtils.jsm
mailnews/imap/test/unit/test_saveTemplate.js
mailnews/jsaccount/test/unit/test_jaMsgFolder.js
--- a/mail/base/content/SearchDialog.js
+++ b/mail/base/content/SearchDialog.js
@@ -520,18 +520,17 @@ function MsgDeleteSelectedMessages(aComm
 function MoveMessageInSearch(destFolder) {
   // Get the msg folder we're moving messages into.
   // If the id (uri) is not set, use file-uri which is set for
   // "File Here".
   let destUri = destFolder.getAttribute("id");
   if (destUri.length == 0)
     destUri = destFolder.getAttribute("file-uri");
 
-  let destMsgFolder = MailUtils.getFolderForURI(destUri)
-    .QueryInterface(Ci.nsIMsgFolder);
+  let destMsgFolder = MailUtils.getOrCreateFolder(destUri);
 
   gFolderDisplay.hintAboutToDeleteMessages();
   gFolderDisplay.doCommandWithFolder(nsMsgViewCommandType.moveMessages,
                                      destMsgFolder);
 }
 
 function OpenInFolder() {
   MailUtils.displayMessageInFolderTab(gFolderDisplay.selectedMessage);
--- a/mail/base/content/folderDisplay.js
+++ b/mail/base/content/folderDisplay.js
@@ -748,17 +748,17 @@ FolderDisplayWidget.prototype = {
   // @}
 
   /**
    * @name What To Display
    * @protected
    */
   // @{
   showFolderUri(aFolderURI) {
-    return this.show(MailUtils.getFolderForURI(aFolderURI));
+    return this.show(MailUtils.getExistingFolder(aFolderURI));
   },
 
   /**
    * Invoked by showFolder when it turns out the folder is in fact a server.
    * @private
    */
   _showServer() {
     // currently nothing to do.  makeActive handles everything for us (because
--- a/mail/base/content/mail3PaneWindowCommands.js
+++ b/mail/base/content/mail3PaneWindowCommands.js
@@ -545,19 +545,19 @@ var DefaultController = {
         if (Services.prefs.getBoolPref("mail.last_msg_movecopy_was_move")) {
           let loadedFolder = gFolderTreeView.getSelectedFolders()[0];
           if (loadedFolder && !loadedFolder.canDeleteMessages)
             return false;
         }
         let targetURI = Services.prefs.getCharPref("mail.last_msg_movecopy_target_uri");
         if (!targetURI)
           return false;
-        let targetFolder = MailUtils.getFolderForURI(targetURI);
-        // If parent is null, folder doesn't exist.
-        return targetFolder && targetFolder.parent &&
+        let targetFolder = MailUtils.getExistingFolder(targetURI);
+        // If null, folder doesn't exist.
+        return (targetFolder !== null) &&
                gFolderDisplay.selectedCount > 0;
       case "cmd_fullZoomReduce":
       case "cmd_fullZoomEnlarge":
       case "cmd_fullZoomReset":
       case "cmd_fullZoomToggle":
         // If we are a message tab, then we've got a message displayed, so
         // always allow zooming in the message
         if (document.getElementById("tabmail").selectedTab.mode.name == "message")
@@ -940,17 +940,17 @@ var DefaultController = {
           break;
       case "cmd_synchronizeOffline":
           MsgSynchronizeOffline();
           break;
       case "cmd_settingsOffline":
           MailOfflineMgr.openOfflineAccountSettings();
           break;
       case "cmd_moveToFolderAgain":
-          var folder = MailUtils.getFolderForURI(
+          var folder = MailUtils.getOrCreateFolder(
             Services.prefs.getCharPref("mail.last_msg_movecopy_target_uri"));
           if (Services.prefs.getBoolPref("mail.last_msg_movecopy_was_move"))
             MsgMoveMessage(folder);
           else
             MsgCopyMessage(folder);
           break;
       case "cmd_selectAll":
         // XXX If the message pane is selected but the tab focused, this ends
--- a/mail/base/content/mailCommands.js
+++ b/mail/base/content/mailCommands.js
@@ -331,17 +331,17 @@ function Subscribe(preselectedMsgFolder)
                     {
                       folder: preselectedMsgFolder,
                       okCallback: SubscribeOKCallback,
                     });
 }
 
 function SubscribeOKCallback(changeTable) {
   for (var serverURI in changeTable) {
-    var folder = MailUtils.getFolderForURI(serverURI, true);
+    var folder = MailUtils.getExistingFolder(serverURI);
     var server = folder.server;
     var subscribableServer =
           server.QueryInterface(Ci.nsISubscribableServer);
 
     for (var name in changeTable[serverURI]) {
       if (changeTable[serverURI][name]) {
         try {
           subscribableServer.subscribe(name);
@@ -433,17 +433,17 @@ saveAsUrlListener.prototype = {
     messenger.saveAs(this.uri, false, this.identity, null);
   },
 };
 
 function SaveAsTemplate(uri) {
   if (uri) {
     let hdr = messenger.msgHdrFromURI(uri);
     let identity = getIdentityForHeader(hdr, Ci.nsIMsgCompType.Template);
-    let templates = MailUtils.getFolderForURI(identity.stationeryFolder, false);
+    let templates = MailUtils.getOrCreateFolder(identity.stationeryFolder);
     if (!templates.parent) {
       templates.setFlag(Ci.nsMsgFolderFlags.Templates);
       let isAsync = templates.server.protocolInfo.foldersCreatedAsync;
       templates.createStorageIfMissing(new saveAsUrlListener(uri, identity));
       if (isAsync)
         return;
     }
     messenger.saveAs(uri, false, identity, null);
--- a/mail/base/content/mailTabs.js
+++ b/mail/base/content/mailTabs.js
@@ -1,14 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 ChromeUtils.import("resource:///modules/MsgHdrSyntheticView.jsm");
 ChromeUtils.import("resource:///modules/errUtils.js");
+ChromeUtils.import("resource:///modules/MailUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 /**
  * Displays message "folder"s, mail "message"s, and "glodaList" results.  The
  *  commonality is that they all use the "mailContent" panel's folder tree,
  *  thread tree, and message pane objects.  This happens for historical reasons,
  *  likely involving the fact that prior to the introduction of this
  *  abstraction, everything was always stored in global objects.  For the 3.0
@@ -169,20 +170,17 @@ var mailTabType = {
           return retval;
         } catch (e) {
           logException(e);
           return null;
         }
       },
       restoreTab(aTabmail, aPersistedState) {
       try {
-        let rdfService = Cc["@mozilla.org/rdf/rdf-service;1"]
-                           .getService(Ci.nsIRDFService);
-        let folder = rdfService.GetResource(aPersistedState.folderURI)
-                       .QueryInterface(Ci.nsIMsgFolder);
+        let folder = MailUtils.getExistingFolder(aPersistedState.folderURI);
         // if the folder no longer exists, we can't restore the tab
         if (folder) {
           let folderPaneVisible = ("folderPaneVisible" in aPersistedState) ?
                                     aPersistedState.folderPaneVisible :
                                     true;
           // If we are talking about the first tab, it already exists and we
           //  should poke it.  We are assuming it is the currently displayed
           //  tab because we are privvy to the implementation details and know
--- a/mail/base/content/mailWindow.js
+++ b/mail/base/content/mailWindow.js
@@ -471,17 +471,17 @@ nsMsgStatusFeedback.prototype = {
 
 function nsMsgWindowCommands() {
 }
 
 nsMsgWindowCommands.prototype = {
   QueryInterface: ChromeUtils.generateQI(["nsIMsgWindowCommands"]),
 
   selectFolder(folderUri) {
-    gFolderTreeView.selectFolder(MailUtils.getFolderForURI(folderUri));
+    gFolderTreeView.selectFolder(MailUtils.getOrCreateFolder(folderUri));
   },
 
   selectMessage(messageUri) {
     let msgHdr = messenger.msgHdrFromURI(messageUri);
     gFolderDisplay.selectMessage(msgHdr);
   },
 
   clearMsgPane() {
--- a/mail/base/content/mailWindowOverlay.js
+++ b/mail/base/content/mailWindowOverlay.js
@@ -596,17 +596,17 @@ function showCommandInSpecialFolder(aCom
  * The menu item label and accesskey are adjusted to include the folder name.
  *
  * @param aMenuItem the menu item to adjust
  */
 function initMoveToFolderAgainMenu(aMenuItem) {
   var lastFolderURI = Services.prefs.getCharPref("mail.last_msg_movecopy_target_uri");
   var isMove = Services.prefs.getBoolPref("mail.last_msg_movecopy_was_move");
   if (lastFolderURI) {
-    var destMsgFolder = MailUtils.getFolderForURI(lastFolderURI);
+    var destMsgFolder = MailUtils.getOrCreateFolder(lastFolderURI);
     var bundle = document.getElementById("bundle_messenger");
     var stringName = isMove ? "moveToFolderAgain" : "copyToFolderAgain";
     aMenuItem.label = bundle.getFormattedString(stringName,
                                                 [destMsgFolder.prettyName], 1);
     // This gives us moveToFolderAgainAccessKey and copyToFolderAgainAccessKey.
     aMenuItem.accesskey = bundle.getString(stringName + "AccessKey");
   }
 }
@@ -1066,17 +1066,17 @@ function populateHistoryMenu(menuPopup, 
   }
   // For populating the back menu, we want the most recently visited
   // messages first in the menu. So we go backward from curPos to 0.
   // For the forward menu, we want to go forward from curPos to the end.
   var relPos = 0;
   for (var i = curPos.value; (isBackMenu) ? i >= 0 : i < historyArray.length; i += ((isBackMenu) ? -2 : 2)) {
     navDebug("history[" + i + "] = " + historyArray[i] + "\n");
     navDebug("history[" + i + "] = " + historyArray[i + 1] + "\n");
-    folder = MailUtils.getFolderForURI(historyArray[i + 1]);
+    folder = MailUtils.getOrCreateFolder(historyArray[i + 1]);
     navDebug("folder URI = " + folder.URI + "pretty name " + folder.prettyName + "\n");
     var menuText = "";
 
     // If the message was not being displayed via the current folder, prepend
     //  the folder name.  We do not need to check underlying folders for
     //  virtual folders because 'folder' is the display folder, not the
     //  underlying one.
     if (folder != gFolderDisplay.displayedFolder)
@@ -1690,17 +1690,17 @@ BatchMessageMover.prototype = {
     this.continueBatch();
   },
 
   // continue processing of default archive operations
   continueBatch() {
       let batch = this._currentBatch;
       let srcFolder = batch.srcFolder;
       let archiveFolderURI = batch.archiveFolderURI;
-      let archiveFolder = MailUtils.getFolderForURI(archiveFolderURI, false);
+      let archiveFolder = MailUtils.getOrCreateFolder(archiveFolderURI);
       let dstFolder = archiveFolder;
 
       let moveArray = Cc["@mozilla.org/array;1"]
                         .createInstance(Ci.nsIMutableArray);
       // Don't move any items that the filter moves or deleted
       for (let item of batch.messages) {
         if (srcFolder.msgDatabase.ContainsKey(item.messageKey) &&
             !(srcFolder.getProcessingFlags(item.messageKey) &
@@ -1729,26 +1729,26 @@ BatchMessageMover.prototype = {
       if (!forceSingle && (archiveFolder.server instanceof
                            Ci.nsIImapIncomingServer))
         forceSingle = archiveFolder.server.isGMailServer;
       if (forceSingle)
          granularity = Ci.nsIMsgIncomingServer.singleArchiveFolder;
 
       if (granularity >= Ci.nsIMsgIdentity.perYearArchiveFolders) {
         archiveFolderURI += "/" + batch.yearFolderName;
-        dstFolder = MailUtils.getFolderForURI(archiveFolderURI, false);
+        dstFolder = MailUtils.getOrCreateFolder(archiveFolderURI);
         if (!dstFolder.parent) {
           dstFolder.createStorageIfMissing(this);
           if (isAsync)
             return; // continues with OnStopRunningUrl
         }
       }
       if (granularity >= Ci.nsIMsgIdentity.perMonthArchiveFolders) {
         archiveFolderURI += "/" + batch.monthFolderName;
-        dstFolder = MailUtils.getFolderForURI(archiveFolderURI, false);
+        dstFolder = MailUtils.getOrCreateFolder(archiveFolderURI);
         if (!dstFolder.parent) {
           dstFolder.createStorageIfMissing(this);
           if (isAsync)
             return; // continues with OnStopRunningUrl
         }
       }
 
       // Create the folder structure in Archives.
--- a/mail/base/content/messageWindow.js
+++ b/mail/base/content/messageWindow.js
@@ -1040,19 +1040,19 @@ var MessageWindowController = {
       case "cmd_moveToFolderAgain":
         loadedFolder = gFolderDisplay.displayedFolder;
         if (!loadedFolder || (Services.prefs.getBoolPref("mail.last_msg_movecopy_was_move") &&
             !loadedFolder.canDeleteMessages))
           return false;
         let targetURI = Services.prefs.getCharPref("mail.last_msg_movecopy_target_uri");
         if (!targetURI)
           return false;
-        let targetFolder = MailUtils.getFolderForURI(targetURI);
-        // If parent is null, folder doesn't exist.
-        return targetFolder && targetFolder.parent;
+        let targetFolder = MailUtils.getExistingFolder(targetURI);
+        // If null, folder doesn't exist.
+        return (targetFolder !== null);
       case "cmd_applyFilters":
       case "cmd_runJunkControls":
       case "cmd_deleteJunk":
         return false;
       case "cmd_chat":
         return true;
       default:
         return false;
@@ -1119,17 +1119,17 @@ var MessageWindowController = {
         break;
       case "cmd_newMsgFromTemplate":
         MsgNewMessageFromTemplate(null);
         break;
       case "cmd_editTemplateMsg":
         MsgEditTemplateMessage(null);
         break;
       case "cmd_moveToFolderAgain":
-        var folder = MailUtils.getFolderForURI(
+        var folder = MailUtils.getOrCreateFolder(
                        Services.prefs.getCharPref("mail.last_msg_movecopy_target_uri"));
         if (Services.prefs.getBoolPref("mail.last_msg_movecopy_was_move"))
           MsgMoveMessage(folder);
         else
           MsgCopyMessage(folder);
         break;
       case "cmd_createFilterFromPopup":
         break;// This does nothing because the createfilter is invoked from the popupnode oncommand.
@@ -1349,10 +1349,10 @@ function getMailToolbox() {
 function RestoreFocusAfterHdrButton() {
   // set focus to the message pane
   window.content.focus();
 }
 
 function SelectFolder(aFolderUri) {
   gFolderDisplay.clearSelection();
   gFolderDisplay.treeSelection.currentIndex = -1;
-  gFolderDisplay.show(MailUtils.getFolderForURI(aFolderUri));
+  gFolderDisplay.show(MailUtils.getExistingFolder(aFolderUri));
 }
--- a/mail/base/content/msgMail3PaneWindow.js
+++ b/mail/base/content/msgMail3PaneWindow.js
@@ -838,17 +838,17 @@ function loadStartFolder(initialUri) {
     let loadFolder = !atStartupRestoreTabs(!!initialUri);
 
     if (initialUri)
       loadFolder = true;
 
     // First get default account
     try {
         if (initialUri) {
-            startFolder = MailUtils.getFolderForURI(initialUri);
+            startFolder = MailUtils.getOrCreateFolder(initialUri);
         } else {
             let defaultAccount = accountManager.defaultAccount;
             if (!defaultAccount)
                 return;
 
             defaultServer = defaultAccount.incomingServer;
             var rootMsgFolder = defaultServer.rootMsgFolder;
 
@@ -1185,17 +1185,17 @@ function ThreadTreeOnClick(event) {
   }
 }
 
 function GetSelectedMsgFolders() {
   return gFolderTreeView.getSelectedFolders();
 }
 
 function SelectFolder(folderUri) {
-  gFolderTreeView.selectFolder(MailUtils.getFolderForURI(folderUri));
+  gFolderTreeView.selectFolder(MailUtils.getOrCreateFolder(folderUri));
 }
 
 function ReloadMessage() {
   if (!gFolderDisplay.selectedMessage)
     return;
 
   let view = gFolderDisplay.view.dbView;
   if (view)
--- a/mail/base/modules/MailUtils.jsm
+++ b/mail/base/modules/MailUtils.jsm
@@ -58,40 +58,39 @@ var MailUtils = {
     for (let folder of fixIterator(folders, Ci.nsIMsgFolder)) {
       if (folder.filePath.equals(aFile))
         return folder;
     }
     return null;
   },
 
   /**
-   * Get the nsIMsgFolder corresponding to this URI. This uses the RDF service
-   * to do the work.
+   * Get the nsIMsgFolder corresponding to this URI.
    *
-   * @param aFolderURI the URI to convert into a folder
-   * @param aCheckFolderAttributes whether to check that the folder either has
-   *                              a parent or isn't a server
-   * @returns the nsIMsgFolder corresponding to this URI, or null if
-   *          aCheckFolderAttributes is true and the folder doesn't have a
-   *          parent or is a server
+   * @param aFolderURI the URI of the target folder
+   * @returns {nsIMsgFolder} Folder corresponding to this URI, or null if
+   *          the folder doesn't already exist.
    */
-  getFolderForURI(aFolderURI, aCheckFolderAttributes) {
-    let folder = null;
-    let rdfService = Cc["@mozilla.org/rdf/rdf-service;1"]
-                       .getService(Ci.nsIRDFService);
-    folder = rdfService.GetResource(aFolderURI);
-    // This is going to QI the folder to an nsIMsgFolder as well
-    if (folder && folder instanceof Ci.nsIMsgFolder) {
-      if (aCheckFolderAttributes && !(folder.parent || folder.isServer))
-        return null;
-    } else {
-      return null;
-    }
+  getExistingFolder(aFolderURI) {
+    let fls = Cc["@mozilla.org/mail/folder-lookup;1"]
+                .getService(Ci.nsIFolderLookupService);
+    return fls.getFolderForURL(aFolderURI);
+  },
 
-    return folder;
+  /**
+   * Get the nsIMsgFolder corresponding to this URI, or create a detached
+   * folder if it doesn't already exist.
+   *
+   * @param aFolderURI the URI of the target folder
+   * @returns {nsIMsgFolder} Folder corresponding to this URI.
+   */
+  getOrCreateFolder(aFolderURI) {
+    let fls = Cc["@mozilla.org/mail/folder-lookup;1"]
+                .getService(Ci.nsIFolderLookupService);
+    return fls.getOrCreateFolderForURL(aFolderURI);
   },
 
   /**
    * Display this message header in a new tab, a new window or an existing
    * window, depending on the preference and whether a 3pane or standalone
    * window is already open. This function should be called when you'd like to
    * display a message to the user according to the pref set.
    *
--- a/mail/components/compose/content/MsgComposeCommands.js
+++ b/mail/components/compose/content/MsgComposeCommands.js
@@ -4108,17 +4108,17 @@ function ComposeCanClose() {
   }
 
   // Returns FALSE only if user cancels save action
   if (gContentChanged || gMsgCompose.bodyModified || gAutoSaveKickedIn) {
     // call window.focus, since we need to pop up a dialog
     // and therefore need to be visible (to prevent user confusion)
     window.focus();
     let draftFolderURI = gCurrentIdentity.draftFolder;
-    let draftFolderName = MailUtils.getFolderForURI(draftFolderURI).prettyName;
+    let draftFolderName = MailUtils.getOrCreateFolder(draftFolderURI).prettyName;
     let result = Services.prompt
                          .confirmEx(window,
                                     getComposeBundle().getString("saveDlogTitle"),
                                     getComposeBundle().getFormattedString("saveDlogMessages3", [draftFolderName]),
                                     (Services.prompt.BUTTON_TITLE_SAVE * Services.prompt.BUTTON_POS_0) +
                                     (Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1) +
                                     (Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_2),
                                     null,
@@ -4159,33 +4159,34 @@ function ComposeCanClose() {
 
   return true;
 }
 
 function RemoveDraft() {
   try {
     var draftUri = gMsgCompose.compFields.draftId;
     var msgKey = draftUri.substr(draftUri.indexOf("#") + 1);
-    var rdf = Cc["@mozilla.org/rdf/rdf-service;1"]
-                .getService(Ci.nsIRDFService);
-
-    var folder = rdf.GetResource(gMsgCompose.savedFolderURI)
-                    .QueryInterface(Ci.nsIMsgFolder);
+    let folder = MailUtils.getExistingFolder(gMsgCompose.savedFolderURI);
+    if (!folder)
+      return;
     try {
-      if (folder.flags & Ci.nsMsgFolderFlags.Drafts) {
+      if (folder.getFlag(Ci.nsMsgFolderFlags.Drafts)) {
         var msgs = Cc["@mozilla.org/array;1"].
             createInstance(Ci.nsIMutableArray);
         msgs.appendElement(folder.GetMessageHeader(msgKey));
         folder.deleteMessages(msgs, null, true, false, null, false);
       }
     } catch (ex) { // couldn't find header - perhaps an imap folder.
       var imapFolder = folder.QueryInterface(Ci.nsIMsgImapMailFolder);
-      var keyArray = [];
-      keyArray[0] = msgKey;
-      imapFolder.storeImapFlags(8, true, keyArray, 1, null);
+      if (imapFolder) {
+        let keyArray = [];
+        keyArray[0] = msgKey;
+        imapFolder.storeImapFlags(Ci.nsMsgFolderFlags.Expunged,
+                                  true, keyArray, 1, null);
+      }
     }
   } catch (ex) {}
 }
 
 function SetContentAndBodyAsUnmodified() {
   gMsgCompose.bodyModified = false;
   gContentChanged = false;
 }
@@ -5975,17 +5976,17 @@ var attachmentBucketDNDObserver = {
 function DisplaySaveFolderDlg(folderURI) {
   try {
     var showDialog = gCurrentIdentity.showSaveMsgDlg;
   } catch (e) {
     return;
   }
 
   if (showDialog) {
-    let msgfolder = MailUtils.getFolderForURI(folderURI, true);
+    let msgfolder = MailUtils.getExistingFolder(folderURI);
     if (!msgfolder)
       return;
     let checkbox = {value: 0};
     let bundle = getComposeBundle();
     let SaveDlgTitle = bundle.getString("SaveDialogTitle");
     let dlgMsg = bundle.getFormattedString("SaveDialogMsg",
                                            [msgfolder.name,
                                             msgfolder.server.prettyName]);
--- a/mail/test/mozmill/folder-display/test-message-commands.js
+++ b/mail/test/mozmill/folder-display/test-message-commands.js
@@ -371,18 +371,18 @@ function yearly_archive(keep_structure) 
   // Figure out where the messages should have gone.
   let archiveRoot = "mailbox://nobody@Local%20Folders/Archives";
   let firstArchiveUri = archiveRoot + "/" + firstMsgYear;
   let lastArchiveUri = archiveRoot + "/" + lastMsgYear;
   if (keep_structure) {
     firstArchiveUri += "/ArchiveSrc";
     lastArchiveUri += "/ArchiveSrc";
   }
-  let firstArchiveFolder =  MailUtils.getFolderForURI(firstArchiveUri);
-  let lastArchiveFolder = MailUtils.getFolderForURI(lastArchiveUri);
+  let firstArchiveFolder =  MailUtils.getOrCreateFolder(firstArchiveUri);
+  let lastArchiveFolder = MailUtils.getOrCreateFolder(lastArchiveUri);
   be_in_folder(firstArchiveFolder);
   assert_true(mc.dbView.getMsgHdrAt(0).messageId == firstMsgHdrMsgId,
               "Message should have been archived to " + firstArchiveUri + ", but it isn't present there");
   be_in_folder(lastArchiveFolder);
 
   assert_true(mc.dbView.getMsgHdrAt(0).messageId == lastMsgHdrMsgId,
               "Message should have been archived to " + lastArchiveUri + ", but it isn't present there");
 }
@@ -419,18 +419,18 @@ function monthly_archive(keep_structure)
   let firstArchiveUri = archiveRoot + "/" + firstMsgYear +
                         "/" + firstMonthFolderName;
   let lastArchiveUri = archiveRoot + "/" + lastMsgYear +
                         "/" + lastMonthFolderName;
   if (keep_structure) {
     firstArchiveUri += "/ArchiveSrc";
     lastArchiveUri += "/ArchiveSrc";
   }
-  let firstArchiveFolder =  MailUtils.getFolderForURI(firstArchiveUri);
-  let lastArchiveFolder = MailUtils.getFolderForURI(lastArchiveUri);
+  let firstArchiveFolder = MailUtils.getOrCreateFolder(firstArchiveUri);
+  let lastArchiveFolder = MailUtils.getOrCreateFolder(lastArchiveUri);
   be_in_folder(firstArchiveFolder);
   assert_true(mc.dbView.getMsgHdrAt(0).messageId == firstMsgHdrMsgId,
               "Message should have been archived to Local Folders/" +
               firstMsgYear + "/" + firstMonthFolderName + "/Archives, but it isn't present there");
   be_in_folder(lastArchiveFolder);
   assert_true(mc.dbView.getMsgHdrAt(0).messageId == lastMsgHdrMsgId,
               "Message should have been archived to Local Folders/" +
               lastMsgYear + "/" + lastMonthFolderName + "/Archives, but it isn't present there");
--- a/mail/test/mozmill/folder-pane/test-folder-pane.js
+++ b/mail/test/mozmill/folder-pane/test-folder-pane.js
@@ -10,16 +10,17 @@
 
 "use strict";
 
 var MODULE_NAME = 'test-folder-pane';
 
 var RELATIVE_ROOT = '../shared-modules';
 var MODULE_REQUIRES = ['folder-display-helpers'];
 
+ChromeUtils.import("resource:///modules/MailUtils.jsm");
 ChromeUtils.import("resource:///modules/MailServices.jsm");
 
 function setupModule(module) {
   collector.getModule('folder-display-helpers').installInto(module);
 }
 
 /**
  * Assert the Folder Pane is in All Folder mode by default.  Check that the
@@ -46,20 +47,17 @@ function test_all_folders_toggle_folder_
   let archives = 1;
   let folderPaneA = 1;
   // Create archives folder - this is ugly, but essentially the same as
   // what mailWindowOverlay.js does. We can't use the built-in helper
   // method to create the folder because we need the archive flag to get
   // set before the folder added notification is sent out, which means
   // creating the folder object via RDF, setting the flag, and then
   // creating the storage, which sends the notification.
-  let rdfService = Cc['@mozilla.org/rdf/rdf-service;1']
-                     .getService(Ci.nsIRDFService);
-  let folder = rdfService.GetResource(pop3Server.rootFolder.URI + "/Archives")
-                         .QueryInterface(Ci.nsIMsgFolder);
+  let folder = MailUtils.getOrCreateFolder(pop3Server.rootFolder.URI + "/Archives");
   folder.setFlag(Ci.nsMsgFolderFlags.Archive);
   folder.createStorageIfMissing(null);
   // After creating Archives, account should have expanded
   // so that we should have 5 rows visible
   assert_folder_tree_view_row_count(accounts + inbox + trash +
                                     archives);
   // close the tinderbox server.
   mc.folderTreeView.toggleOpenState(0)
--- a/mailnews/base/content/junkCommands.js
+++ b/mailnews/base/content/junkCommands.js
@@ -64,17 +64,17 @@ function determineActionsForJunkMsgs(aFo
     if (!spamFolderURI)
     {
       // XXX TODO
       // we should use nsIPromptService to inform the user of the problem,
       // e.g. when the junk folder was accidentally deleted.
       dump('determineActionsForJunkMsgs: no spam folder found, not moving.');
     }
     else
-      actions.junkTargetFolder = MailUtils.getFolderForURI(spamFolderURI);
+      actions.junkTargetFolder = MailUtils.getOrCreateFolder(spamFolderURI);
   }
 
   return actions;
 }
 
 /**
  * performActionsOnJunkMsgs
  *
--- a/mailnews/base/content/msgFolderPickerOverlay.js
+++ b/mailnews/base/content/msgFolderPickerOverlay.js
@@ -19,17 +19,17 @@ function MsgFolderPickerOnLoad(pickerID)
   }
 
   if (uri) {
     //dump("on loading, set titled button to " + uri + "\n");
 
     // verify that the value we are attempting to
     // pre-flight the menu with is valid for this
     // picker type
-    var msgfolder = MailUtils.getFolderForURI(uri, true);
+    var msgfolder = MailUtils.getExistingFolder(uri);
           if (!msgfolder) return;
     
     var verifyFunction = null;
 
     switch (pickerID) {
       case "msgNewFolderPicker":
         verifyFunction = msgfolder.canCreateSubfolders;
         break;
@@ -50,17 +50,17 @@ function MsgFolderPickerOnLoad(pickerID)
 function PickedMsgFolder(selection,pickerID)
 {
   var selectedUri = selection.getAttribute('id');
   SetFolderPicker(selectedUri,pickerID);
 }
 
 function SetFolderPickerElement(uri, picker)
 {
-  var msgfolder = MailUtils.getFolderForURI(uri, true);
+  var msgfolder = MailUtils.getExistingFolder(uri);
 
   if (!msgfolder)
     return;
 
   var selectedValue = null;
   var serverName;
 
   if (msgfolder.isServer)
--- a/mailnews/base/content/msgSynchronize.js
+++ b/mailnews/base/content/msgSynchronize.js
@@ -1,13 +1,14 @@
 /* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+ChromeUtils.import("resource:///modules/MailUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 var gSynchronizeTree = null;
 var gParentMsgWindow;
 var gMsgWindow;
 
 var gInitialFolderStates = {};
 
@@ -64,21 +65,18 @@ function OnSelect()
 
 function selectOkButton()
 {
     return true;
 }
 
 function selectCancelButton()
 {
-    var RDF = Cc["@mozilla.org/rdf/rdf-service;1"]
-                .getService(Ci.nsIRDFService);
     for (var resourceValue in gInitialFolderStates) {
-      var resource = RDF.GetResource(resourceValue);
-      var folder = resource.QueryInterface(Ci.nsIMsgFolder);
+      let folder = MailUtils.getExistingFolder(resourceValue);
       if (gInitialFolderStates[resourceValue])
         folder.setFlag(Ci.nsMsgFolderFlags.Offline);
       else
         folder.clearFlag(Ci.nsMsgFolderFlags.Offline);
     }
     return true;
 }
 
--- a/mailnews/base/content/subscribe.js
+++ b/mailnews/base/content/subscribe.js
@@ -27,17 +27,17 @@ function Stop()
   }
 }
 
 function SetServerTypeSpecificTextValues()
 {
   if (!gServerURI)
     return;
 
-  let serverType = MailUtils.getFolderForURI(gServerURI, true).server.type;
+  let serverType = MailUtils.getExistingFolder(gServerURI).server.type;
 
   // set the server specific ui elements
   let subscribeLabelString = gSubscribeBundle.getString("subscribeLabel-" + serverType);
   let currentListTab  = "currentListTab-" + serverType;
   let currentListTabLabel     = gSubscribeBundle.getString(currentListTab + ".label");
   let currentListTabAccesskey = gSubscribeBundle.getString(currentListTab + ".accesskey");
 
   document.getElementById("currentListTab").setAttribute("label", currentListTabLabel);
@@ -66,17 +66,17 @@ var MySubscribeListener = {
   }
 };
 
 function SetUpTree(forceToServer, getOnlyNew)
 {
   if (!gServerURI)
     return;
 
-  var server = MailUtils.getFolderForURI(gServerURI, true).server;
+  var server = MailUtils.getExistingFolder(gServerURI).server;
   try
   {
     CleanUpSearchView();
     gSubscribableServer = server.QueryInterface(Ci.nsISubscribableServer);
 
     // enable (or disable) the search related UI
     EnableSearchUI();
 
--- a/mailnews/base/content/virtualFolderListEdit.js
+++ b/mailnews/base/content/virtualFolderListEdit.js
@@ -1,23 +1,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+ChromeUtils.import("resource:///modules/MailUtils.jsm");
+
 var gSelectVirtual = {
   _treeElement: null,
   _selectedList: new Set(),
 
   load: function() {
-    let folderLookup = Cc["@mozilla.org/mail/folder-lookup;1"]
-                         .getService(Ci.nsIFolderLookupService);
     if (window.arguments[0].searchFolderURIs) {
       let srchFolderUriArray = window.arguments[0].searchFolderURIs.split('|');
       for (let uri of srchFolderUriArray) {
-        this._selectedList.add(folderLookup.getFolderForURL(uri));
+        this._selectedList.add(MailUtils.getOrCreateFolder(uri));
       }
     }
 
     // Now tweak the folder tree for our purposes here.
     let oldProps = ftvItem.prototype.getProperties;
     ftvItem.prototype.getProperties = function(aColumn) {
       if (!aColumn || aColumn.id != "selectedCol")
         return oldProps.call(this, aColumn);
--- a/mailnews/base/content/virtualFolderProperties.js
+++ b/mailnews/base/content/virtualFolderProperties.js
@@ -101,17 +101,17 @@ function setupSearchRows(aSearchTerms)
 function updateOnlineSearchState()
 {
   var enableCheckbox = false;
   var checkbox = document.getElementById('searchOnline');
   // only enable the checkbox for selection, for online servers
   var srchFolderUriArray = gSearchFolderURIs.split('|');
   if (srchFolderUriArray[0])
   {
-    var realFolder = MailUtils.getFolderForURI(srchFolderUriArray[0]);
+    var realFolder = MailUtils.getOrCreateFolder(srchFolderUriArray[0]);
     enableCheckbox =  realFolder.server.offlineSupportLevel; // anything greater than 0 is an online server like IMAP or news
   }
 
   if (enableCheckbox)
     checkbox.removeAttribute('disabled');
   else
   {
     checkbox.setAttribute('disabled', true);
@@ -181,17 +181,17 @@ function onOK()
     if (window.arguments[0].onOKCallback)
       window.arguments[0].onOKCallback(virtualFolderWrapper.virtualFolder.URI);
     return true;
   }
   var uri = gPickedFolder.URI;
   if (name && uri) // create a new virtual folder
   {
     // check to see if we already have a folder with the same name and alert the user if so...
-    var parentFolder = MailUtils.getFolderForURI(uri);
+    var parentFolder = MailUtils.getOrCreateFolder(uri);
 
     // sanity check the name based on the logic used by nsMsgBaseUtils.cpp. It can't start with a '.', it can't end with a '.', '~' or ' '.
     // it can't contain a ';' or '#'.
     if (/^\.|[\.\~ ]$|[\;\#]/.test(name))
     {
       Services.prompt.alert(window, null,
                             gMessengerBundle.getString("folderCreationFailed"));
       return false;
@@ -243,17 +243,17 @@ function updateFoldersCount()
   let folderCount = gSearchFolderURIs ? srchFolderUriArray.length : 0;
   let foldersList = document.getElementById("chosenFoldersCount");
   foldersList.textContent =
     PluralForm.get(folderCount, gMessengerBundle.getString("virtualFolderSourcesChosen"))
               .replace("#1", folderCount);
   if (folderCount > 0) {
     let folderNames = [];
     for (let folderURI of srchFolderUriArray) {
-      let folder = MailUtils.getFolderForURI(folderURI);
+      let folder = MailUtils.getOrCreateFolder(folderURI);
       let name = this.gMessengerBundle.getFormattedString("verboseFolderFormat",
         [folder.prettyName, folder.server.prettyName]);
       folderNames.push(name);
     }
     foldersList.setAttribute("tooltiptext", folderNames.join("\n"));
   } else {
     foldersList.removeAttribute("tooltiptext");
   }
--- a/mailnews/base/prefs/content/am-copies.js
+++ b/mailnews/base/prefs/content/am-copies.js
@@ -109,21 +109,21 @@ function SetFolderDisplay(pickerMode, di
     var selectAccountRadioElem = document.getElementById(selectAccountRadioId);
     var selectFolderRadioId = radioElemPrefix + "_selectFolder";
     var selectFolderRadioElem = document.getElementById(selectFolderRadioId);
     var accountPicker = document.getElementById(accountPickerId);
     var folderPicker = document.getElementById(folderPickerId);
     var rg = selectAccountRadioElem.radioGroup;
     var folderPickedElement = document.getElementById(folderPickedField);
     var uri = folderPickedElement.getAttribute("value");
-    // Get message folder from the given uri. Second argument (false) signifies
-    // that there is no need to check for the existence of special folders as
+    // Get message folder from the given uri.
+    // There is no need to check for the existence of special folders as
     // these folders are created on demand at runtime in case of imap accounts.
     // For POP3 accounts, special folders are created at the account creation time.
-    var msgFolder = MailUtils.getFolderForURI(uri, false);
+    var msgFolder = MailUtils.getOrCreateFolder(uri);
     InitFolderDisplay(msgFolder.server.rootFolder, accountPicker);
     InitFolderDisplay(msgFolder, folderPicker);
 
     switch (pickerMode)
     {
         case "0" :
             rg.selectedItem = selectAccountRadioElem;
             SetPickerEnabling(accountPickerId, folderPickerId);
--- a/mailnews/base/prefs/content/am-junk.js
+++ b/mailnews/base/prefs/content/am-junk.js
@@ -33,27 +33,27 @@ function onInit(aPageId, aServerId)
   let moveOnSpamValue = moveOnSpamCheckbox.checked;
 
   // Check if there are any invalid junk targets and fix them.
   [ spamActionTargetAccount, spamActionTargetFolder, moveOnSpamValue ] =
     sanitizeJunkTargets(spamActionTargetAccount,
                         spamActionTargetFolder,
                         deferredToURI || aServerId,
                         document.getElementById("server.moveTargetMode").value,
-                        MailUtils.getFolderForURI(aServerId, false).server.spamSettings,
+                        MailUtils.getOrCreateFolder(aServerId).server.spamSettings,
                         moveOnSpamValue);
 
   spamActionTargetAccountElement.value = spamActionTargetAccount;
   spamActionTargetFolderElement.value = spamActionTargetFolder;
   moveOnSpamCheckbox.checked = moveOnSpamValue;
 
-  let server = MailUtils.getFolderForURI(spamActionTargetAccount, false);
+  let server = MailUtils.getOrCreateFolder(spamActionTargetAccount);
   document.getElementById("actionAccountPopup").selectFolder(server);
 
-  let folder = MailUtils.getFolderForURI(spamActionTargetFolder, true);
+  let folder = MailUtils.getExistingFolder(spamActionTargetFolder);
   document.getElementById("actionFolderPopup").selectFolder(folder);
 
   var currentArray = [];
   if (document.getElementById("server.useWhiteList").checked)
     currentArray = document.getElementById("server.whiteListAbURI").value.split(" ");
 
   // set up the whitelist UI
   var wList = document.getElementById("whiteListAbURI");
--- a/mailnews/base/prefs/content/am-server.js
+++ b/mailnews/base/prefs/content/am-server.js
@@ -373,27 +373,27 @@ function setupImapDeleteUI(aServerId)
   selectImapDeleteModel(deleteModel);
 
   // read trash folder path preference
   var trashFolderName = getTrashFolderName();
 
   // set folderPicker menulist
   var trashPopup = document.getElementById("msgTrashFolderPopup");
   trashPopup._teardown();
-  trashPopup._parentFolder = MailUtils.getFolderForURI(aServerId);
+  trashPopup._parentFolder = MailUtils.getOrCreateFolder(aServerId);
   trashPopup._ensureInitialized();
 
   // Convert the folder path in Unicode to MUTF-7.
   let manager = Cc['@mozilla.org/charset-converter-manager;1']
                   .getService(Ci.nsICharsetConverterManager);
   // Escape backslash and double-quote with another backslash before encoding.
   let trashMutf7 = manager.unicodeToMutf7(trashFolderName.replace(/([\\"])/g, '\\$1'));
   // TODO: There is something wrong here, selectFolder() fails even if the
   // folder does exist. Try to fix in bug 802609.
-  let trashFolder = MailUtils.getFolderForURI(aServerId + "/" + trashMutf7, false);
+  let trashFolder = MailUtils.getOrCreateFolder(aServerId + "/" + trashMutf7);
   try {
     trashPopup.selectFolder(trashFolder);
   } catch(ex) {
     trashPopup.parentNode.setAttribute("label", trashFolder.prettyName);
   }
   trashPopup.parentNode.folder = trashFolder;
 }
 
--- a/mailnews/base/prefs/content/amUtils.js
+++ b/mailnews/base/prefs/content/amUtils.js
@@ -67,18 +67,22 @@ function prettyFolderName(aTargetFolder)
  * @param aIsServer   true if the URI specifies only a server (without folder)
  *
  * @return  the value of aTargetURI if it is valid (usable), otherwise null
  */
 function checkJunkTargetFolder(aTargetURI, aIsServer)
 {
   try {
     // Does the target account exist?
-    let targetServer = MailUtils.getFolderForURI(aTargetURI + (aIsServer ? "/Junk" : ""),
-                                                 !aIsServer).server;
+    let targetServer;
+    if (aIsServer) {
+      targetServer = MailUtils.getOrCreateFolder(aTargetURI + "/Junk").server;
+    } else {
+      targetServer = MailUtils.getExistingFolder(aTargetURI).server;
+    }
 
     // If the target server has deferred storage, Junk can't be stored into it.
     if (targetServer.rootFolder != targetServer.rootMsgFolder)
       return null;
   } catch (e) {
     return null;
   }
 
@@ -94,17 +98,17 @@ function checkJunkTargetFolder(aTargetUR
  *
  * @return  the server/folder URI of a usable target for storing Junk
  */
 function chooseJunkTargetFolder(aTargetURI, aIsServer)
 {
   let server = null;
 
   if (aTargetURI) {
-    server = MailUtils.getFolderForURI(aTargetURI, false).server;
+    server = MailUtils.getOrCreateFolder(aTargetURI).server;
     if (!server.canCreateFoldersOnServer || !server.canSearchMessages ||
         (server.rootFolder != server.rootMsgFolder))
       server = null;
   }
   if (!server)
     server = MailServices.accounts.localFoldersServer;
 
   return server.serverURI + (!aIsServer ? "/Junk" : "");
--- a/mailnews/base/public/nsIFolderLookupService.idl
+++ b/mailnews/base/public/nsIFolderLookupService.idl
@@ -23,13 +23,24 @@ interface nsIMsgFolder;
 interface nsIFolderLookupService : nsISupports
 {
   /**
    * Returns a folder with the given URL or null if no such folder exists.
    *
    * @param aUrl The folder URL
    */
   nsIMsgFolder getFolderForURL(in ACString aUrl);
+
+  /**
+   * Returns a folder with the given URL.
+   * Will happily create and return an invalid (unparented) folder.
+   * Will return null if aUrl is not a folder url.
+   * NOTE: don't use this for new code! It's here purely to help
+   * transition away from RDF-based folder creation.
+   *
+   * @param aUrl The folder URL
+   */
+  nsIMsgFolder getOrCreateFolderForURL(in ACString aUrl);
 };
 
 %{C++
 #define NSIFLS_CONTRACTID "@mozilla.org/mail/folder-lookup;1"
 %}
--- a/mailnews/base/search/content/FilterEditor.js
+++ b/mailnews/base/search/content/FilterEditor.js
@@ -701,23 +701,23 @@ function AssignMeaningfulName()
 
 
 function GetFirstSelectedMsgFolder()
 {
   var selectedFolder = gActionTargetElement.getAttribute("uri");
   if (!selectedFolder)
     return null;
 
-  var msgFolder = MailUtils.getFolderForURI(selectedFolder, true);
+  var msgFolder = MailUtils.getExistingFolder(selectedFolder);
   return msgFolder;
 }
 
 function SearchNewFolderOkCallback(name, uri)
 {
-  var msgFolder = MailUtils.getFolderForURI(uri, true);
+  var msgFolder = MailUtils.getExistingFolder(uri);
   var imapFolder = null;
   try
   {
     imapFolder = msgFolder.QueryInterface(Ci.nsIMsgImapMailFolder);
   }
   catch(ex) {}
   if (imapFolder) //imapFolder creation is asynchronous.
   {
@@ -740,17 +740,17 @@ function SearchNewFolderOkCallback(name,
   if (imapFolder)
     SetBusyCursor(window, true);
 
   msgFolder.createSubfolder(name, msgWindow);
 
   if (!imapFolder)
   {
     var curFolder = uri+"/"+encodeURIComponent(name);
-    let folder = MailUtils.getFolderForURI(curFolder);
+    let folder = MailUtils.getOrCreateFolder(curFolder);
     gActionTargetElement.selectFolder(folder);
   }
 }
 
 function UpdateAfterCustomHeaderChange()
 {
   updateSearchAttributes();
 }
--- a/mailnews/base/search/content/searchWidgets.js
+++ b/mailnews/base/search/content/searchWidgets.js
@@ -126,17 +126,17 @@ class MozRuleactiontargetFolder extends 
 
     this.menulist = this.querySelector("menulist");
 
     this.menulist.addEventListener("command", (event) => {
       this.setPicker(event);
     });
 
     let folder = this.menulist.value ?
-      MailUtils.getFolderForURI(this.menulist.value) :
+      MailUtils.getOrCreateFolder(this.menulist.value) :
       gFilterList.folder;
 
     // An account folder is not a move/copy target; show "Choose Folder".
     folder = folder.isServer ? null : folder;
 
     // The menupopup constructor needs to finish first.
     this.menulist.menupopup.selectFolder(folder);
 
--- a/mailnews/base/search/content/searchWidgets.xml
+++ b/mailnews/base/search/content/searchWidgets.xml
@@ -19,17 +19,16 @@
 %filterEditorDTD;
   <!ENTITY % messengerDTD    SYSTEM "chrome://messenger/locale/messenger.dtd" >
 %messengerDTD;
 ]>
 
 <bindings   id="filterBindings"
             xmlns="http://www.mozilla.org/xbl"
             xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-            xmlns:nc="http://home.netscape.com/NC-rdf#"
             xmlns:xbl="http://www.mozilla.org/xbl">
 
   <binding id="ruleactiontype-menulist">
     <content>
       <xul:menulist class="ruleaction-type">
           <xul:menupopup>
             <xul:menuitem label="&moveMessage.label;" value="movemessage" enablefornews="false"/>
             <xul:menuitem label="&copyMessage.label;" value="copymessage"/>
@@ -216,45 +215,38 @@
         - @return  True if at least one template was found, otherwise false.
       -->
       <method name="getTemplates">
         <parameter name="populateTemplateList"/>
         <parameter name="templateMenuList"/>
         <body>
           <![CDATA[
             ChromeUtils.import("resource:///modules/iteratorUtils.jsm", this);
+            ChromeUtils.import("resource:///modules/MailUtils.jsm", this);
             let identitiesRaw = MailServices.accounts
               .getIdentitiesForServer(gFilterList.folder.server);
             let identities = Array.from(this.fixIterator(identitiesRaw,
                                                          Ci.nsIMsgIdentity));
 
             if (identities.length == 0) { // typically if this is Local Folders
               if (MailServices.accounts.defaultAccount)
                 identities.push(MailServices.accounts.defaultAccount.defaultIdentity);
             }
 
             let templateFound = false;
             let foldersScanned = [];
 
             for (let identity of identities) {
               let enumerator = null;
-              let msgFolder;
-              try {
-                msgFolder = Cc["@mozilla.org/rdf/rdf-service;1"]
-                              .getService(Ci.nsIRDFService)
-                              .GetResource(identity.stationeryFolder)
-                              .QueryInterface(Ci.nsIMsgFolder);
-                // If we already processed this folder, do not set enumerator
-                // so that we skip this identity.
-                if (foldersScanned.indexOf(msgFolder) == -1) {
-                  foldersScanned.push(msgFolder);
-                  enumerator = msgFolder.msgDatabase.EnumerateMessages();
-                }
-              } catch (e) {
-                // The Templates folder may not exist, that is OK.
+              let msgFolder = this.MailUtils.getExistingFolder(identity.stationeryFolder);
+              // If we already processed this folder, do not set enumerator
+              // so that we skip this identity.
+              if (msgFolder && !foldersScanned.includes(msgFolder)) {
+                foldersScanned.push(msgFolder);
+                enumerator = msgFolder.msgDatabase.EnumerateMessages();
               }
 
               if (!enumerator)
                 continue;
 
               while (enumerator.hasMoreElements()) {
                 let header = enumerator.getNext();
                 if (header instanceof Ci.nsIMsgDBHdr) {
@@ -457,17 +449,17 @@
                                     actionTarget.ruleactiontargetElement.childNodes[0].value;
             var errorString, customError;
 
             switch (filterActionString)
             {
               case "movemessage":
               case "copymessage":
                 let msgFolder = actionTargetLabel ?
-                  this.MailUtils.getFolderForURI(actionTargetLabel) : null;
+                  this.MailUtils.getOrCreateFolder(actionTargetLabel) : null;
                 if (!msgFolder || !msgFolder.canFileMessages)
                   errorString = "mustSelectFolder";
                 break;
               case "forwardmessage":
                 if (actionTargetLabel.length < 3 ||
                     actionTargetLabel.indexOf('@') < 1)
                   errorString = "enterValidEmailAddress";
                 break;
--- a/mailnews/base/src/folderLookupService.js
+++ b/mailnews/base/src/folderLookupService.js
@@ -44,16 +44,17 @@ folderLookupService.prototype = {
   // nsIFolderLookupService impl
   getFolderForURL: function (aUrl) {
     let folder = null;
     // First, see if the folder is in our cache.
     if (this._map.has(aUrl)) {
       let valid = false;
       try {
         folder = this._map.get(aUrl).QueryReferent(Ci.nsIMsgFolder);
+        // We don't want to return "dangling" (parentless) folders.
         valid = isValidFolder(folder);
       } catch (e) {
         // The object was deleted, so it's not valid
       }
 
       if (valid)
         return folder;
 
@@ -75,21 +76,37 @@ folderLookupService.prototype = {
         folder = rdf.GetResource(aUrl)
                     .QueryInterface(Ci.nsIMsgFolder);
       } catch (e) {
         // If the QI fails, then we somehow picked up an RDF resource that isn't
         // a folder. Return null in this case.
         return null;
       }
     }
+    // We don't want to return "dangling" (parentless) folders.
     if (!isValidFolder(folder))
       return null;
 
     // Add the new folder to our map. Store a weak reference instead, so that
     // the folder can be closed when necessary.
     let weakRef = folder.QueryInterface(Ci.nsISupportsWeakReference)
                         .GetWeakReference();
     this._map.set(aUrl, weakRef);
     return folder;
   },
+  getOrCreateFolderForURL: function (aUrl) {
+    // NOTE: this doesn't update _map, but it'll work fine and
+    // it's a transitional function we want deleted anyway.
+    let rdf = Cc["@mozilla.org/rdf/rdf-service;1"]
+                .getService(Ci.nsIRDFService);
+    try {
+      let folder = rdf.GetResource(aUrl)
+                      .QueryInterface(Ci.nsIMsgFolder);
+      return folder;
+    } catch (e) {
+      // If the QI fails, then we somehow picked up an RDF resource that isn't
+      // a folder. Return null in this case.
+      return null;
+    }
+  }
 };
 
 var NSGetFactory = XPCOMUtils.generateNSGetFactory([folderLookupService]);
--- a/mailnews/base/src/nsMailNewsCommandLineHandler.js
+++ b/mailnews/base/src/nsMailNewsCommandLineHandler.js
@@ -51,17 +51,17 @@ var nsMailNewsCommandLineHandler =
           // Convert the message URI into a folder URI
           let folderURI = mailURL.slice(0, messageIDIndex)
                                  .replace("-message", "");
           // Get the message ID
           let messageID = mailURL.slice(messageIDIndex + MESSAGE_ID_PARAM.length);
           // Make sure the folder tree is initialized
           MailUtils.discoverFolders();
 
-          let folder = MailUtils.getFolderForURI(folderURI, true);
+          let folder = MailUtils.getExistingFolder(folderURI);
           // The folder might not exist, so guard against that
           if (folder && messageID.length > 0)
             msgHdr = folder.msgDatabase.getMsgHdrForMessageID(messageID);
         }
         else {
           // message URI
           msgHdr = this._messenger.msgHdrFromURI(mailURL);
         }
--- a/mailnews/base/src/virtualFolderWrapper.js
+++ b/mailnews/base/src/virtualFolderWrapper.js
@@ -4,16 +4,17 @@
 
 /*
  * Wrap everything about virtual folders.
  */
 
 this.EXPORTED_SYMBOLS = ['VirtualFolderHelper'];
 
 ChromeUtils.import("resource:///modules/MailServices.jsm");
+ChromeUtils.import("resource:///modules/MailUtils.jsm");
 ChromeUtils.import("resource:///modules/iteratorUtils.jsm");
 
 var VirtualFolderHelper = {
   /**
    * Create a new virtual folder (an actual nsIMsgFolder that did not previously
    *  exist), wrapping it in a VirtualFolderWrapper, and returning that wrapper.
    *
    * If the call to addSubfolder fails (and therefore throws), we will NOT catch
@@ -106,25 +107,22 @@ function VirtualFolderWrapper(aVirtualFo
   this.virtualFolder = aVirtualFolder;
 };
 VirtualFolderWrapper.prototype = {
   /**
    * @return the list of nsIMsgFolders that this virtual folder is a
    *     search over.
    */
   get searchFolders() {
-    let rdfService = Cc['@mozilla.org/rdf/rdf-service;1']
-                       .getService(Ci.nsIRDFService);
     let virtualFolderUris =
       this.dbFolderInfo.getCharProperty("searchFolderUri").split("|");
     let folders = [];
     for (let folderURI of virtualFolderUris) {
       if (folderURI)
-        folders.push(rdfService.GetResource(folderURI)
-                               .QueryInterface(Ci.nsIMsgFolder));
+        folders.push(MailUtils.getExistingFolder(folderURI));
     }
     return folders;
   },
   /**
    * Set the search folders that back this virtual folder.
    *
    * @param aFolders Either a "|"-delimited string of folder URIs or a list of
    *     nsIMsgFolders that fixIterator can traverse (JS array/nsIMutableArray).
--- a/mailnews/db/gloda/modules/datamodel.js
+++ b/mailnews/db/gloda/modules/datamodel.js
@@ -1,16 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 this.EXPORTED_SYMBOLS = ["GlodaAttributeDBDef", "GlodaAccount",
                     "GlodaConversation", "GlodaFolder", "GlodaMessage",
                     "GlodaContact", "GlodaIdentity", "GlodaAttachment"];
 
+ChromeUtils.import("resource:///modules/MailUtils.jsm");
 ChromeUtils.import("resource:///modules/MailServices.jsm");
 
 ChromeUtils.import("resource:///modules/gloda/log4moz.js");
 var LOG = Log4Moz.repository.getLogger("gloda.datamodel");
 
 ChromeUtils.import("resource:///modules/gloda/utils.js");
 
 // Make it lazy.
@@ -389,20 +390,17 @@ GlodaFolder.prototype = {
    *
    * @param aActivity One of the kActivity* constants.  If you pass
    *     kActivityIndexing, we will set indexing for you, but you will need to
    *     clear it when you are done.
    * @return The nsIMsgFolder if available, null on failure.
    */
   getXPCOMFolder: function gloda_folder_getXPCOMFolder(aActivity) {
     if (!this._xpcomFolder) {
-      let rdfService = Cc['@mozilla.org/rdf/rdf-service;1']
-                         .getService(Ci.nsIRDFService);
-      this._xpcomFolder = rdfService.GetResource(this.uri)
-                                    .QueryInterface(Ci.nsIMsgFolder);
+      this._xpcomFolder = MailUtils.getExistingFolder(this.uri);
     }
     switch (aActivity) {
       case this.kActivityIndexing:
         // mark us as indexing, but don't bother with live tracking.  we do
         //  that independently and only for header retrieval.
         this.indexing = true;
         break;
       case this.kActivityHeaderRetrieval:
--- a/mailnews/db/gloda/modules/index_msg.js
+++ b/mailnews/db/gloda/modules/index_msg.js
@@ -2604,17 +2604,17 @@ var GlodaMsgIndexer = {
      *  all of which is actually done by the datastore for us.
      * This method needs to deal with the complexity where local folders will
      *  generate a rename notification for each sub-folder, but IMAP folders
      *  will generate only a single notification.  Our logic primarily handles
      *  this by not exploding if the original folder no longer exists.
      */
     _folderRenameHelper: function gloda_indexer_folderRenameHelper(aOrigFolder,
                                                                    aNewURI) {
-      let newFolder = MailUtils.getFolderForURI(aNewURI);
+      let newFolder = MailUtils.getOrCreateFolder(aNewURI);
       let specialFolderFlags = Ci.nsMsgFolderFlags.Trash | Ci.nsMsgFolderFlags.Junk;
       if (newFolder.isSpecialFolder(specialFolderFlags, true)) {
         let descendentFolders = newFolder.descendants;
 
         // First thing to do: make sure we don't index the resulting folder and
         //  its descendents.
         GlodaMsgIndexer.resetFolderIndexingPriority(newFolder);
         for (let folder of fixIterator(descendentFolders, Ci.nsIMsgFolder)) {
--- a/mailnews/db/gloda/test/unit/base_index_messages.js
+++ b/mailnews/db/gloda/test/unit/base_index_messages.js
@@ -1014,17 +1014,17 @@ function* test_folder_nuking_message_del
   queryExpect(msgPrivQuery, []);
   yield false; // queryExpect is async
 }
 
 /* ===== Folder Move/Rename/Copy (Single and Nested) ===== */
 
 function get_nsIMsgFolder(aFolder) {
   if (!(aFolder instanceof Ci.nsIMsgFolder))
-    return MailUtils.getFolderForURI(aFolder);
+    return MailUtils.getOrCreateFolder(aFolder);
   else
     return aFolder;
 }
 
 function get_testFolder(aFolder) {
   if ((typeof aFolder) != "string")
     return aFolder.URI;
   else
--- a/mailnews/extensions/newsblog/content/FeedUtils.jsm
+++ b/mailnews/extensions/newsblog/content/FeedUtils.jsm
@@ -1716,17 +1716,17 @@ var FeedUtils = {
    * Return a folder path string constructed from individual folder UTF8 names
    * stored as properties (not possible hashes used to construct disk foldername).
    *
    * @param {nsIMsgFolder} aFolder         - The folder.
    *
    * @returns {String} prettyName or null  - Name or null if not a disk folder.
    */
   getFolderPrettyPath(aFolder) {
-    let msgFolder = MailUtils.getFolderForURI(aFolder.URI, true);
+    let msgFolder = MailUtils.getExistingFolder(aFolder.URI);
     if (!msgFolder) {
       // Not a real folder uri.
       return null;
     }
 
     if (msgFolder.URI == msgFolder.server.serverURI) {
       return msgFolder.server.prettyName;
     }
@@ -1734,17 +1734,17 @@ var FeedUtils = {
     // Server part first.
     let pathParts = [msgFolder.server.prettyName];
     let rawPathParts = msgFolder.URI.split(msgFolder.server.serverURI + "/");
     let folderURI = msgFolder.server.serverURI;
     rawPathParts = rawPathParts[1].split("/");
     for (let i = 0; i < rawPathParts.length - 1; i++) {
       // Two or more folders deep parts here.
       folderURI += "/" + rawPathParts[i];
-      msgFolder = MailUtils.getFolderForURI(folderURI, true);
+      msgFolder = MailUtils.getExistingFolder(folderURI);
       pathParts.push(msgFolder.name);
     }
 
     // Leaf folder last.
     pathParts.push(aFolder.name);
     return pathParts.join("/");
   },
 
--- a/mailnews/imap/test/unit/test_saveTemplate.js
+++ b/mailnews/imap/test/unit/test_saveTemplate.js
@@ -69,17 +69,17 @@ saveAsUrlListener.prototype = {
 // This is similar to the method in mailCommands.js, to test the way that
 // it creates a new templates folder before saving the message as a template.
 function* saveAsTemplate() {
   let hdr = mailTestUtils.firstMsgHdr(IMAPPump.inbox);
   let uri = IMAPPump.inbox.getUriForMsg(hdr);
   let identity = MailServices.accounts
                   .getFirstIdentityForServer(IMAPPump.incomingServer);
   identity.stationeryFolder = IMAPPump.incomingServer.rootFolder.URI + "/Templates";
-  let templates = MailUtils.getFolderForURI(identity.stationeryFolder, false);
+  let templates = MailUtils.getOrCreateFolder(identity.stationeryFolder);
   // Verify that Templates folder doesn't exist, and then create it.
   Assert.equal(templates.parent, null);
   templates.setFlag(Ci.nsMsgFolderFlags.Templates);
   templates.createStorageIfMissing(new saveAsUrlListener(uri, identity));
   yield false;
 }
 
 // listener for saveAsTemplate adding a message to the templates folder.
--- a/mailnews/jsaccount/test/unit/test_jaMsgFolder.js
+++ b/mailnews/jsaccount/test/unit/test_jaMsgFolder.js
@@ -4,29 +4,29 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // This tests the additional methods added to JaMsgFolder.cpp that are not
 // in nsMsgDBFolder.cpp  Although this code have been done creating the
 // delegator class directly, instead we use a JS component as a demo of
 // JS override classes.
 
 ChromeUtils.import("resource://testing-common/mailnews/testJaBaseMsgFolder.jsm");
+ChromeUtils.import("resource:///modules/MailUtils.jsm");
 ChromeUtils.import("resource:///modules/MailServices.jsm");
 
 var interfaces = JaBaseMsgFolderProperties.baseInterfaces;
 
 function run_test()
 {
   let server = MailServices.accounts.createIncomingServer("foouser", "foohost", "testja");
   Assert.ok(server instanceof Ci.msgIOverride);
 
   // If you create a folder object directly, it will complain about not being registered.
-  // Use RDF instead.
-  let rdfService = Cc['@mozilla.org/rdf/rdf-service;1'].getService(Ci.nsIRDFService);
-  let testJaMsgFolder = rdfService.GetResource("testja://foouser@foohost/somefolder");
+  // Use folder-lookup-service instead.
+  let testJaMsgFolder = MailUtils.getOrCreateFolder("testja://foouser@foohost/somefolder");
   //let testJaMsgFolder = Cc[JaBaseMsgFolderProperties.contractID]
   //                        .createInstance(Ci.msgIOverride);
   Assert.ok(testJaMsgFolder instanceof Ci.nsIMsgFolder);
 
   JaBaseMsgFolderProperties.baseInterfaces.forEach(iface => {
     dump('testing interface ' + iface + '(' + Ci[iface] + ')\n');
     testJaMsgFolder.QueryInterface(Ci[iface]);
   });