More on XPCOM

Before getting into the nuts and bolts of constructing your own XPCOM-enabled application or even your own XPCOM components, you will need a proper development environment. The best way to verify that all of the tools are in place and working properly is to do a build of the Mozilla browser.

Build the Mozilla

Mozilla is built on top of XPCOM and XPConnect so building it gives you both libraries and a suite of useful XPCOM components – some of which do not depend upon cohabitation with a browser. Go to http://www.mozilla.org, download the source code and supplemental development tools, and build everything. The Mozilla code uses generic make files, shell scripts, and Perl scripts in its build process instead of compiler-specific project files. This “least common denominator” approach is the only way to make sure that the same code gets built the same way on different platforms.

Be aware that this approach eats up lots of disk space. As of this writing, just unpacking the source code tarball held 160,975,003 bytes in over 28567 files. Due to file system inefficiencies on NTFS, this works out to 244,293,632 bytes of actual disk space used. That’s only the source code! Source code and binaries after a release build add up to just under 450 megabytes. This doesn’t include disk space for additional tools or space for a debug build.

CVS

Part of the magic of getting the same source code to compile on different platforms comes from the availability of some tools that have themselves been ported to numerous platforms. Some of those tools are gmake, CVS, and infozip.

CVS — the Concurrent Versioning System — is a simple but well-used and well-tested multiuser source control and versioning system for allowing large numbers of programmers to collaborate on large projects. It is comparable in function to Microsoft’s SourceSafe tool. RCS is another similar UNIX-oriented program.

CVS is a crucial weapon in the Mozilla arsenal for tracking bugs and patches. Your main interest in CVS is the ability to incrementally download the latest changes to the source code just as soon as they become available to everyone else. You can skip CVS if you don’t mind waiting for the next tarball release.

The CVS server requires a password and, unless you’ve gained the confidence of a module owner to nominate you for write access privileges, you will be using the generic, read-only account. This does not mean you aren’t allowed to contribute — far from it. It just means you will need to submit your patches directly to one of the module owners for consideration.

Setting up a Windows environment

Here’s the fast track to getting through this step on MS Windows:

  1. Make sure Microsoft Visual C++ 6 is installed along with all the latest updates (they were up to Service Pack 4 last I checked). There are efforts underway to support other compilers on Windows such as gcc, but for now only VC6 is known to work. VC5 with SP3 is also supposed to work, but I haven’t tried it.
  2. Make sure your compiler, linker, and make tool can be run from a shell window.
    • type “cl” – you should see something like “Microsoft (R) 32 bit Optimizing C/C++ compiler version ..”
    • type “nmake” – you should see something like “Microsoft (R) Program Maintenance Utility …”
    • type “lib” – you should see something like “Microsoft (R) Library Manager …”

