Labs/JS Modules: Difference between revisions

not out of date => now out of date
(not out of date => now out of date)
 
(11 intermediate revisions by 4 users not shown)
Line 1: Line 1:
Back to [[Labs]].
'''The modules described here have been superseded by the [https://addons.mozilla.org/en-US/developers/docs/sdk/latest/ Addon-SDK]. What is described on this page is now out of date.'''


Here you'll find a collection of modules which you can import into your extension.  The goal is to make extension development easier by implementing common functionality as reusable libraries.  If you would like to contribute a new module, get in touch with us at #labs!
Here you'll find a collection of modules which you can import into your extension.  The goal is to make extension development easier by implementing common functionality as reusable libraries.  If you would like to contribute a new module, get in touch with us at #labs!
Line 11: Line 11:


= Modules =
= Modules =
== JSON ==
The [http://hg.mozdev.org/jsmodules/file/tip/JSON.js JSON module] wraps the incompatible Gecko 1.9.0 (Firefox 3.0) and Gecko 1.9.1 (Firefox 3.5) JSON APIs, presenting the Gecko 1.9.1 API on both versions, for extensions that support multiple versions of Gecko-based applications.
Import this module into your extension to parse and stringify JSON in both Firefox 3.0 and 3.5 (and other Gecko-based applications, like Thunderbird) without checking the application's version each time.
Note: don't import this into the global namespace!  If you do, you'll hork native application code that expects the Gecko 1.9.0 API.  Instead, import it into your own object like this:
let MyExtension = {
  JSON: null,
  ...
};
Components.utils.import("chrome://myextension/modules/JSON.js", MyExtension);
// Now MyExtension.JSON is an object implementing the Gecko 1.9.1 JSON API.
The Gecko 1.9.1 (Firefox 3.5) JSON API is documented in the article [https://developer.mozilla.org/En/Using_JSON_in_Firefox Using JSON in Firefox].


== Logging ==
== Logging ==
Line 55: Line 72:


Get it here:
Get it here:
* [http://hg.mozilla.org/labs/weave/index.cgi/file/tip/modules/log4moz.js log4moz.js] - Adorned
* [http://mxr.mozilla.org/services-central/source/fx-sync/services/sync/modules/log4moz.js log4moz.js] - Adorned
* [http://hg.mozilla.org/labs/weave/index.cgi/raw-file/tip/modules/log4moz.js log4moz.js] - Raw (right-click to save)
* [http://mxr.mozilla.org/services-central/source/fx-sync/services/sync/modules/log4moz.js?raw=1 log4moz.js] - Raw (right-click to save)


== Observers ==
== Observers ==
Line 112: Line 129:
Unregister observers by calling <code>Observers.remove</code> with the same parameters you passed to <code>Observers.add</code>:
Unregister observers by calling <code>Observers.remove</code> with the same parameters you passed to <code>Observers.add</code>:


  Observers.remove("topic", someFunction);
  Observers.remove("something", someFunction);
  Observers.remove("topic", someObject.onSomething, someObject);
  Observers.remove("something", someObject.onSomething, someObject);
  Observers.remove("topic", someObject);
  Observers.remove("something", someObject);


Note: the Observers module wraps observers in a wrapper that implements [http://mxr.mozilla.org/mozilla/source/xpcom/base/nsIWeakReference.idl?mark=71-94#71 nsISupportsWeakReference], so you don't necessarily have to remove your observers to avoid leaking memory.  Nevertheless, it is good practice to do so anyway.
Note: the Observers module wraps observers in a wrapper that implements [http://mxr.mozilla.org/mozilla/source/xpcom/base/nsIWeakReference.idl?mark=71-94#71 nsISupportsWeakReference], so you don't necessarily have to remove your observers to avoid leaking memory.  Nevertheless, it is good practice to do so anyway.
Line 164: Line 181:


== Preferences ==
== Preferences ==
=== Getting & Setting ===


The [http://hg.mozdev.org/jsmodules/file/tip/Preferences.js Preferences module] provides an API for accessing application preferences.  Getting and setting prefs is easy:
The [http://hg.mozdev.org/jsmodules/file/tip/Preferences.js Preferences module] provides an API for accessing application preferences.  Getting and setting prefs is easy:


  let foo = Preferences.get("extensions.test.foo");
  let foo = Preferences.get("extensions.test.foo");
  Preferences.set("extensions.test.foo", foo);
  Preferences.set("extensions.test.foo", "some value");


As with [http://developer.mozilla.org/en/docs/FUEL:PreferenceBranch FUEL's preferences API], datatypes are auto-detected, and you can pass a default value that the API will return if the pref doesn't have a value:
As with [http://developer.mozilla.org/en/docs/FUEL:PreferenceBranch FUEL's preferences API], datatypes are auto-detected, and you can pass a default value that the API will return if the pref doesn't have a value:
Line 175: Line 194:
  // foo == "default value"
  // foo == "default value"


Unlike FUEL, which returns null in the same situation, the module doesn't return a value when you get a nonexistent pref without specifying a default value:
Unlike FUEL, which returns null in the same situation, the module returns undefined when you get a nonexistent pref without specifying a default value:


  let foo = Preferences.get("extensions.test.nonexistent");
  let foo = Preferences.get("extensions.test.nonexistent");
Line 182: Line 201:
(Although the preferences service doesn't currently store null values, other interfaces like nsIVariant and nsIContentPrefService and embedded storage engines like SQLite distinguish between the null value and "doesn't have a value," as does JavaScript, so it seems more consistent and robust to do so here as well.)
(Although the preferences service doesn't currently store null values, other interfaces like nsIVariant and nsIContentPrefService and embedded storage engines like SQLite distinguish between the null value and "doesn't have a value," as does JavaScript, so it seems more consistent and robust to do so here as well.)


Because we aren't using XPCOM, we can include some interesting API features. First, as you may have noticed already, the interface doesn't require you to create a branch just to get a pref, but you can create one if you want to via an intuitive syntax:
Because we aren't using XPCOM, we can include some interesting API features. First, as you may have noticed already, the interface doesn't require you to create a branch just to get a pref, but you can create one via an intuitive syntax:


  let testBranch = new Preferences("extensions.test.");
  let testBranch = new Preferences("extensions.test.");
  // Preferences.get("extensions.test.foo") == testBranch.get("foo")
  // Preferences.get("extensions.test.foo") == testBranch.get("foo")


The get method uses polymorphism to enable you to retrieve multiple values in a single call, and, with JS 1.7's destructuring assignment, you can assign those values to individual variables:
<code>get</code> uses polymorphism to enable you to retrieve multiple values in a single call, and, with JS 1.7's destructuring assignment, you can assign those values to individual variables:


  let [foo, bar, baz] = testBranch.get(["foo", "bar", "baz"]);
  let [foo, bar, baz] = testBranch.get(["foo", "bar", "baz"]);
Line 194: Line 213:


  testBranch.set({ foo: 1, bar: "awesome", baz: true });
  testBranch.set({ foo: 1, bar: "awesome", baz: true });
=== Resetting & Checking ===
You can also reset preferences (i.e. remove the user-set value) and check if they're set:
Preferences.reset("extensions.test.foo");
let isSetFoo = Preferences.isSet("extensions.test.foo");
// isSetFoo == false, since we've just reset it
<code>isSet</code> tells you whether or not the preference has a set value. Some preferences also have default values that apply if the preference doesn't have a set value.  To find out if a preference has any value (whether set or default), use the <code>has</code> method:
Preferences.reset("extensions.test.foo");
let hasFoo = Preferences.has("extensions.test.foo");
// hasFoo == true, if the pref has a default value
To reset an entire branch of preferences, call <code>resetBranch</code>:
Preferences.resetBranch("extensions.test.");
// Any pref starting with "extensions.test." is now reset.
=== Locking and Unlocking ===
Preferences with default values can be locked to prevent users from changing them. While they are locked, <code>get</code> will always return the default value, even if the user set a value back when the preference was unlocked.
Use the <code>lock</code> and <code>unlock</code> methods to lock and unlock such preferences and the <code>locked</code> method to determine whether or not a preference is locked:
Preferences.lock("extensions.test.foo");
let lockedFoo = Preferences.locked("extensions.test.foo");
// lockedFoo == true, since we've just locked it
let foo = Preferences.get("extensions.test.foo");
// foo == the preference's default value, even if the user changed it
Preferences.unlock("extensions.test.foo");
lockedFoo = Preferences.locked("extensions.test.foo");
// lockedFoo == false, since we've just unlocked it
=== Registering a Function Observer ===
Register a function as an observer by calling <code>observe</code> with the name of the preference to observe and the function to observe it:
function someFunction(newValue) {...};
Preferences.observe("something", someFunction);
When receiving a notification that the preference has changed, the function will be provided the new value of the preference.
=== Registering a Method Observer ===
Register a method as an observer by calling <code>observe</code> with the name of the preference to observe, the method to observe it, and the object to which the method belongs:
let someObject = {
  onSomething: function(newValue) {...},
};
Preferences.observe("something", someObject.onSomething, someObject);
When receiving a notification that the preference has changed, the method will be provided the new value of the preference.
=== Registering an Object Observer ===
Register an object that implements the [http://mxr.mozilla.org/mozilla/source/xpcom/ds/nsIObserver.idl nsIObserver interface] as an observer by calling <code>observe</code> with the name of the preference to observe and the object to observe it:
let someObject = {
  observe: function(subject, topic, data) {...}
};
Preferences.observe("something", someObject);
When receiving a notification that a preference has changed, the object's observe method will be provided the notification's subject, topic, and data parameters, in that order, which is compatible with the type signature of the [http://mxr.mozilla.org/mozilla/source/xpcom/ds/nsIObserver.idl nsIObserver] interface.
=== Unregistering Observers ===
Unregister observers by calling <code>ignore</code> with the same parameters you passed to <code>observe</code>:
Preferences.ignore("something", someFunction);
Preferences.ignore("something", someObject.onSomething, someObject);
Preferences.ignore("something", someObject);
Note: the module wraps observers in a wrapper that implements [http://mxr.mozilla.org/mozilla/source/xpcom/base/nsIWeakReference.idl?mark=71-94#71 nsISupportsWeakReference], so you don't necessarily have to remove your observers to avoid leaking memory.  Nevertheless, it is good practice to do so anyway.


== StringBundle ==
== StringBundle ==
Line 216: Line 313:
   dump(string.key + " = " + string.value + "\n");
   dump(string.key + " = " + string.value + "\n");
  }
  }
== Sync ==
The [http://hg.mozdev.org/jsmodules/file/tip/Sync.js Sync module] provides a simple way to write asynchronous code in a sequential, callback-free fashion that looks like synchronous code. It also provides a sleep function written using its functionality.
Using the sleep function is really easy. Just import the module, which adds the Sync symbol to your namespace, then call its Sync.sleep method:
Components.utils.import("resource://path/to/Sync.js");
Sync.sleep(1000); // sleep 1000 milliseconds
You can create your own such "synchronous asynchronous" function by adding the Sync.sync method to the Function prototype and calling it wherever you would normally call your function. Your function must then accept a callback as its first argument that it calls once it is done with its asynchronous work.
Here's how to implement and then call your own sleep function:
Components.utils.import("resource://path/to/Sync.js");
Function.prototype.sync = Sync.sync;
function sleep(callback, milliseconds) {
  setTimeout(callback, milliseconds);
}
sleep.sync(1000); // sleep 1000 milliseconds


== URI ==
== URI ==
Confirmed users
52

edits