A bird's-eye view of the Mozilla framework

  • Author: Susan D. Tiner
  • Last Updated Date: 11/23/05

Statement of purpose

The purpose of this article is to provide a high-level technical overview of the architecture of the extensible, object-based Mozilla application framework. It examines what happens when the user performs a simple user interface (UI) action such as clicking a link in the Contents Panel of the Help Viewer window shown below.

The article focuses on the architecture of the overall framework supporting the Mozilla application suite, not the architecture of the individual applications themselves.

Image:Help.PNG

Prerequisites

This article assumes you have access to Mozilla sources and are familiar with the directory structure of the source tree. The code samples in the article are based on Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9a1) Gecko/20051104 SeaMonkey/1.1a from a new source tree checked out 11/04/05. The build ID is 2005110409. TheHelp Viewer files referenced in the article are located in

/seamonkey/extensions/help/

This article also assumes you are familiar with the JavaScript and C++ programming languages, object-oriented programming (OOP) terminology and design concepts, the Microsoft® Component Object Model (COM), and the CORBA OMG Interface Definition Language (IDL).

Organization

This article first covers some conceptual groundwork, then walks through a user scenario to illustrate how key Mozilla architectural components work together to support a simpleHelp Viewer UI action.

Framework Layers

At a high level, you can think of the Mozilla framework as consisting of a suite of applications, each of which is organized into three basic layers depicted in Figure 1.


Figure 1.

Image:Aol.png

User Interface (UI) Widgets and Services

The X Window System Toolkit defines the termwidget as a pre-defined object that encapsulates both the command actions and data associated with a graphical user interface (GUI) control. The various X Toolkit implementations provide a set of widgets for UI controls such as menus, command buttons, dialog boxes and scroll bars. The Mozilla XPToolkit module provides a similar set of facilities for building cross-platform UI controls implemented as XML User Interface Language (XUL) packages. A XUL package consists of

  • a XUL description of the UI widget
  • Cascading Style Sheets customizing appearance
  • JavaScript services implementing the UI behavior

A package (also known as chrome) is really just a bundling of a set of UI widgets and associated services implementing a particular application feature. Chrome and Extensions are examples of packages.

Content (DOM)

The Mozilla Document Object Model (DOM) specifies a logical tree structure for storing UI and document content, and provides an API (Application Programming Interface) for accessing and manipulating the content. An application such as the browser may have one or more open documents accessible via DOM APIs. When an HTML, XML, SVG or other type of document is loaded, the NGLayout engine (also known as Gecko) parses the contents into a DOM tree, and handles the layout and rendering of the document pages. The NGLayout engine also parses XUL UI controls into a DOM tree and handles rendering of the UI.

Core Services

Modules such as the NGLayout engine comprise the core application services available to

  • other core modules, and
  • XUL packages

Core application modules are implemented as a set of one or more XPCOM (Cross-Platform COM) objects.

XPCOM Object Model

Before getting into the details of the UI example, it's helpful to have a basic understanding of the XPCOM object model. Mozilla architectural modules are comprised of groups of related XPCOM objects that provide services to and access services from each other through dynamically queryable interfaces.

Object Model At a Glance

Mozilla dynamically loads and integrates core modules and XUL packages into the runtime environment on startup. Once loaded, a module or package has full access to the XPCOM objects of all the other modules in the runtime environment. The ability to dynamically integrate modules and packages at startup is a powerful benefit of the object model, which defines a module as a set of one or more XPCOM objects calledcomponents. These objects provide services to client packages and modules and access services provided by other modules through dynamically queryable interfaces. Figure 2 shows this structure.


Figure 2.

Image:Object.png

In the diagram above, each horizontal line extending from an object and terminated by an open circle indicates an interface to the object. The client object does not need to know anything about the internal structure or implementation of the provider object in order to take advantage of its services. The client simply queries the provider for a particular service, and if available, accesses that service through an interface defined in XPIDL (Cross-Platform IDL), derived from the CORBA IDL. The XPIDL interface description is independent of the programming language used to implement the object itself. For example, a JavaScript object or C++ class could both implement the same XPIDL interface.

Scripting languages such as JavaScript cannot directly access XPCOM components, but access them indirectly using XPConnect. XPConnect also provides a required layer for XPCOM components written in a scripting language.

Let’s consider a Resource Description Framework (RDF) example, where RDF is a framework for describing and interchangingmetadata, that is, information about information. In this example, we’re interested in the RDF sub-graph underlying theContents Panel widget within theHelp Viewer window. Suppose the client already has an nsIRDFDataSource object representing a sub-graph of an RDF graph and calls nsIRDFDataSource.GetTarget(resource, NC_LINK, true) to obtain an nsIRDFNode representing a specific node in the graph, in this case a link to aHelp Viewer document page. The client is completely unaware of which C++ class (or other language) actually implements nsIRDFNode; it only interacts with the IDL interface.

