Mail and RDF

Mozilla Mail exposes many of it's data structures to RDF through a few datasources. This allows exposure of mailnews-specific data to user interface using RDF Templates.

You should learn about RDF before reading this document or you will be hopelessly confused.

Overview of Mail RDF graph

The root resource for all accounts, folders and messages is the RDF Resource named msgaccounts:/. From this resource, you can follow a number of arcs to find servers, folders, and finally messages. Eventually we'll probably hang mail filters, annotations, etc, off of nodes in the graph. Here is an example of how this might be set up:

In this tree-style representation of an RDF graph, I've made arcs italic and resources bold.

msgaccounts:/
    +-- http://home.netscape.com/NC-rdf#child -->
    |     imap://alecf@imap.mywork.com
    |   +-- http://home.netscape.com/NC-rdf#IsServer --> "true"
    |   +-- http://home.netscape.com/NC-rdf#child -->
    |         imap://alecf@imap.mywork.com/INBOX
    |       +-- http://home.netscape.com/NC-rdf#TotalMessages --> "4"
    |       +-- http://home.netscape.com/NC-rdf#IsServer --> "false"
    |       +-- http://home.netscape.com/NC-rdf#MessageChild -->
    |       |     imap_message://alecf@imap.mywork.com/INBOX#1
    |       +-- http://home.netscape.com/NC-rdf#MessageChild -->
    |       |     imap_message://alecf@imap.mywork.com/INBOX#2
    |       +-- http://home.netscape.com/NC-rdf#MessageChild -->
    |       |     imap_message://alecf@imap.mywork.com/INBOX#3
    |       +-- http://home.netscape.com/NC-rdf#MessageChild -->
    |             imap_message://alecf@imap.mywork.com/INBOX#4
    |       etc...          
    +-- http://home.netscape.com/NC-rdf#child -->
    |     mailbox://alecf@pop.mywork.com
    |   +-- http://home.netscape.com/NC-rdf#IsServer --> "true"
    |   +-- http://home.netscape.com/NC-rdf#child -->
    |         mailbox://alecf@pop.mywork.com/INBOX
    |       +-- http://home.netscape.com/NC-rdf#TotalMessages --> "2"
    |       +-- http://home.netscape.com/NC-rdf#IsServer --> "false"
    |       +-- http://home.netscape.com/NC-rdf#MessageChild -->
    |       |     mailbox_message://alecf@pop.mywork.com/INBOX#1
    |       +-- http://home.netscape.com/NC-rdf#MessageChild -->
    |             mailbox_message://alecf@pop.mywork.com/INBOX#2
    |       etc...          
      

There are of course many more properties that are exposed via RDF, but this should give you a feel for it.

Datasources

We have a few primary datasources used in mail:

Datasources and the UI

Mail does not link the UI to datasources in a "traditional" manner.

Instead of having a singleton datasource that is shared across all UI components, we have per-view datasources. This allows each template-based widget to maintain view/window-specific data with each datasource. For example...??? (sorting? what else do we store?)

Datasources are created when each window's JavaScript is loaded by declaring the datasource variables in the source javascript as global variables. In the document's onload= handler the datasources are attached to their respective widgets by setting the database property on each RDF template's parent element.

Reflecting data to RDF

In order to have a dynamic UI that updates when the underlying content changes, a datasource must implement two key methods of reflecting data into RDF.

The details

Answering Queries

Mail uses RDF Resource Factories to attach mail-specific information to RDF resources. (The details of RDF Resource Factories will be left to RDF documentation for now.) From an RDF Resource, it is possible to QueryInterface() to the appropriate mail/news object, and then access information from there.

For example, the folder pane needs to display the number of messages in the INBOX. Information for this column is queried when the tree's RDF Template calls the folder datasource's GetTarget() method. The query's target is the resource named mailbox://alecf@pop.myisp.com/INBOX and the property node is named http://home.netscape.com/NC-rdf#TotalMessages. This is basically what happens, behind the scenes:

    var target = RDF.GetResource("mailbox://alecf@pop.myisp.com/INBOX");
    var property = RDF.GetResource("http://home.netscape.com/NC-rdf#TotalMessages");
    var resultNode = dataSource.GetTarget(target, property, true);
    

In the folder datasource's GetTarget(), target would be QueryInterfaced to a nsIMsgFolder. To get the total messages, the datasource would then call nsIMsgFolder.GetTotalMessages(). Finally, it would convert the result of this call to an RDF Literal, and pass it back through the return parameter of GetTarget().

An example of how this might work inside the datasource:

    var msgCountArc = RDF.GetResource("http://home.netscape.com/NC-rdf#TotalMessages");

    function GetTarget(target, property, unused) {
       var folder = target.QueryInterface(Components.interfaces.nsIMsgFolder);
       if (property == msgCountArc) {
           var msgCount = folder.GetTotalMessages(false);
           var result = RDF.GetLiteral(msgCount.toString());
           return result;
       }
    }
    

Asynchronously notifying RDF

When a mail object's data changes and the data is reflected in RDF by notifying all of the observers that RDF has registered with the datasource.

In the example of mail folders, each folder datasource first registers itself with the mail session as a nsIFolderListener because it wants information about when a folder changes. Each template registers itself as an RDF observer. When a folder's contents or properties change, it tells the mail session to notify the folder listeners that the data has changed. The folder datasource then translates these property changes into OnAssert() or OnUnassert() calls to the observers.

The calling chain essentially looks like this:

Registration:

  1. Folder datasource registers itself with the mail session as a folder listener
  2. RDF Template registers itself with the datasource as a content observer.
Notification:
  1. Folder data changes.
  2. Folder notifies mail session that it's data changed.
  3. Mail session notifies folder listeners that the folder has changed.
  4. Folder datasource notifies RDF Content observers of the changes.
  5. Content observers update UI.
An aside: Rational behind the design

After reviewing this design, it might seem unnecessary to have the double-levels of notification/registration. Why can't folders directly notify the RDF Content observers when things change?

Here is the rational behind this design:

Please comment!
Alec Flett
Last modified: Thu Oct 7 11:33:42 PDT 1999