XPConnect Sample

Ariel Blackenroth <arielb@netscape.com>
Michael Ang <mang@subcarrier.org>
Last modified Aug 4, 1999


In the spirit of "worse is better" this somewhat rough guide is being released to the world. It will be expanded upon and improved.

XPConnect allows JavaScript to transparantly access and manipulate XPCOM objects; this communication between JavaScript and native code is done by having their interfaces defined in the XPIDL interface definition language. See the Roadmap for documentation on XPCOM, XPConnect, XPTCall and XPIDL for more information.

Overview

This sample demonstrates accessing a XPCOM object through XPConnect. The JavaScript executed when this page loads creates an instance of the object by using the Components object, then accesses it through the nsISample interface by calling QueryInterface:

var sample = Components.classes["component://netscape/sample/sample-world"].createInstance();
sample = sample.QueryInterface(Components.interfaces.nsISample);

The buttons on the form are connected to JavaScript event handlers which call the methods defined in C++.

nsISample.idl

This is the interface declaration for the XPCOM object. It defines two functions, their parameters, and one attribute. It also defines the interface's id. The idl file is compiled by the xpidl compiler into a C++ header, nsISample.h and a .xpt file which is a binary representation of the interface used at runtime.
attribute string Value;
void WriteValue(in string aPrefix);
void Poke(in string aValue);

nsSample.cpp

This contains the implementation of nsISample.idl. SampleImpl inherits from nsISample.h, the header dynamically created by the xpidl compiler. The attribute Value has been expanded into a get and set and the return values have been modified to NS_IMETHOD, a success status for the method. The macro NS_DECL_ISUPPORTS, defined in mozilla/xpcom/public/nsISupportsUtils.h defines the inherited methods from nsISupports.h.
NS_IMPL_ISUPPORTS(SampleImpl, nsISample::GetIID());
In the constructor, the macro NS_INIT_REFCNT is called which sets the reference count to 0.

nsSampleFactory.cpp

This is the class which builds the instance of the nsSample class. The COM framework uses factories to create instance of implementations rather than having the implementations instatiate themselves in order to increase portability of code. This factory inherits from nsFactory, which is also an XPCOM object. To gain more knowledge of factories see the generic factory document or the Modularization techniques document.

Compiling the idl

The XPIDL compiler (xpidl on Unix, xpidl.exe on Windows, and a CodeWarrior plugin on Mac) is compiled at build time (except on Mac) thus you will have to build mozilla in order to test this out. If you have already built mozilla then the compiler will be located at mozilla\dist\WIN32_D.OBJ\bin\xpidl.exe.

Once you have the XPIDL compiler enter the following command at your prompt:
D:\mozilla\xpcom\sample>d:\mozilla\dist\WIN32_D.OBJ\bin\xpidl -I d:\mozilla\dist\idl -m header nsISample.idl

The -I d:\mozilla\dist\idl points the compiler to the folder containing the other idl files, needed because nsISample.idl inherits from nsISupports.idl. The -m header instruction tells the compiler to build the C++ header. To build the .xpt file substitute -m typelib.

For more information on compilation see the xpidl compiler page.

Building the Sample

To build the Sample just enter
d:\mozilla\xpcom\sample>nmake /f makefile.win

In order to do this you need to have your environment variables set correctly. See the Build page for more information.

Running the sample

Using Mozilla, load resource://res/samples/xpconnect-sample.html (i.e. what you're reading now). Pay attention to the console when clicking "write". Notice that the value printed is calculated in C++ code defined in nsSample.cpp.


JavaScript and form source:

<script>
var sample = Components.classes["component://netscape/sample/sample-world"].createInstance();
sample = sample.QueryInterface(Components.interfaces.nsISample);
dump("sample = " + sample + "\n");

function get()
{
  var field = document.getElementById('Value');
  field.value = sample.Value;
}

function set()
{
  var field = document.getElementById('Value');
  sample.Value = field.value;
}

function poke()
{
  var field = document.getElementById('Value');
  sample.Poke(field.value);
}

function write()
{
  sample.WriteValue("here is what I'm writing!" + Components.interfaces.nsISample.MY_CONST_THINGIE);
}
</script>

<form name="form">
<input type="button" value="Get" onclick="get();">
<input type="button" value="Set" onclick="set();">
<input type="button" value="Poke" onclick="poke();">
<input type="text" id="Value">
<input type="button" value="Write" onclick="write();">
<form>


Resources:
Comments to: Michael Ang <mang@subcarrier.org>