Using JavaScript code modules

  • Revision slug: JavaScript_code_modules/Using
  • Revision title: Using JavaScript code modules
  • Revision id: 239592
  • Created:
  • Creator: mattflaschen
  • Is current revision? No
  • Comment 8 words added

Revision Content

{{ gecko_minversion_header("1.9") }}

JavaScript code modules are a concept introduced in {{ Gecko("1.9") }} and can be used for sharing code between different privileged scopes. Modules can also be used to create global JavaScript singletons that previously required using JavaScript XPCOM objects. A JavaScript code module is simply some JavaScript code located in registered location. The module is loaded into a specific JavaScript scope, such as XUL script or JavaScript XPCOM script, using Components.utils.import().

Creating a JavaScript code module

A very simple JavaScript module looks like this:

var EXPORTED_SYMBOLS = ["foo", "bar"];

function foo() {
  return "foo";
}

var bar = {
  name : "bar",
  size : 3
};

var dummy = "dummy";

Notice that the module uses normal JavaScript to create functions, objects, constants and any other JavaScript type. The module also defines a special Array named EXPORTED_SYMBOLS. Any JavaScript item named in EXPORTED_SYMBOLS will be exported from the module and injected into the importing scope. For example:

Components.utils.import("resource://app/my_module.jsm");

alert(foo());         // displays "foo"
alert(bar.size + 3);  // displays "6"
alert(dummy);         // displays "dummy is not defined" because 'dummy' was not exported from the module
Note: When you're testing changes to a code module, be sure to change the application's build ID (e.g. the version) before your next test run; otherwise, you may find yourself running the previous version of your code module's code still.

The URL for a code module

As you can see from the example above, you need a URL to import a code module. (The URL in example is "resource://app/my_module.jsm".)

Code modules can only be loaded using a chrome: ({{ gecko_minversion_inline("2") }}), resource:, or file: URL.

  • If you're writing an extension for Firefox 4 and already have a chrome.manifest with a content instruction in it, you can put the code module in your content folder and reference it like your other content files via chrome://<yourextension>/content/<yourmodule>.jsm
  • If your extension or application needs to support Mozilla 1.9.x (Firefox 3.x), you should register a new resource URL. Details on doing this are in the "Extending resource: URLs" section below.

Sharing objects using code modules

An extremely important behavior of Components.utils.import() is that modules are cached when loaded and subsequent imports do not reload a new version of the module, but instead use the previously cached version. This means that a given module will be shared when imported multiple times. Any modifications to data, objects or functions will be available in any scope that has imported the module. For example, if the simple module were imported into two different JavaScript scopes, changes in one scope can be observed in the other scope.

Scope 1:

Components.utils.import("resource://app/my_module.jsm");

alert(bar.size + 3);  // displays "6"

bar.size = 10;

Scope 2:

Components.utils.import("resource://app/my_module.jsm");

alert(foo());         // displays "foo"
alert(bar.size + 3);  // displays "13"

This sharing behavior can be used to create singleton objects that can share data across windows and between XUL script and XPCOM components.

{{ Note("Each scope which imports a module receives a by-value copy of the exported symbols in that module. Changes to the symbol\'s value will not propagate to other scopes (though an object\'s properties will be manipulated by reference).") }}

Scope 1:

Components.utils.import("resource://app/my_module.jsm");

bar = "foo";
alert(bar);         // displays "foo"

Scope 2:

Components.utils.import("resource://app/my_module.jsm");

alert(bar);         // displays "[object Object]"

The main effect of the by-value copy is global variables of simple types won't be shared across scopes. Always put variables in a wrapper class and export the wrapper (such as bar in the above example).

{{ h2_gecko_minversion("Unloading code modules", "7.0") }}

Components.utils.unload() allows you to unload a previously imported code module. Once this method has been called, references to the module will continue to work but any subsequent import of the module will reload it and give a new reference.

Extending resource: URLs

Prior to {{ Gecko("2.0") }}, the most common way to load code modules was using resource: URLs. The basic syntax of a resource URL is as follows:

resource://<alias>/<relative-path>/<file.js|jsm>

