Adding XPCOM components to Mozilla build system

  • Revision slug: Adding_XPCOM_components_to_Mozilla_build_system
  • Revision title: Adding XPCOM components to Mozilla build system
  • Revision id: 488861
  • Created:
  • Creator: kscarfone
  • Is current revision? No
  • Comment

Revision Content

 

This document describes adding new components to Mozilla, including the build system and component registration. It does not deal with extensions, only core components checked into the main tree. It assumes you already know how to write XPCOM components, and want to integrate them into Mozilla.

Where to put your files

You will have to pick a good place for your component to live. Some general shared classes live in {{ Source("toolkit/components") }}, Firefox-specific classes live in {{ Source("browser/components") }}, a lot of random other stuff lives in {{ Source("extensions") }} and in other directories underneath the main mozilla directory. If you are extending a component, you can just use that component's directories. Otherwise, ask your favorite Mozilla guru or on #developers if you don't know where it should go.

Off of your component's directory, there is often:

  • public Where your IDL files go. Sometimes, there are also header files that will be #included by other components.
  • src Where your implementation files go. For C++ components, you will typically have a .cpp/.h pair for each implementation class. If you are implementing a class in JavaScript, put it here as well.
  • content Where your user interface files go, including .xul and any associated .js files that are used at runtime. If you aren't building new UI specific for this component, you won't have one of these.

Makefiles

Once you have your component written, you will need to write the makefiles. Your best bet is to copy the Makefile.in from another component similar to yours: most of the makefiles are very similar. These Makefile.in files are processed and the "real" Makefile is created. If you are using a separate directory tree for your object files, that final Makefiles will appear there.

For more details on Makefiles see How Mozilla's build system works.

Generally, you will have a top-level directory foo for your component, which will then contain foo/public, foo/src, etc. subdirectories. The makefile in foo will just reference the subdirecories and otherwise just needs the boilerplate:

   DEPTH           = ../..   # <-- SEE BELOW FOR THE MEANING OF THIS
   topsrcdir       = @top_srcdir@
   srcdir          = @srcdir@
   VPATH           = @srcdir@
   
   include $(DEPTH)/config/autoconf.mk
   
   DIRS            = public src
   
   include $(topsrcdir)/config/rules.mk

Your other makefiles will actually include all of your files. In these other makefiles, you should see/add the lines:

   MODULE = foo
   XPIDL_MODULE = foo

MODULE = foo means that a directory dist/include/foo will be created and your public files, such as .h files generated from .idl sources, will be placed there. Other modules will then be able to say REQUIRES = foo to add that directory to their include paths and use your interface files. XPIDL_MODULE controls the name of the .xpt file, which is a compiled version of your IDL files. The exact name of this doesn't matter much, and is generally the same as your MODULE name.

Other interesting makefile sections:

  • DEPTH: Near the top of the makefile gives the relative path of your mozilla/ directory from the current makefile. So {{ Source("toolkit/components/history/Makefile.in") }} would contain DEPTH = ../../..
  • XPIDLSRCS: This is the list of the IDL files in the current directory that will be processed. Generally, this will only be in makefiles in "public" subdirectories.
  • REQUIRES: This is a list of modules that the files in the current directory include files from. These names correspond to the MODULE=foo line in the other module (which generates a dist/include/foo directory that contains the header files).
  • CPPSRCS: A list of the .cpp files in the current directory to be compiled.
  • EXPORTS: A list of files to be copied to the "dist/include/foo" directory for other components to use. This is not very common, and is generally only seen in "public" subdirectories. It may be used, for example, if you have a helper .h file that is not an integral part of your component (and therefore has no IDL), but would be needed or useful to other C++ modules.
  • EXTRA_COMPONENTS: Here is where you list your JavaScript XPCOM files.
  • EXTRA_PP_COMPONENTS: Use this to list JavaScript XPCOM components that must be preprocessed before being installed.
  • LOCAL_INCLUDES: You can manually specify other include directories. Normally, you should use REQUIRES instead which will set up the include path for you. The format is LOCAL_INCLUDES = -Isome_directory/somewhere. For relative paths, use something like -I$(srcdir)/../some_component/src ($srcdir identifies the current directory, which might actually be different than the current directory of the compiler).
  • DEFINES: Additional preprocessor defines for C++. Example: DEFINES = -DSOME_DEFINE -DOTHER_DEFINE=25