The client would like to ask the nsIRDFNode whether its supports nsIRDFLiteral. An nsIRDFNode must be an nsIRDFLiteral or an nsIRDFResource. The link is a literal, so the client wants nsIRDFLiteral. Since all XPCOM interfaces inherit the base interface nsISupports, the client can ask whether nsIRDFNode supports nsIRDFLiteral by calling nsIRDFNode.QueryInterface(), a method in the nsISupports interface.

[scriptable, uuid(00000000-0000-0000-c000-000000000046)]
  interface nsISupports {
  void QueryInterface(in nsIIDRef uuid,
            iid_is(uuid),retval] out nsQIResult result);
  [noscript, notxpcom] nsrefcnt AddRef();
  [noscript, notxpcom] nsrefcnt Release();
};

The uuid parameter to QueryInterface() is the IID uniqely identifying the interface.

If nsIRDFNode doesn't support nsIRDFLiteral, it returns null and it's up to the client to choose an alternate course of action.

The XPCOM component keeps track of all interface pointers currently held by its clients using an internal reference count it increments via client calls to AddRef(). It is up to the client to call Release() when it no longer needs the interface. Release() decrements the reference count and then the object becomes a candidate for destruction.

JavaScript handles its own garbage collection, so it doesn’t need to call AddRef() and Release().

Inside an XPCOM Component

Figure 3 shows a more detailed view of an XPCOM component.


Figure 3.

Image:Xpcom.png

As noted earlier, the component-specific interfaces of an XPCOM component all inherit from nsISupports. Likewise, an XPCOM component must implement

  • nsIFactory, which provides a mechanism for creating an object without having access to the class declaration for that object,
  • nsIModule, which provides a mechanism for registering and unregistering the XPCOM component, as well as for accessing the underlying class objects implementing the IDL interfaces, and
  • NSGetModule(), the entry point used to load the XPCOM component.

XPCOM components also link with a set of XPCOM utilities, such as strings, smart pointers, basic assertion and condition checking. These utilities are referred to as XPCOM glue.

At startup, the core application component identifies and loads all of the different XPCOM components comprising the core application services and builds a central registry it uses to generate instances of XPCOM components and instances of interface implementation classes on demand.

XPCOM in Summary

To summarize, the XPCOM object model

  • specifies the structure of XPCOM components,
  • builds a central registry based on the XPCOM components loaded at startup,
  • generates XPCOM component instances on demand using the registry, which specifies the supported interfaces, their corresponding implementation objects, and the nsIFactory interface,
  • provides API facilities clients can use to dynamically create XPCOM components, and
  • specifies the mechanism clients use to query an XPCOM component for one of its interfaces, and to release the interface when it’s no longer needed.

JavaScript Client Example

Suppose the JavaScript service in Figure 2 is getLink() in Help.js, which responds to the user clicking on a link in theContents Panel within theHelp Viewer window by obtaining the link from theContents Panel elements stored in a DOM tree. As a client, getLink() begins by obtaining the XULElement in the DOM tree representing theContents Panel.

var tocTree = document.getElementById("help-toc-panel");

The document object is a XULDocument, and tocTree is theContents Panel XULElement. Using the database attribute of the XULElement, getLink() obtains an nsIRDFCompositeDataSource, which presents the individual datasources of the XULElement as a single RDF graph.

var tocDS = tocTree.database;

NsIRDFCompositeDataSource inherits from nsIRDFDataSource.

Help.js declares an nsIRDFService as a compile time constant at the beginning of the file.

const RDF = Components.classes["@mozilla.org/rdf/rdf-service;1"].getService(Components.interfaces.nsIRDFService);

The Components object is made available to JavaScript via XPConnect; it serves as a bridge connecting JavaScript and XPCOM.

getLink() uses the nsIRDFService to obtain an nsIRDFResource for the requested link.

var resource = RDF.GetResource(rdfID);

nsIRDFCompositeDataSource inherits from nsIRDFDataSource, which supports a GetTarget() method for obtaining the nsIRDFNode for a given resource and property.

var link = tocDS.GetTarget(resource, NC_LINK, true);

Now that getLink() has the nsIRDFNode for the link, it can call QueryInterface() to check whether the nsIRDFLiteral is supported.

link = link.QueryInterface(Components.interfaces.nsIRDFLiteral);

If nsIRDFLiteral is supported, getLink() uses it to return the value of the link. Otherwise it returns null.

if (link)
  return link.Value;
else
  return null;

The document page for the link is displayed in theHelp Viewer window.