Folders and Messages

Perform Operation on Folder

See the nsIMsgFolder interface or the nsIMsgDatabase interface, which probably provide the methods to accomplish many of the use cases below.

Determine Selected Folder

  • For determining a selected folder on the display:
    • gFolderDisplay.displayedFolder: The nsIMsgFolder being displayed.
  • If you're right-clicking on a folder and want to get the folder(s) actually selected:
      <meta content="text/html; charset=utf-8" http-equiv="content-type"/>gFolderTreeView.getSelectedFolders(): An array of nsIMsgFolders corresponding to the current selection of the tree.

Check for New Mail

Watch for New Mail

This question was originally answered in the news groups, see the original answer for more background.

Thunderbird2 gives us the nsiMsgFolderNotificationService interface which we pass a nsIMsgFolderListener interface to receive the events we want. (See also: Mail Event System.)

var newMailListener = {
    itemAdded: function(item) {   
	alert("Got mail.  Look at item's properties for more details.");
	var hdr = item.QueryInterface(Components.interfaces.nsIMsgDBHdr);

function init() {
    var notificationService =

This interface was changed in Thunderbird 3. The msgAdded flag should be passed to addListener to tell it to listen for new messages. The listener code should implement a msgAdded function instead of a itemAdded function, and there's no need to QI to an nsIMsgDBHdr any more.

var newMailListener = {
    msgAdded: function(aMsgHdr) {   
	alert("Got mail.  Look at aMsgHdr's properties for more details.");

function init() {
    var notificationService =
    notificationService.addListener(newMailListener, notificationService.msgAdded); 

List Subfolders

List Messages

Select Messages

  • gFolderDisplay.selectMessages(aListOfMessageHeaders): Select all the headers in the list you pass. It will attempt to scroll the view so the range is visible, but obviously, with huge selections it can only show part of it (and we choose the first part).
  • gFolderDisplay.selectMessage(aMessageHeader): Select just the one message.
  • gFolderDisplay.clearSelection(): Select no messages!

Inspect Selected Messages

  • gFolderDisplay.selectedCount: To get the number of things selected.
  • gFolderDisplay.selectedMessages: To get a list of the nsIMsgDBHdr corresponding to the selected messages. If a collapsed thread is in there and working with collapsed threads is enabled, this will include the headers for the messages in that collapsed thread.
  • gFolderDisplay.selectedMessageUris: To get a list of the URIs corresponding to the selected messages. Also includes messages in selected collapsed threads when so enabled.
  • gFolderDisplay.selectedMessage: To get the first selected header. Only use this when you know there is only one message happening or only care about one. In some cases I believe this is used as an (erroneous) proxy for knowing some attribute about all the messages in a selection.

Move / Copy (Selected) Messages

Messages are copied or moved using the message copy service via its CopyMessages method (for both moves and copies). If you simply want to move / copy the selected messages in the thread pane, however, it is easier to just ask the db view to do the work for you. In the code below whenever we talk about a folder it is an nsIMsgFolder instance.

Tell the thread pane to copy the currently selected messages into "destFolder":


Tell the thread pane to move the currently selected messages into "destFolder":


If the messages you are working with are not displayed / selected in the thread pane, you will need to do the following. Note that you need to issue a separate call to CopyMessages for each pairing of source folder and target folder. For example, if you have messages in folders "Foo" and "Bar" and want to move them into "Hat", you will need to issue 2 moves: one from "Foo" to "Hat", and one from "Bar" to "Hat".

let copyService = Cc[";1"]
copyService.CopyMessages(srcFolder, xpcomHdrArray, destFolder, isMove, /* copy listener */ null, msgWindow, /* allow undo */ false);

xpcomHdrArray needs to be an XPCOM nsIArray, not just a JavaScript array of headers. You can convert a JS array to an appropriate array as follows:

Components.utils.import("resource:///iteratorUtils.jsm"); // import toXPCOMArray
let xpcomHdrArray = toXPCOMArray(jsMsgHdrArray, Components.interfaces.nsIMutableArray);

If you would like to know when the move / copy completes, implement the nsIMsgCopyServiceListener interface and pass it instead of a null value.

If your code is not operating as part of the UI, msgWindow will not be available to you and you should pass null. In that case, you must pass false for allowUndo because the nsIMsgWindow provides the undo stack. Even if you are not operating as part of the UI, it is possible to create your own nsIMsgWindow instance to provide for an undo stack, but you will also need to surface a UI to perform the undo or it doesn't make a lot of sense.