Package manifest

{{ NeedsTechnicalReview() }}

When you add a new makefile for an XPCOM component, or when you add new files to an existing makefile, you should also add the relevant .xpt, .js and .manifest files to the appropriate section of the package-manifest.in file for all the products that are going to include your new components or interfaces.

For example, if your makefile includes the following lines:

EXTRA_COMPONENTS = \
  DownloadManagerUI.js \
  DownloadManagerUI.manifest \
  $(NULL)

You should also add the following lines to package-manifest.in:

@BINPATH@/components/DownloadManagerUI.js
@BINPATH@/components/DownloadManagerUI.manifest 

You can then follow the package build instructions to create an installer and verify that your new component is included.

Adding new JavaScript components to Mozilla core

Put your JavaScript file in some directory such as foo/src which is for XPCOM components (don't use content which is for interface code). Then add your filename to the EXTRA_COMPONENTS= section of the Makefile.in.

At the bottom of your javascript implementation, you should create a function NSGetModule, which just returns a reference to an nsIModule implementation:

function NSGetModule(compMgr, fileSpec) {
   return myModule;
}

Here is an example nsIModule implementation, which creates objects of type myImplementation:

var myModule = {
  mCID: Components.ID("{d1a83725-4a27-4a66-b212-915b14722c75}"), // (use your CID)
  mContractID: "@example.com/my-contract-id;1",                  // (use your contract ID)

  registerSelf: function (compMgr, fileSpec, location, type) {
    compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
    compMgr.registerFactoryLocation(this.mCID,
                                    "My class that does something useful", // (use your description)
                                    this.mContractID,
                                    fileSpec,
                                    location,
                                    type);
  },

  getClassObject: function (compMgr, cid, iid) {
    if (!cid.equals(this.mCID))
        throw Components.results.NS_ERROR_NO_INTERFACE;

    if (!iid.equals(Components.interfaces.nsIFactory))
        throw Components.results.NS_ERROR_NOT_IMPLEMENTED;

    return this.mFactory;
  },

  mFactory: {
    createInstance: function (outer, iid) {
      if (outer != null)
        throw Components.results.NS_ERROR_NO_AGGREGATION;
      return (new myImplementation()).QueryInterface(iid);  // (use your implementation object name)
    }
  }, 

  canUnload: function(compMgr) {
    return true;
  }
};

Adding new C++ components to Mozilla core

The init function

You can provide an initialization function for your class. This will be called immediately after your class is allocated and the constructor is called. The init function takes 0 arguments, returns an nsresult, and must be public. You can call it anything you like, just reference it from NS_GENERIC_FACTORY_CONSTRUCTOR_INIT (discussed below).

The advantage of using the init function over the normal C++ constructor is that you can return an error from the init function. If an error is detected, the function will be deallocated and the caller requesting an instance of your class will get the error. Generally, you should perform all initialization that may fail in your class' Init function.

Registering the component

The components are registered as part of a module, the source for which usually lives in a directory called build. For example, {{ Source("toolkit/components/build") }} is the module source for the components underneath {{ Source("toolkit/components/") }}. Likewise, there is a {{ Source("browser/components/build") }}, a {{ Source("widget/src/build") }}, etc.

First, find the Makefile.in and give it the name of your component. This should be the same name as the MODULE = foo line in your component's makefile. This makes sure the include path contains dist/include/foo where your component's generated header files are placed.

In the build directory, you will find a cpp file corresponding to the module, the naming of which is somewhat arbitrary. In this file, you will want to. First, include your components's header file. This is not the header generated from the IDL, but the header for the concrete implementation of the component (since this module is what will actually make instances of your implementation).

Typically, your implementation header file (required here) lives in your component's directory and is not public. This means it is not copied/linked to from dist/include/foo and just including your directory in the build REQUIRES section is not sufficient. In this case, you'll need to add your implementation source directory to LOCAL_INCLUDES. See the file {{ Source("toolkit/components/build/Makefile.in") }} for an example of how to do this.

Next, add a line:

   NS_GENERIC_FACTORY_CONSTRUCTOR(nsYourConcreteClassName)

This will generate a function called nsYourConcreteClassNameConstructor that allocates your object (see {{ Source("xpcom/glue/nsIGenericFactory.h") }}). You can also use:

   NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsYourConcreteClassName, Init)