If none of these work, see the environment variables hint under step 5 below.

  1. Download the source tarball and unzip the contents to a “mozilla” directory on a disk with lots of space (about 1 gig minimum or 2 gigs if you plan to do debug and release builds at the same time).
  2. Download the supplemental tools needed like cygwin, Active State Perl and infozip ( Note that you can use winzip or pkzip to unzip the tarball, but the Mozilla makefiles use infozip as part of the build.
  3. The following environment variables need to be defined:

For Windows 9X add a few commands to AUTOEXEC.BAT like so:

 set PATH=%PATH%;C:\cygwin\bin;C:\progra~1\micros~1\common\msdev98\bin;c:\perl\bin
 C:\progra~1\micros~1\vc98\bin\vcvars32
 set MOZ_BITS=32
 set MOZ_src=C:
 set MOZ_TOOLS=C:
 set CVSROOT=:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot

For Windows 2000 navigate to the Control Panel (click Start/Settings/Control Panel), click on “System”, select the “Advanced” tab and click on “Environment Variables” to get the edit dialog. Check for the following or something equivalent for your system (see Table 1).

Table 1. System environment variables [

PATH

C:\Program Files\Microsoft Visual Studio\Common\Tools\WinNT;
C:\Program Files\Microsoft Visual Studio\Common\MSDev98\Bin;
C:\Program Files\Microsoft Visual Studio\Common\Tools;
C:\Program Files\Microsoft Visual Studio\VC98\bin;
C:\Perl\bin;
C:\cygwin\bin

include

C:\Program Files\Microsoft Visual Studio\VC98\atl\include;
C:\Program Files\Microsoft Visual Studio\VC98\mfc\include;
C:\Program Files\Microsoft Visual Studio\VC98\include

lib

C:\Program Files\Microsoft Visual Studio\VC98\mfc\lib;
C:\Program Files\Microsoft Visual Studio\VC98\lib

MSDevDir

C:\Program Files\Microsoft Visual Studio\Common\MSDev98

MOZ_BITS

32

MOZ_SRC

C:

MOZ_TOOLS

C:

CVSROOT

:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot

The exact PATH specification will vary depending upon where you’ve installed some of your tools. The MOZ_SRC variable should point to the directory just above the mozilla directory tree. The MOZ_TOOLS variable should point to the directory just above your cygwin tools bin directory.

Cygwin GNU tools

The first set of third-party tools to install are a set of GNU command-line tools ported for Windows. Download the file “setup.exe” and run it (you need an active Internet connection for this to work). The setup program downloads the selected components and installs them. The components needed are ash, cygwin, diff, fileutils, gawk, grep, sed, shellutils, textutils, unzip, and zip. You could also pick up Perl and CVS while you are downloading these GNU tools but don’t do it — the build experts are recommending different versions, which are available elsewhere. Downloading the different modules can take time so some patience is required.

Infozip

The build process requires infozip to make JAR files. The cygwin setup above should have given you the option to download and install zip and unzip (the two pieces of infozip). As an alternate route for getting the infozip, tools you can follow these steps. The zip tool distribution comes in a ZIP file, so to unzip it you will need the separate unzip as well. Download the files “unz542xN.exe” and “zip23xN.zip” from the infozip site (or one of its mirrors) to a temporary directory and type these commands:

cd \temp
unz542xN
unzip zip23.zip
copy *.exe c:\bin

Netscape Wintools

Netscape has modified a handful of the GNU command-line tools to solve some problems mostly for makefile compatibility with the GNU-styled UNIX builds. You should have already added the environment variables mentioned earlier, as the installer for this bundle uses MOZ_TOOLS variable to determine where to install a few of the executables. The following, when done from the command prompt, will unzip the wintools.zip file to the default directory “buildtools” and complete the installation:

cd \temp
unzip wintools.zip
cd \buildtools\windows
install

The above step actually creates the “C:\bin” and “C:\include” directories (assuming you’ve defined MOZ_TOOLS to be “C:”). You can delete everything under “buildtools” after this step if you want to save some space. Unlike the other tools, the path to Netscape Wintools should not be included in the PATH environment variable. The makefiles use MOZ_TOOLS to find gmake and other Netscape-modified binaries.

Perl

Installing Active-State Perl is dreadfully easy — you can download it as a Windows installer module or MSI file. Using Windows Explorer, browse to the folder where you downloaded it, and click on the file named something like “ActivePerl-5_6_0_616-MSWin32-x86-multi-thread.msi”.

There are two minor potential “gotchas” to be aware of. First, the installer will add “\Perl\bin” to your PATH environment variable. If you are running Windows NT or Windows 2000, this will only apply to the currently logged-in user. Second, if you are running Windows 9X or NT, then you may need to download the Microsoft Windows Installer package from Microsoft’s Web site.

As a final test, reboot your machine to make sure any changes took effect. Open up a command-line window and try to run each of the third party tools by typing their names at the command prompt. Some commands to try are ash, diff, grep, perl, and unzip.

The preceding steps are only necessary when you are creating or recreating your build environment. These next steps are the ones you will need to perform each time you wish to actually build Mozilla.

If you plan to do a debug build, enter this command just before the build:

set MOZ_DEBUG=1

A few extra neat-to-know options are:

  • MOZ_SVG which adds SVG support
  • MOZ_MATHML which adds MathML support
  • MOZ_LDAP_XPCOM for LDAP support
  • MOZ_DISABLE_JAR_PACKAGING is useful if you do not want to mess with using zip in your builds
  • Some others worth playing with are MOZ_LITE, MOZ_MEDIUM, and MOZ_MAIL_NEWS

Finally, run the following command to kick off the build:

nmake /f client.mak build_all

Most of the common potential errors at this point involve the makefile issuing a command that fails because either the tool involved in the command is missing or is not visible from the search path. Once the kinks in the build environment are ironed out, the build process will run for a couple of hours (I am not kidding).

Even with a correct build environment, you may run into problems. To validate the above procedure, I tested it against the mozilla-0.7 tarball. I quickly ran into problems with gmake crashing on the NSPR module when the PR_CLIENT_BUILD_WINDOWS option was set (NSPR is the very first module to get built). I downloaded and unzipped the mozilla-0.8 tarball, re-ran the above command and everything compiled fine. The Mozilla build page offers a list of common errors and how to resolve them.

CVS on Windows

Download CVS binaries for Windows from the cvshome.org site.

Here’s the command sequence to pull the whole source tree from the CVS server:

  • set CVSROOT=:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot
  • set HOME=\TEMP
  • cvs login (answer the prompts for the CVS login)
  • cvs checkout mozilla/client.mak
  • cd mozilla
  • nmake -f client.mak pull_all

Here’s the command sequence to update an existing source tree (like the latest tarball) by having the CVS server only deliver those files that have changed:

  • set CVSROOT=:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot
  • set HOME=\TEMP
  • cvs login (answer the prompts for the CVS login)
  • cd mozilla
  • cvs -z3 checkout -PA mozilla/client.mk
  • nmake -f mozilla/client.mak checkout MOZ_CO_FLAGS=-PA

Setting up a Linux environment

In contrast to Windows, most of the programmer-friendly Linux distributions include all the tools needed to build Mozilla. If you’ve got a Linux CD like Debian or RedHat or Slackware, chances are that all the pieces you need are already there. The only issue for getting your build environment up and running is verifying which versions are installed.

Here’s a summary of the packages needed:

  • C++ Compiler
    • egcs
    • gcc
  • GNU make
  • GTK/GLib
  • Perl 5
  • zip

Set up your environment variables like so:

  • setenv MOZ_BITS 32
  • setenv MOZ_src=/usr/home
  • setenv CVSROOT=:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot

Run the config tool from the mozilla directory like so:

cd mozilla
./configure

From the mozilla directory, do a default build:

gmake

From the mozilla directory, do a manual build:

gmake -f client.mk build

CVS on Linux

Use these commands to pull the whole source tree from the CVS server:

  • setenv CVSROOT :pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot
  • cvs login (answer the CVS server login prompts)
  • cvs checkout mozilla/client.mk
  • cd mozilla
  • make -f client.mk checkout

Use these commands to update an existing source tree (like the latest tarball) with the latest sources from the CVS server:

  • setenv CVSROOT :pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot
  • cvs login (answer the CVS server login prompts)
  • cd mozilla
  • cvs -z3 checkout -PA mozilla/client.mk
  • make -f mozilla/client.mk checkout MOZ_CO_FLAGS=-PA

Other environments

Check the build page for explicit build instructions for Win32, Mac, UNIX, Linux, BSD, and other platforms. If you have actually followed through all of this and successfully established a working build environment on your computer, take a bow (and a break)! Pat yourself on the back, for now you can actually start to do something productive.

Enabling XPCOM in an application

Before an application can actually begin using XPCOM components, there are a set of libraries that must be loaded and initialized for the XPCOM framework to operate. Here’s a sample app to do that:

Listing 1. Sample app to load and initialize necessary libraries

 
#include <stdio.h>
#include <nsIServiceManager.h>
#include <nsISomething.h>

int main()
{
   static const char szContractId[] =
      "Your component's contract ID goes here";
   nsresult rv;

   // Initialize XPCOM and check for failure ...
   rv = NS_InitXPCOM(nsnull, nsnull);
   if ( NS_FAILED(rv) )
   {
      printf("Calling NS_InitXPCOM returns [%x].\n", rv);
      return -1;
   }

   // optional autoregistration - forces component manager to check for new components.
   (void)nsComponentManager::AutoRegister(nsIComponentManager::NS_Startup, nsnull);

   // Create an instance of our component
   nsCOMPtr
 mysample = do_CreateInstance(szContractId, &rv);
   if ( NS_FAILED(rv) )
   {
      printf("Creating component instance of %s fails.\n", szContractId);
      return -2;
   }

   // Do something useful with your component ...

   // (main body of code goes here)

   // Released any interfaces.

   // Shutdown XPCOM
   NS_ShutdownXPCOM(nsnull);
   return 0;
}

The two crucial calls are NS_InitXPCOM and NS_ShutdownXPCOM. The XPCOM core libraries are normally located in the same directory as the application, and an additional subdirectory named “components” is required. Once XPCOM has been initialized, the two big XPCOM components of immediate interest for doing something productive are the Component Manager and the Service Manager. The call to AutoRegister is actually optional, and is really only needed when a new component is installed.

The above example assumes a stand-alone application that does not need browser support. nsISomething is a placebo for some actual interface. Real-world application code that makes use of the interface would be placed where the “main body of code goes here” comment appears above.

Component manager

The component manager does just what its name implies. It keeps track of what components are currently installed and what DLL or shared library must be loaded to create a specific component. The components subdirectory mentioned above is where the component manager expects to find any components. It scans this directory in the AutoRegister step above looking for components not already registered and adds a descriptive entry into a private map file. Subsequent requests for components happen much quicker because it already knows from the map which DLL or shared library to load. Components are identified in one of two ways: a 128-bit UUID known as a class ID or CID or a short text name known as a Contract ID.

Note to MSCOM programmers: a contract ID is functionally equivalent to an MSCOM Program ID or ProgID.

Here are some core methods offered by the component manager in IDL. The first one looks up the class ID for a given contract ID. If you plan to create a lot of components of the same class and you only know the component’s contract ID, you can improve performance by calling this method first and using the shorter, faster class ID for subsequent calls to createInstance.

void contractIDToClassID(in string aContractID, out nsCID aClass);

This next method just does the inverse of the above:

string CLSIDToContractID(in nsCIDRef aClass, out string aClassName);

The following method verifies that some component has been registered and is therefore available for use:

boolean isRegistered(in nsCIDRef aClass);

The next two methods do all the grunt work for loading an arbitrary XPCOM component. You get your choice of identifying a component in the first parameter by class ID with createInstance or contractID in createInstanceByContractID. The second parameter aDelegate is only needed when you are doing what is called aggregation and is usually set to nsnull. The third parameter is the interface IID.

voidPtr createInstance(in nsCIDRef aClass, in nsISupports aDelegate, in nsIIDRef aIID);
voidPtr createInstanceByContractID(in string aContractID, in nsISupports aDelegate, in nsIIDRef IID);

The IDL source for all of the component manager methods can be found in nsIComponentManager.idl.

Service manager

XPCOM services are referred to in some books as singleton objects. No matter how many times you request a service you will always receive an interface to the same component. You’ve already seen the biggest service of them all — the component manager. How’s this for indirection: the service manager is itself a service. In a nutshell, the service manager takes care of loading and unloading services. When a request is made for a service that is already loaded, the service manager is smart enough to return another pointer to the existing service instead of trying to construct another object. Note how this behavior differs from the component manager which gives you a fresh new component on each request. Services are usually requested using the NS_WITH_SERVICE macro as illustrated in Listing 2.

Listing 2. Service request

 
{  // enter scope of service smart pointer ...
   NS_WITH_SERVICE(nsIMyService, service, kMyServiceCID, &rv);
   if (NS_FAILED(rv)) return rv;
   service->DoSomething(...);    // use my service
} // leaving scope of service smart pointer ...

Note that the NS_WITH_SERVICE macro uses nsCOMPtr to create a smart pointer. Some examples of services are listed in Table 2.

Table 2. Examples of services

  • LDAP
  • WebShell
  • JSRuntime
  • Editor
  • EventQueue
  • RDF
  • SMTP
  • IMAP
  • POP3
  • NNTP
  • DNS
  • Error
  • Logging

Category manager

The component manager and service manager will fetch a component given its contract or class ID. How would you go about finding components without either of these? The answer to this question is the category manager. The category manager provides a directory of class IDs grouped into categories. When I say “directory” here, think phone book (as in the Yellow Pages) and not disk drive. Using the phone book analogy, if we wanted to find all of the hotels in the area, we could look up “hotels” in the phone book.

Suppose you’ve written a neat editor with lots of word processing features that supports multiple document types through a generic set of interfaces. The document types you chose to support are text, HTML, RTF and PDF — but you’ve also registered your document handlers under the “document handler” category and written your program to always check the document handler category to determine what document types are available.

Your friend decides your program desperately needs to support WordPerfect files so she creates a new document handler component that implements the same set of interfaces, and she registers it under the document handler category. Any user of your program can now download, install and begin using your friend’s new document handler without any extra work on your part.

Components grouped under a category usually have something in common, like a predefined set of interfaces. The category becomes an implied contract for any component registering itself under that category. Categories are a very powerful means of achieving object independence since the code that makes use of a category only cares about the interfaces and can divorce itself from any specific implementation. Despite this power, categories are one of the most under-used features of XPCOM.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: