The Mail event system

Mozilla mail requires an event system to notify different subsystems that data has changed. This document describes the system that events are passed amongst the mail objects.

For example, when a folder gets a new message, its total message count increases. The folder pane needs to know that this changed so that it can update the message count so the user can see it.


The key interfaces here are:


Each event type has a two methods associated with it:

Where <event> is the type of event, such as BoolPropertyChanged or the generic Event.

Sample control flow

Here is an example of a possible flow of control when a new message is added to a folder. In this example, there is a dialog open that shows properties for this folder including the message count. This dialog is a listener on this particular folder.

  1. A message is added to an nsImapMailFolder containing 4 messages.
  2. Because the number of messages in the folder have increased, this change in total message count needs to be broadcast to the world. The folder calls NotifyIntPropertyChanged on itself with the atom that represents "TotalMessages":
    this->NotifyIntPropertychanged(kTotalMessagesAtom, 4, 5);.
  3. NotifyPropertyChanged broadcasts this event to each its nsIFolderListeners by calling OnItemIntPropertyChanged on each listener:
    listener->OnIntPropertyChanged(this, kTotalMessagesAtom, 4, 5);
  4. NotifyPropertyChanged then broadcasts this event to the mail session:
    mailSession->OnIntPropertyChanged(this, kTotalMessagesAtom, 4, 5);
  5. The mail session rebroadcasts this information to each of the global listeners that has been registered with it. For each global listener, it calls OnIntPropertyChanged:
    listener->OnIntPropertyChanged(folder, kTotalMessagesAtom, 4, 5);


At the time this document is being written, these are the current events:

NotifyItemAdded OnItemAdded
NotifyItemRemoved OnItemRemoved
NotifyItemPropertyChanged OnItemPropertyChanged
NotifyItemIntPropertyChanged OnItemIntPropertyChanged
NotifyItemBoolPropertyChanged OnItemBoolPropertyChanged
NotifyItemUnicharPropertyChanged OnItemUnicharPropertyChanged
NotifyItemPropertyFlagChanged OnItemPropertyFlagChanged
NotifyItemEvent OnItemEvent
NotifyFolderLoaded OnFolderLoaded
NotifyDeleteOrMoveMessages OnDeleteOrMoveMessages

Sample code

In this example, a listener will be set up to be notified when the message count changes in a folder:

// our variable to know if the listener fired
var listenerHasFired = false;
var totalMessagesListenerHasFired = false;

// the listening function that will react to changes
function myOnIntPropertyChanged(item, property, oldValue, newValue) {

  var propertyString = property.GetUnicode();

  dump("OnIntPropertyChanged has fired with property + " +
    propertyString + "!\n");
  if (propertyString == "TotalMessages") {

     //now show us visually
     var folder = item.QueryInterface(Components.interfaces.nsIMsgFolder);
     dump("The folder " + folder.prettyName + " now has " +
       newValue + " messages.");
  } else if (propertyString == "TestProperty") {
     dump("Recieved integer test property fired on folder " +
       folder.prettyName + " with values " + oldValue + " and " +
       newValue + "\n");

// set up the folder listener to point to the above function
var folderListener = {
  OnItemAdded: function(parent, item, viewString) {},
  OnItemRemoved: function(parent, item, viewString) {},
  OnItemPropertyChanged: function(parent, item, viewString) {},
  OnItemIntPropertyChanged: myOnIntPropertyChanged,
  OnItemBoolPropertyChanged: function(item, property, oldValue, newValue) {},
  OnItemUnicharPropertyChanged: function(item, property, oldValue, newValue) {},
  OnItemPropertyFlagChanged: function(item, property, oldFlag, newFlag) {},
  OnItemEvent: function(item, event) = {},
  OnFolderLoaded: function(aFolder) = {}
  OnDeleteOrMoveMessagesCompleted: function( aFolder) = {},

// now register myself as a listener on every mail folder
var mailSession = Components.classes["component://netscape/messenger/services/session"].


// now test to see if integer stuff is firing at all
// let's say "folder" is a folder we know about

// first we need an atom to play with
atomService = Components.classes["component://netscape/atom-service"].

var testPropertyAtom = atomService.getAtom("TestProperty");

// now fire the test notification
folder.NotifyIntPropertyChanged(testPropertyAtom, 0, 100);

// Now we would do some operations to change the message count, such
// as copying a message into this folder or something. Then we could 
// verify that our listener fired by checking if listenerHasFired and
// totalMessagesListenerHasFired are true

// this is left as an exercise for the reader.

Future plans

The notification system has two duplicate methods which could be implemented with OnItemEvent/NotifyItemEvent: FolderLoaded, and DeleteOrMoveMessagesCompleted. Both of these could be done by making atoms for each of these events and just firing it with NotifyItemEvent.


There are some of redundant methods between the nsIMsgMailSession and the nsIFolderListener interfaces. nsIMsgMailSession also contains a number of other methods that are completely unrealted to folder notification. It would make sense to collapse the nsIMsgMailSession simply into an object that implements the the nsIFolderListener interface to receive notifications from the folders. The notification functions should probably go to an nsIFolderBroadcaster interface or something, since they need to know what folder is being modified.

Alec Flett
Last modified: Fri Mar 31 12:22:03 PST 2000