which will call the function nsYourConcreteClassName->Init() after the object is allocated (see {{ Anch("The_init_function") }} above).

Add an nsModuleComponentInfo for your class

In the module .cpp file, there should be a long list of nsModuleComponentInfo structures corresponding to each of the classes the module implements. These structures generally look like this:

   { "Friendly class description for people to read",
     YOUR_CLASS_CID,
     YOUR_CLASS_CONTRACTID,
     nsYourConcreteClassNameConstructor }

There are a lot of optional arguments after this that are not often used. See the definition of nsModuleComponentInfo in {{ Source("xpcom/glue/nsIGenericFactory.h") }} for the full list.

YOUR_CLASS_CID: (component ID) is a GUID that identified the implementation of your class. This is different than the GUIDs used to identify each of the interfaces. (It is possible, but not common, to create a class instance using this value instead of the contract ID.) The value is often defined in a somethingCID.h file alongside the module .cpp file in the build directory. Sometimes, people declare them in their component's .h file instead. A typical definition is:

   /* 2d96b3d0-c051-11d1-a827-0040959a28c9 */
   #define NS_WINDOW_CID \
   { 0x2d96b3d0, 0xc051, 0x11d1, \
       {0xa8, 0x27, 0x00, 0x40, 0x95, 0x9a, 0x28, 0xc9}}

The contract ID: (the @example.com/... part, not to be confused with the "CID" = component ID) is the string that people can pass to GetService or CreateInstance to get an instance of your class. Sometimes these are defined alongside the CID in the header file in the build directory, or there is a special public header file that defines it. Both of these methods are fine. Other times they are just hard-coded into the structure or are defined in the IDL file, but these are not recommended.

The constructor is the name of the constructor automatically generated by the NS_GENERIC_FACTORY_CONSTRUCTOR line you added above. It will be the name of your concrete class followed by "Constructor".

If your class implements more than one contract: Define one nsModuleComponentInfo structure for each interface your class implements. The CID and the constructor name will be the same for all of these (since your concrete implementation is the same), but you will have different contract IDs corresponding to each interface.

Revision Source

<p>&nbsp;</p>
<p>This document describes adding new components to Mozilla, including the build system and component registration. It does not deal with extensions, only core components checked into the main tree. It assumes you already know how to <a href="/en/Creating_XPCOM_Components" title="en/Creating_XPCOM_Components"> write XPCOM components</a>, and want to integrate them into Mozilla.</p>
<h3 id="Where_to_put_your_files" name="Where_to_put_your_files">Where to put your files</h3>
<p>You will have to pick a good place for your component to live. Some general shared classes live in {{ Source("toolkit/components") }}, Firefox-specific classes live in {{ Source("browser/components") }}, a lot of random other stuff lives in {{ Source("extensions") }} and in other directories underneath the main <code>mozilla</code> directory. If you are extending a component, you can just use that component's directories. Otherwise, ask your favorite Mozilla guru or on <code>#developers</code> if you don't know where it should go.</p>
<p>Off of your component's directory, there is often:</p>
<ul>
 <li><code>public</code> Where your IDL files go. Sometimes, there are also header files that will be #included by other components.</li>
 <li><code>src</code> Where your implementation files go. For C++ components, you will typically have a .cpp/.h pair for each implementation class. If you are implementing a class in JavaScript, put it here as well.</li>
 <li><code>content</code> Where your user interface files go, including .xul and any associated .js files that are used at runtime. If you aren't building new UI specific for this component, you won't have one of these.</li>
</ul>
<h3 id="Makefiles" name="Makefiles">Makefiles</h3>
<p>Once you have your component written, you will need to write the makefiles. Your best bet is to copy the Makefile.in from another component similar to yours: most of the makefiles are very similar. These Makefile.in files are processed and the "real" Makefile is created. If you are using a separate directory tree for your object files, that final Makefiles will appear there.</p>
<p>For more details on Makefiles see <a href="/en/How_Mozilla's_build_system_works" title="en/How_Mozilla's_build_system_works">How Mozilla's build system works</a>.</p>
<p>Generally, you will have a top-level directory <code>foo</code> for your component, which will then contain <code>foo/public</code>, <code>foo/src</code>, etc. subdirectories. The makefile in <code>foo</code> will just reference the subdirecories and otherwise just needs the boilerplate:</p>
<pre class="eval">
   <a href="/en/DEPTH" title="en/DEPTH">DEPTH</a>           = ../..   # &lt;-- SEE BELOW FOR THE MEANING OF THIS
   topsrcdir       = @top_srcdir@
   srcdir          = @srcdir@
   <a href="/en/VPATH" title="en/VPATH">VPATH</a>           = @srcdir@
   
   include $(DEPTH)/config/autoconf.mk
   
   <a href="/en/DIRS" title="en/DIRS">DIRS</a>            = public src
   
   include $(topsrcdir)/config/rules.mk