The <alias> is an alias to a location, usually a physical location relative to the application or XUL runtime. There are several pre-defined aliases setup by the XUL runtime:

  • app - Alias to the location of the XUL application.
  • gre - Alias to the location of the XUL runtime.

The <relative-path> can be multiple levels deep and is always relative to the location defined by the <alias>. The common relative path is "modules" and is used by XUL Runner and Firefox. Code modules are simple JavaScript files with a .js or .jsm extension.

<alias> must be unique to your add-on, as the application and other extensions share the same namespace for all aliases.

Using chrome.manifest

The easiest way for extensions and XUL applications to add custom aliases is by registering an alias in the chrome manifest using a line like this:

resource aliasname uri/to/files/

For example, if the XPI for your foo extension includes a top-level modules/ directory containing the bar.js module (that is, the modules/ directory is a sibling to chrome.manifest and install.rdf), you could create an alias to that directory via the instruction:

resource foo modules/

(Don't forget the trailing slash!) You could then import the module into your JavaScript code via the statement:

Components.utils.import("resource://foo/bar.js");

Programmatically adding aliases

Custom aliases to paths that can be represented as an {{ interface("nsILocalFile") }} can be programmatically added as well. For example:

var ioService = Components.classes["@mozilla.org/network/io-service;1"]
                          .getService(Components.interfaces.nsIIOService);
var resProt = ioService.getProtocolHandler("resource")
                       .QueryInterface(Components.interfaces.nsIResProtocolHandler);

var aliasFile = Components.classes["@mozilla.org/file/local;1"]
                          .createInstance(Components.interfaces.nsILocalFile);
aliasFile.initWithPath("/some/absolute/path");

var aliasURI = ioService.newFileURI(aliasFile);
resProt.setSubstitution("myalias", aliasURI);

// assuming the code modules are in the alias folder itself

Notes

Custom modules and XPCOM components

Note that prior to {{ Gecko("2.0") }} JavaScript XPCOM components are loaded before chrome registration. This means you can't use Components.utils.import() with your own resource URL at the top level in a component source. A possible solution is moving the call to Components.utils.import() into the XPCOM component constructor (discussion).

Packaging notes

It's important to note that you should not typically put your JavaScript code modules in a JAR file in your add-on. Firefox 3.6 doesn't support them at all, and there's only one case in which it's remotely useful: a Firefox 4-only add-on which must be installed unpacked. Otherwise placing code modules in a JAR file breaks compatibility unnecessarily.

See also

JavaScript code modules topic page.

{{ languages( { "es": "es/Usando_m\u00f3dulos_de_c\u00f3digo_JavaScript", "fr": "fr/Utilisation_de_modules_de_code_JavaScript", "ja": "ja/Using_JavaScript_code_modules", "pl": "pl/Zastosowanie_modu\u0142\u00f3w_JavaScript" } ) }}

Revision Source

<p>{{ gecko_minversion_header("1.9") }}</p>
<p>JavaScript code modules are a concept introduced in {{ Gecko("1.9") }} and can be used for sharing code between different privileged scopes. Modules can also be used to create global JavaScript singletons that previously required using JavaScript XPCOM objects. A JavaScript code module is simply some JavaScript code located in registered location. The module is loaded into a specific JavaScript scope, such as XUL script or JavaScript XPCOM script, using <code><a href="/en/Components.utils.import" title="en/Components.utils.import">Components.utils.import()</a></code>.</p>
<h2>Creating a JavaScript code module</h2>
<p>A very simple JavaScript module looks like this:</p>
<pre class="brush: js" style="width: 900px;">var EXPORTED_SYMBOLS = ["foo", "bar"];

function foo() {
  return "foo";
}

var bar = {
  name : "bar",
  size : 3
};

var dummy = "dummy";
</pre>
<p>Notice that the module uses normal JavaScript to create functions, objects, constants and any other JavaScript type. The module also defines a special Array named <code>EXPORTED_SYMBOLS</code>. Any JavaScript item named in <code>EXPORTED_SYMBOLS</code> will be exported from the module and injected into the importing scope. For example:</p>
<pre class="brush: js">Components.utils.import("resource://app/my_module.jsm");

alert(foo());         // displays "foo"
alert(bar.size + 3);  // displays "6"
alert(dummy);         // displays "dummy is not defined" because 'dummy' was not exported from the module
</pre>
<div class="note"><strong>Note:</strong> When you're testing changes to a code module, be sure to change the application's build ID (e.g. the version) before your next test run; otherwise, you may find yourself running the previous version of your code module's code still.</div>
<h3>The URL for a code module</h3>
<p>As you can see from the example above, you need a URL to import a code module. (The URL in example is "<a class=" external" href="resource://app/my_module.jsm" rel="freelink">resource://app/my_module.jsm</a>".)</p>
<p>Code modules can only be loaded using a <strong>chrome:</strong> ({{ gecko_minversion_inline("2") }}), <strong>resource:</strong>, or <strong>file:</strong> URL.</p>
<ul> <li>If you're writing an extension for Firefox 4 and already have a <a href="/en/Chrome_Registration" title="en/Chrome registration">chrome.manifest</a> with a <code>content</code> instruction in it, you can put the code module in your content folder and reference it like your other content files via <code>chrome://&lt;yourextension&gt;/content/&lt;yourmodule&gt;.jsm</code></li> <li>If your extension or application needs to support Mozilla 1.9.x (Firefox 3.x), you should register a new resource URL. Details on doing this are in the <a href="#resource-urls">"Extending resource: URLs" section</a> below.</li>
</ul>
<h3>Sharing objects using code modules</h3>
<p>An extremely important behavior of <code><a href="/en/Components.utils.import" title="en/Components.utils.import">Components.utils.import()</a></code> is that modules are cached when loaded and subsequent imports do not reload a new version of the module, but instead use the previously cached version. This means that a given module will be shared when imported multiple times. Any modifications to data, objects or functions will be available in any scope that has imported the module. For example, if the simple module were imported into two different JavaScript scopes, changes in one scope can be observed in the other scope.</p>
<p>Scope 1:</p>
<pre class="brush: js">Components.utils.import("resource://app/my_module.jsm");

alert(bar.size + 3);  // displays "6"

bar.size = 10;
</pre>
<p>Scope 2:</p>
<pre class="brush: js">Components.utils.import("resource://app/my_module.jsm");

alert(foo());         // displays "foo"
alert(bar.size + 3);  // displays "13"
</pre>
<p>This sharing behavior can be used to create singleton objects that can share data across windows and between XUL script and XPCOM components.</p>
<p>{{ Note("Each scope which imports a module receives a by-value copy of the exported symbols in that module. Changes to the symbol\'s value will not propagate to other scopes (though an object\'s properties will be manipulated by reference).") }}</p>
<p>Scope 1:</p>
<pre class="brush: js">Components.utils.import("resource://app/my_module.jsm");

bar = "foo";
alert(bar);         // displays "foo"
</pre>
<p>Scope 2:</p>
<pre class="brush: js">Components.utils.import("resource://app/my_module.jsm");

alert(bar);         // displays "[object Object]"
</pre>
<p>The main effect of the by-value copy is global variables of simple types won't be shared across scopes. Always put variables in a wrapper class and export the wrapper (such as <code>bar</code> in the above example).</p>
<p>{{ h2_gecko_minversion("Unloading code modules", "7.0") }}</p>
<p><code><a href="/en/Components.utils.unload" title="en/Components.utils.unload">Components.utils.unload()</a></code> allows you to unload a previously imported code module. Once this method has been called, references to the module will continue to work but any subsequent import of the module will reload it and give a new reference.</p>
<h2 id="resource-urls">Extending resource: URLs</h2>
<p>Prior to {{ Gecko("2.0") }}, the most common way to load code modules was using <strong>resource:</strong> URLs. The basic syntax of a resource URL is as follows:</p>
<pre class="eval">resource://&lt;alias&gt;/&lt;relative-path&gt;/&lt;file.js|jsm&gt;
</pre>
<p>The <code>&lt;alias&gt;</code> is an alias to a location, usually a physical location relative to the application or XUL runtime. There are several pre-defined aliases setup by the XUL runtime:</p>
<ul> <li><code>app</code> - Alias to the location of the XUL application.</li> <li><code>gre</code> - Alias to the location of the XUL runtime.</li>
</ul>
<p>The <code>&lt;relative-path&gt;</code> can be multiple levels deep and is always relative to the location defined by the <code>&lt;alias&gt;</code>. The common relative path is "modules" and is used by XUL Runner and Firefox. Code modules are simple JavaScript files with a .js or .jsm extension.<br> <code> </code></p>
<p><code>&lt;alias&gt;</code> must be unique to your add-on, as the application and other extensions share the same namespace for all aliases.</p>
<h4>Using chrome.manifest</h4>
<p>The easiest way for extensions and XUL applications to add custom aliases is by registering an alias in the <a href="/en/Chrome_Registration" title="en/Chrome_Registration">chrome manifest</a> using a line like this:</p>
<pre class="eval">resource <em>aliasname</em> <em>uri/to/files/</em>
</pre>
<p>For example, if the XPI for your <em>foo</em> extension includes a top-level modules/ directory containing the <em>bar.js</em> module (that is, the modules/ directory is a sibling to chrome.manifest and install.rdf), you could create an alias to that directory via the instruction:</p>
<pre class="eval">resource foo modules/
</pre>
<p>(Don't forget the trailing slash!) You could then import the module into your JavaScript code via the statement:</p>
<pre class="brush: js">Components.utils.import("<a class=" external" href="resource://foo/bar.js" rel="freelink">resource://foo/bar.js</a>");
</pre>
<h4>Programmatically adding aliases</h4>
<p>Custom aliases to paths that can be represented as an {{ interface("nsILocalFile") }} can be programmatically added as well. For example:</p>
<pre class="brush: js">var ioService = Components.classes["@<a class="linkification-ext" href="http://mozilla.org/network/io-service;1" title="Linkification: http://mozilla.org/network/io-service;1">mozilla.org/network/io-service;1</a>"]
                          .getService(Components.interfaces.nsIIOService);
var resProt = ioService.getProtocolHandler("resource")
                       .QueryInterface(Components.interfaces.nsIResProtocolHandler);

var aliasFile = Components.classes["@<a class="linkification-ext" href="http://mozilla.org/file/local;1" title="Linkification: http://mozilla.org/file/local;1">mozilla.org/file/local;1</a>"]
                          .createInstance(Components.interfaces.nsILocalFile);
aliasFile.initWithPath("/some/absolute/path");

var aliasURI = ioService.newFileURI(aliasFile);
resProt.setSubstitution("myalias", aliasURI);

// assuming the code modules are in the alias folder itself
</pre>
<h2>Notes</h2>
<h3>Custom modules and XPCOM components</h3>
<p>Note that prior to {{ Gecko("2.0") }} JavaScript XPCOM components are loaded before chrome registration. This means you can't use <code><a href="/en/Components.utils.import" title="en/Components.utils.import">Components.utils.import()</a></code> with your own resource URL at the top level in a component source. A possible solution is moving the call to <code><a href="/en/Components.utils.import" title="en/Components.utils.import">Components.utils.import()</a></code> into the XPCOM component constructor (<a class="external" href="http://groups.google.com/group/mozilla.dev.apps.firefox/browse_thread/thread/e178d41afa2ccc87?hl=en#" title="http://groups.google.com/group/mozilla.dev.apps.firefox/browse_thread/thread/e178d41afa2ccc87?hl=en#">discussion</a>).</p>
<h3>Packaging notes</h3>
<p>It's important to note that you should not typically put your JavaScript code modules in a JAR file in your add-on. Firefox 3.6 doesn't support them at all, and there's only one case in which it's remotely useful: a Firefox 4-only add-on which must be installed unpacked. Otherwise placing code modules in a JAR file breaks compatibility unnecessarily.</p>
<h2 name="See_also">See also</h2>
<p><a href="/en/JavaScript_code_modules" title="en/JavaScript code modules">JavaScript code modules</a> topic page.</p>
<p>{{ languages( { "es": "es/Usando_m\u00f3dulos_de_c\u00f3digo_JavaScript", "fr": "fr/Utilisation_de_modules_de_code_JavaScript", "ja": "ja/Using_JavaScript_code_modules", "pl": "pl/Zastosowanie_modu\u0142\u00f3w_JavaScript" } ) }}</p>
Revert to this revision