Components.Constructor

Summary

Creates a JavaScript function which can be used to create or construct new instances of XPCOM components.

Syntax

var func = [ new ] Components.Constructor(contractID [, interfaceName [, initializer ] ]);

Parameters

contractID
A string containing the contract ID of the component
interfaceName
If given, nsISupports.QueryInterface() will be called on each newly-created instance with the interface named by this string
initializer
If given, a string containing the name of a function which will be called on the newly-created instance, using the arguments provided to the created function when called

Description

Components.Constructor() is a handy shortcut for creating instances of XPCOM components. It eliminates the clutter of typing Components.classes, Components.interfaces, createInstance, and so on every time you wish to create an instance. It also gives creation of XPCOM objects more JavaScript-like syntax. Another important benefit is that it precomputes some of the computation it does, so using a function returned from Components.Constructor() to create XPCOM objects is faster than creating the object from base principles, as demonstrated below. (This benefit is also partly a result of having to travel through the layer between the JavaScript engine and XPCOM fewer times.)

The behavior of functions returned by Components.Constructor() varies depending upon the arguments given to Components.Constructor() when called. If only one argument is given, that argument is the contract ID of the XPCOM component to create. The component is then returned immediately, with only the base interface nsISupports available on it; you must call nsISupports.QueryInterface() on it to call methods on the object. For example:

var BinaryInputStream = Components.Constructor("@mozilla.org/binaryinputstream;1");
var bis = new BinaryInputStream();
print(bis.toString()); // "[xpconnect wrapped nsISupports]"

try
{
  // someInputStream is an existing nsIInputStream
  // throws because bis hasn't been QI'd to nsIBinaryInputStream
  bis.setInputStream(someInputStream);
}
catch (e)
{
  bis.QueryInterface(Components.interfaces.nsIBinaryInputStream);
  bis.setInputStream(someInputStream); // succeeds now
}

If two arguments are given, the created instance will be nsISupports.QueryInterface()'d to the XPCOM interface whose name is the second argument:

var BinaryInputStream = Components.Constructor("@mozilla.org/binaryinputstream;1",
                                               "nsIBinaryInputStream");
var bis = new BinaryInputStream();
print(bis.toString()); // "[xpconnect wrapped nsIBinaryInputStream]"

// someInputStream is an existing nsIInputStream
bis.setInputStream(someInputStream); // succeeds

If three arguments are given, then in addition to being nsISupports.QueryInterface()'d, the instance will also have had an initialization method called on it. The arguments used with the initialization method are the arguments passed to the Components.Constructor()-created function when called:

var BinaryInputStream = Components.Constructor("@mozilla.org/binaryinputstream;1",
                                               "nsIBinaryInputStream",
                                               "setInputStream");
try
{
  // throws, because number of arguments isn't equal to the number of
  // arguments nsIBinaryInputStream.setInputStream takes
  var bis = new BinaryInputStream();
}
catch (e)
{
  // someInputStream is an existing nsIInputStream
  bis = new BinaryInputStream(someInputStream); // succeeds
  var bytes = bis.readByteArray(someNumberOfBytes); // succeeds
}

Compare instance creation from base principles with instance creation using Components.Constructor(); the latter is much easier to read than the former (particularly if you're creating instances of a component in many different places):

var bis = Components.classes["@mozilla.org/binaryinputstream;1"]
                    .createInstance(Components.interfaces.nsIBinaryInputStream);
bis.setInputStream(someInputStream);
// assumes BinaryInputStream was initialized previously
var bis = new BinaryInputStream(someInputStream);

Components.Constructor() is purely syntactic sugar (albeit speedy and pretty syntactic sugar) for actions that can be accomplished using other common methods. It is equivalent to the following JavaScript function:

function Components_Constructor(contractID, interfaceName, initializer)
{
  var ccArgs = arguments;

  function ctor()
  {
    var instance = Components.classes[contractID]
                             .createInstance(Components.interfaces.nsISupports);

    if (ccArgs.length > 1)
    {
      instance.QueryInterface(Components.interfaces[interfaceName]);

      if (ccArgs.length > 2)
        instance[initializer].apply(instance, arguments);
    }

    return instance;
  }

  return ctor;
}