</pre>
<p>Your other makefiles will actually include all of your files. In these other makefiles, you should see/add the lines:</p>
<pre class="eval">
   <a href="/en/MODULE" title="en/MODULE">MODULE</a> = foo
   <a href="/en/XPIDL_MODULE" title="en/XPIDL_MODULE">XPIDL_MODULE</a> = foo
</pre>
<p><code>MODULE = foo</code> means that a directory <code>dist/include/foo</code> will be created and your public files, such as .h files generated from .idl sources, will be placed there. Other modules will then be able to say <code><a href="/en/REQUIRES" title="en/REQUIRES">REQUIRES</a> = foo</code> to add that directory to their include paths and use your interface files. <code>XPIDL_MODULE</code> controls the name of the .xpt file, which is a compiled version of your IDL files. The exact name of this doesn't matter much, and is generally the same as your <code>MODULE</code> name.</p>
<p>Other interesting makefile sections:</p>
<ul>
 <li><strong>DEPTH:</strong> Near the top of the makefile gives the relative path of your mozilla/ directory from the current makefile. So {{ Source("toolkit/components/history/Makefile.in") }} would contain <code>DEPTH = ../../..</code></li>
 <li><strong>XPIDLSRCS:</strong> This is the list of the IDL files in the current directory that will be processed. Generally, this will only be in makefiles in "public" subdirectories.</li>
 <li><strong>REQUIRES:</strong> This is a list of modules that the files in the current directory include files from. These names correspond to the <code>MODULE=foo</code> line in the other module (which generates a <code>dist/include/foo</code> directory that contains the header files).</li>
 <li><strong><a href="/en/CPPSRCS" title="en/CPPSRCS">CPPSRCS</a>:</strong> A list of the .cpp files in the current directory to be compiled.</li>
 <li><strong><a href="/en/EXPORTS" title="en/EXPORTS">EXPORTS</a>:</strong> A list of files to be copied to the "dist/include/foo" directory for other components to use. This is not very common, and is generally only seen in "public" subdirectories. It may be used, for example, if you have a helper .h file that is not an integral part of your component (and therefore has no IDL), but would be needed or useful to other C++ modules.</li>
 <li><strong><a href="/en/EXTRA_COMPONENTS" title="en/EXTRA_COMPONENTS">EXTRA_COMPONENTS</a>:</strong> Here is where you list your JavaScript XPCOM files.</li>
 <li><strong><a href="/en/EXTRA_PP_COMPONENTS" title="en/EXTRA_PP_COMPONENTS">EXTRA_PP_COMPONENTS</a>:</strong> Use this to list JavaScript XPCOM components that must be preprocessed before being installed.</li>
 <li><strong><a href="/en/LOCAL_INCLUDES" title="en/LOCAL_INCLUDES">LOCAL_INCLUDES</a>:</strong> You can manually specify other include directories. Normally, you should use <code>REQUIRES</code> instead which will set up the include path for you. The format is <code>LOCAL_INCLUDES = -Isome_directory/somewhere</code>. For relative paths, use something like <code>-I$(srcdir)/../some_component/src</code> (<code>$srcdir</code> identifies the current directory, which might actually be different than the current directory of the compiler).</li>
 <li><strong><a href="/en/DEFINES" title="en/DEFINES">DEFINES</a>:</strong> Additional preprocessor defines for C++. Example: <code>DEFINES = -DSOME_DEFINE -DOTHER_DEFINE=25</code></li>
</ul>
<h3 id="Adding_new_JavaScript_components_to_Mozilla_core" name="Adding_new_JavaScript_components_to_Mozilla_core">Package manifest</h3>
<p>{{ NeedsTechnicalReview() }}</p>
<p>When you add a new makefile for an XPCOM component, or when you add new files to an existing makefile, you should also add the relevant <code>.xpt</code>, <code>.js</code> and <code>.manifest</code> files to the appropriate section of the <a class="external" href="http://mxr.mozilla.org/comm-central/find?string=package-manifest%5C.in" title="http://mxr.mozilla.org/comm-central/find?string=package-manifest\.in"><strong><code>package-manifest.in</code></strong></a> file for all the products that are going to include your new components or interfaces.</p>
<p>For example, if your makefile includes the following lines:</p>
<pre>
EXTRA_COMPONENTS = \
  DownloadManagerUI.js \
  DownloadManagerUI.manifest \
  $(NULL)</pre>
<p>You should also add the following lines to <code>package-manifest.in</code>:</p>
<pre>
@BINPATH@/components/DownloadManagerUI.js
@BINPATH@/components/DownloadManagerUI.manifest 
</pre>
<p>You can then follow the <a href="/en/Build_and_Install#Installing_Your_Build" title="en/Build_and_Install#Installing_Your_Build">package build instructions</a> to create an installer and verify that your new component is included.</p>
<h3 id="Adding_new_JavaScript_components_to_Mozilla_core" name="Adding_new_JavaScript_components_to_Mozilla_core">Adding new JavaScript components to Mozilla core</h3>
<p>Put your JavaScript file in some directory such as <code>foo/src</code> which is for XPCOM components (don't use <code>content</code> which is for interface code). Then add your filename to the <code>EXTRA_COMPONENTS=</code> section of the <code>Makefile.in</code>.</p>
<p>At the bottom of your javascript implementation, you should create a function <code>NSGetModule</code>, which just returns a reference to an <a href="/en/XPCOM_Interface_Reference/nsIModule" title="en/XPCOM_Interface_Reference/nsIModule">nsIModule</a> implementation:</p>
<pre class="eval">
function NSGetModule(compMgr, fileSpec) {
   return myModule;
}
</pre>
<p>Here is an example nsIModule implementation, which creates objects of type <code>myImplementation</code>:</p>
<pre class="eval">
var myModule = {
  mCID: Components.ID("{d1a83725-4a27-4a66-b212-915b14722c75}"), // (use your CID)
  mContractID: "@example.com/my-contract-id;1",                  // (use your contract ID)

  registerSelf: function (compMgr, fileSpec, location, type) {
    compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
    compMgr.registerFactoryLocation(this.mCID,
                                    "My class that does something useful", // (use your description)
                                    this.mContractID,
                                    fileSpec,
                                    location,
                                    type);
  },

  getClassObject: function (compMgr, cid, iid) {
    if (!cid.equals(this.mCID))
        throw Components.results.NS_ERROR_NO_INTERFACE;

    if (!iid.equals(Components.interfaces.nsIFactory))
        throw Components.results.NS_ERROR_NOT_IMPLEMENTED;

    return this.mFactory;
  },

  mFactory: {
    createInstance: function (outer, iid) {
      if (outer&nbsp;!= null)
        throw Components.results.NS_ERROR_NO_AGGREGATION;
      return (new myImplementation()).QueryInterface(iid);  // (use your implementation object name)
    }
  }, 

  canUnload: function(compMgr) {
    return true;
  }
};
</pre>
<h3 id="Adding_new_C.2B.2B_components_to_Mozilla_core" name="Adding_new_C.2B.2B_components_to_Mozilla_core">Adding new C++ components to Mozilla core</h3>
<h4 id="The_init_function" name="The_init_function">The init function</h4>
<p>You can provide an initialization function for your class. This will be called immediately after your class is allocated and the constructor is called. The init function takes 0 arguments, returns an nsresult, and must be public. You can call it anything you like, just reference it from <code>NS_GENERIC_FACTORY_CONSTRUCTOR_INIT</code> (discussed below).</p>
<p>The advantage of using the init function over the normal C++ constructor is that you can return an error from the init function. If an error is detected, the function will be deallocated and the caller requesting an instance of your class will get the error. Generally, you should perform all initialization that may fail in your class' Init function.</p>
<h4 id="Registering_the_component" name="Registering_the_component">Registering the component</h4>
<p>The components are registered as part of a module, the source for which usually lives in a directory called <code>build</code>. For example, {{ Source("toolkit/components/build") }} is the module source for the components underneath {{ Source("toolkit/components/") }}. Likewise, there is a {{ Source("browser/components/build") }}, a {{ Source("widget/src/build") }}, etc.</p>
<p>First, find the Makefile.in and give it the name of your component. This should be the same name as the <code>MODULE = foo</code> line in your component's makefile. This makes sure the include path contains <code>dist/include/foo</code> where your component's generated header files are placed.</p>
<p>In the build directory, you will find a cpp file corresponding to the module, the naming of which is somewhat arbitrary. In this file, you will want to. First, include your components's header file. This is not the header generated from the IDL, but the header for the concrete implementation of the component (since this module is what will actually make instances of your implementation).</p>
<p>Typically, your implementation header file (required here) lives in your component's directory and is not public. This means it is not copied/linked to from <code>dist/include/foo</code> and just including your directory in the build <code>REQUIRES</code> section is not sufficient. In this case, you'll need to add your implementation source directory to <code>LOCAL_INCLUDES</code>. See the file {{ Source("toolkit/components/build/Makefile.in") }} for an example of how to do this.</p>
<p>Next, add a line:</p>
<pre class="eval">
   NS_GENERIC_FACTORY_CONSTRUCTOR(nsYourConcreteClassName)
</pre>
<p>This will generate a function called nsYourConcreteClassNameConstructor that allocates your object (see {{ Source("xpcom/glue/nsIGenericFactory.h") }}). You can also use:</p>
<pre class="eval">
   NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsYourConcreteClassName, Init)
</pre>
<p>which will call the function <code>nsYourConcreteClassName-&gt;Init()</code> after the object is allocated (see {{ Anch("The_init_function") }} above).</p>
<h5 id="Add_an_nsModuleComponentInfo_for_your_class" name="Add_an_nsModuleComponentInfo_for_your_class">Add an <code>nsModuleComponentInfo</code> for your class</h5>
<p>In the module .cpp file, there should be a long list of <code>nsModuleComponentInfo</code> structures corresponding to each of the classes the module implements. These structures generally look like this:</p>
<pre class="eval">
   { "Friendly class description for people to read",
     YOUR_CLASS_CID,
     YOUR_CLASS_CONTRACTID,
     nsYourConcreteClassNameConstructor }
</pre>
<p>There are a lot of optional arguments after this that are not often used. See the definition of <code>nsModuleComponentInfo</code> in {{ Source("xpcom/glue/nsIGenericFactory.h") }} for the full list.</p>
<p><strong>YOUR_CLASS_CID:</strong> (component ID) is a GUID that identified the <em>implementation</em> of your class. This is different than the GUIDs used to identify each of the interfaces. (It is possible, but not common, to create a class instance using this value instead of the contract ID.) The value is often defined in a <code>somethingCID.h</code> file alongside the module .cpp file in the build directory. Sometimes, people declare them in their component's .h file instead. A typical definition is:</p>
<pre class="eval">
   /* 2d96b3d0-c051-11d1-a827-0040959a28c9 */
   #define NS_WINDOW_CID \
   { 0x2d96b3d0, 0xc051, 0x11d1, \
       {0xa8, 0x27, 0x00, 0x40, 0x95, 0x9a, 0x28, 0xc9}}
</pre>
<p><strong>The contract ID:</strong> (the <code>@example.com/...</code> part, not to be confused with the "CID" = component ID) is the string that people can pass to <code>GetService</code> or <code>CreateInstance</code> to get an instance of your class. Sometimes these are defined alongside the CID in the header file in the build directory, or there is a special public header file that defines it. Both of these methods are fine. Other times they are just hard-coded into the structure or are defined in the IDL file, but these are not recommended.</p>
<p><strong>The constructor</strong> is the name of the constructor automatically generated by the <code>NS_GENERIC_FACTORY_CONSTRUCTOR</code> line you added above. It will be the name of your concrete class followed by "Constructor".</p>
<p><strong>If your class implements more than one contract:</strong> Define one <code>nsModuleComponentInfo</code> structure for each interface your class implements. The CID and the constructor name will be the same for all of these (since your concrete implementation is the same), but you will have different contract IDs corresponding to each interface.</p>
Revert to this revision