simple-prefs #270
simple-prefs #270
Changes from 39 commits
7efa45e
0fb88ad
c38ece3
58fd871
7d2346f
50ca0ac
1812ce5
0edb23b
420aca5
c75fc72
bc9f47e
e48f08b
e787ebf
4d6f92a
8e59e87
fd07f2b
85511f1
0329bcd
e9d6b60
b697bdd
2340b6d
bce02bf
5c75456
5b2d8b9
903d908
315c6de
10da2c0
299ec79
234cb71
af6cce0
9435bb0
c163a67
77b8f4f
5c6d88f
2e1835e
21f1cf0
e638955
4cee3d5
ef31837
56f4aae
8bda536
b7a357a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -48,6 +48,12 @@ called `package.json`. This file is also referred to as the | |
[`icon64URL` entry in the Install Manifest](https://developer.mozilla.org/en/install_manifests#icon64URL), | ||
so the icon should be 64x64 pixels in size. | ||
|
||
* `preferences` - a JSON object whose keys are preference names, where each | ||
key is has a JSON object that has the following optional keys `type`, `value`, | ||
`title`, and `description`. This JSON object will be used to automatically | ||
create a `options.xul` file for [inline options](https://developer.mozilla.org/en/Extensions/Inline_Options), | ||
and a `defaults/preferences/prefs.js` file for default preference values. | ||
For more information see the documentation of [simple-prefs](packages/addon-kit/docs/simple-prefs.html). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: a However, I'm not sure it's worth mentioning these files in this document, given that the purpose of the API is to abstract the complexity of the underlying implementation. I would instead say the JSON object will be used to create a preferences interface for the addon in the Add-ons Manager. |
||
|
||
* `license` - the name of the license as a String, with an optional | ||
URL in parentheses. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
<!-- contributed by Erik Vold [erikvvold@gmail.com] --> | ||
|
||
The `simple-prefs` module lets you easily and persistently store preferences | ||
across application restarts, using the Mozilla preferences system. These | ||
preferences will be configurable by the user in [about:addons](about:addons) and | ||
in [about:config](about:config). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here again the document seems to dive to too low a level. Instead of talking about about:addons, I would talk about the Add-ons Manager. And I'd leave out about:config entirely. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similarly, I wouldn't talk about the "Mozilla preferences system". It doesn't matter what the underlying technology is, and that technology may change over time. The important thing is that this API allows addons to solicit preferences from their users. |
||
|
||
Introduction | ||
------------ | ||
|
||
With the simple preferences module you can store booleans, integers, and string | ||
values. | ||
|
||
|
||
Inline Options & Default Values | ||
------------------------------- | ||
|
||
In order to have a `options.xul` (for inline options) generated, or a | ||
`defaults/preferences/prefs.js` for default preferences, you will need to | ||
define the preferences in your `package.json`, like so: | ||
|
||
{ | ||
"preferences": { | ||
"prefName": { | ||
"title": "Some preference title", | ||
"type": "string", | ||
"value": "this is the default string value" | ||
} | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Developers typically want to present preferences in a particular order, but this data structure doesn't preserve order. It seems like the |
||
|
||
|
||
<api name="prefs"> | ||
@property {object} | ||
A persistent object private to your add-on. Properties with boolean, | ||
number, and string values will be persisted in the Mozilla preferences system. | ||
</api> | ||
|
||
|
||
<api name="on"> | ||
@function | ||
Registers an event `listener` that will be called when a preference is changed. | ||
|
||
**Example:** | ||
|
||
require("simple-prefs").on('pref-name', function(prefName) { | ||
console.log('The following pref was changed: ' + prefName) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: add a semi-colon at the end of the statement. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This example is a bit confusing, since it isn't clear that "pref-name" is the name of a preference, nor is it clear why the example references prefName if pref-name is the name of a preference. I would do something like this instead:
|
||
}); | ||
|
||
@param prefName {String} | ||
The name of the preference to watch for changes. | ||
@param listener {Function} | ||
The listener function that processes the event. | ||
</api> | ||
|
||
<api name="removeListener"> | ||
@function | ||
Unregisters an event `listener` for the specified preference. | ||
|
||
@param prefName {String} | ||
The name of the preference to watch for changes. | ||
@param listener {Function} | ||
The listener function that processes the event. | ||
</api> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
/* ***** BEGIN LICENSE BLOCK ***** | ||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1 | ||
* | ||
* The contents of this file are subject to the Mozilla Public License Version | ||
* 1.1 (the "License"); you may not use this file except in compliance with | ||
* the License. You may obtain a copy of the License at | ||
* http://www.mozilla.org/MPL/ | ||
* | ||
* Software distributed under the License is distributed on an "AS IS" basis, | ||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | ||
* for the specific language governing rights and limitations under the | ||
* License. | ||
* | ||
* The Original Code is Jetpack. | ||
* | ||
* The Initial Developer of the Original Code is | ||
* Mozilla Foundation. | ||
* Portions created by the Initial Developer are Copyright (C) 2010 | ||
* the Initial Developer. All Rights Reserved. | ||
* | ||
* Contributor(s): | ||
* Hernan Rodriguez Colmeiro <colmeiro@gmail.com> (Original Author) | ||
* Erik Vold <erikvvold@gmail.com> | ||
* | ||
* Alternatively, the contents of this file may be used under the terms of | ||
* either the GNU General Public License Version 2 or later (the "GPL"), or | ||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), | ||
* in which case the provisions of the GPL or the LGPL are applicable instead | ||
* of those above. If you wish to allow use of your version of this file only | ||
* under the terms of either the GPL or the LGPL, and not to allow others to | ||
* use your version of this file under the terms of the MPL, indicate your | ||
* decision by deleting the provisions above and replace them with the notice | ||
* and other provisions required by the GPL or the LGPL. If you do not delete | ||
* the provisions above, a recipient may use your version of this file under | ||
* the terms of any one of the MPL, the GPL or the LGPL. | ||
* | ||
* ***** END LICENSE BLOCK ***** */ | ||
|
||
const { Cc, Ci } = require("chrome"); | ||
const observers = require("observer-service"); | ||
const { EventEmitter } = require("events"); | ||
const unload = require("unload"); | ||
const prefService = require("preferences-service"); | ||
const { jetpackID } = require("@packaging"); | ||
|
||
const ADDON_BRANCH = "extensions." + jetpackID + "."; | ||
const BUTTON_PRESSED = jetpackID + "-cmdPressed"; | ||
|
||
// XXX Currently, only Firefox implements the inline preferences. | ||
if (!require("xul-app").is("Firefox")) | ||
throw Error("This API is only supported in Firefox"); | ||
|
||
let branch = Cc["@mozilla.org/preferences-service;1"]. | ||
getService(Ci.nsIPrefService). | ||
getBranch(ADDON_BRANCH). | ||
QueryInterface(Ci.nsIPrefBranch2); | ||
|
||
const events = EventEmitter.compose({ | ||
constructor: function Prefs() { | ||
// Log unhandled errors. | ||
this.on("error", console.exception.bind(console)); | ||
|
||
// Make sure we remove all the listeners | ||
unload.ensure(this); | ||
|
||
this._prefObserver = this._prefObserver.bind(this); | ||
this._buttonObserver = this._buttonObserver.bind(this); | ||
|
||
// Listen to changes in the preferences | ||
branch.addObserver("", this._prefObserver, false); | ||
|
||
// Listen to clicks on buttons | ||
observers.add(BUTTON_PRESSED, this._buttonObserver, this); | ||
}, | ||
_prefObserver: function PrefsPrefObserver(subject, topic, prefName) { | ||
if (topic == "nsPref:changed") { | ||
this._emit(prefName, prefName); | ||
} | ||
}, | ||
_buttonObserver: function PrefsButtonObserver(subject, data) { | ||
this._emit(data); | ||
}, | ||
unload: function manager_unload() { | ||
this._removeAllListeners("error"); | ||
branch.removeObserver("", this._prefObserver); | ||
}, | ||
})(); | ||
|
||
const simple = Proxy.create({ | ||
get: function(receiver, pref) { | ||
return prefService.get(ADDON_BRANCH + pref); | ||
}, | ||
set: function(receiver, pref, val) { | ||
prefService.set(ADDON_BRANCH + pref, val); | ||
}, | ||
delete: function(pref) { | ||
prefService.reset(ADDON_BRANCH + pref); | ||
return true; | ||
}, | ||
has: function(pref) { | ||
return prefService.has(ADDON_BRANCH + pref); | ||
} | ||
}); | ||
|
||
exports.on = events.on; | ||
exports.removeListener = events.removeListener; | ||
exports.prefs = simple; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
/* ***** BEGIN LICENSE BLOCK ***** | ||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1 | ||
* | ||
* The contents of this file are subject to the Mozilla Public License Version | ||
* 1.1 (the "License"); you may not use this file except in compliance with | ||
* the License. You may obtain a copy of the License at | ||
* http://www.mozilla.org/MPL/ | ||
* | ||
* Software distributed under the License is distributed on an "AS IS" basis, | ||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | ||
* for the specific language governing rights and limitations under the | ||
* License. | ||
* | ||
* The Original Code is Jetpack. | ||
* | ||
* The Initial Developer of the Original Code is | ||
* the Mozilla Foundation. | ||
* Portions created by the Initial Developer are Copyright (C) 2011 | ||
* the Initial Developer. All Rights Reserved. | ||
* | ||
* Contributor(s): | ||
* Erik Vold <erikvvold@gmail.com> (Original Author) | ||
* | ||
* Alternatively, the contents of this file may be used under the terms of | ||
* either the GNU General Public License Version 2 or later (the "GPL"), or | ||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), | ||
* in which case the provisions of the GPL or the LGPL are applicable instead | ||
* of those above. If you wish to allow use of your version of this file only | ||
* under the terms of either the GPL or the LGPL, and not to allow others to | ||
* use your version of this file under the terms of the MPL, indicate your | ||
* decision by deleting the provisions above and replace them with the notice | ||
* and other provisions required by the GPL or the LGPL. If you do not delete | ||
* the provisions above, a recipient may use your version of this file under | ||
* the terms of any one of the MPL, the GPL or the LGPL. | ||
* | ||
* ***** END LICENSE BLOCK ***** */ | ||
|
||
|
||
const { Loader } = require("./helpers"); | ||
const setTimeout = require("timers").setTimeout; | ||
const notify = require("observer-service").notify; | ||
const { jetpackID } = require("@packaging"); | ||
|
||
exports.testSetGetBool = function(test) { | ||
test.waitUntilDone(); | ||
|
||
let loader = Loader(module); | ||
let sp = loader.require("simple-prefs").prefs; | ||
|
||
test.assertEqual(sp.test, undefined, "Value should not exist"); | ||
sp.test = true; | ||
test.assert(sp.test, "Value read should be the value previously set"); | ||
|
||
loader.unload(); | ||
test.done(); | ||
}; | ||
|
||
exports.testSetGetInt = function(test) { | ||
test.waitUntilDone(); | ||
|
||
// Load the module once, set a value. | ||
let loader = Loader(module); | ||
let sp = loader.require("simple-prefs").prefs; | ||
|
||
test.assertEqual(sp["test-int"], undefined, "Value should not exist"); | ||
sp["test-int"] = 1; | ||
test.assertEqual(sp["test-int"], 1, "Value read should be the value previously set"); | ||
|
||
loader.unload(); | ||
test.done(); | ||
}; | ||
|
||
exports.testSetComplex = function(test) { | ||
test.waitUntilDone(); | ||
|
||
let loader = Loader(module); | ||
let sp = loader.require("simple-prefs").prefs; | ||
|
||
try { | ||
sp["test-complex"] = {test: true}; | ||
test.fail("Complex values are not allowed"); | ||
} | ||
catch (e) { | ||
test.pass("Complex values are not allowed"); | ||
} | ||
|
||
loader.unload(); | ||
test.done(); | ||
}; | ||
|
||
exports.testSetGetString = function(test) { | ||
test.waitUntilDone(); | ||
|
||
let loader = Loader(module); | ||
let sp = loader.require("simple-prefs").prefs; | ||
|
||
test.assertEqual(sp["test-string"], undefined, "Value should not exist"); | ||
sp["test-string"] = "test"; | ||
test.assertEqual(sp["test-string"], "test", "Value read should be the value previously set"); | ||
|
||
loader.unload(); | ||
test.done(); | ||
}; | ||
|
||
exports.testHasAndRemove = function(test) { | ||
test.waitUntilDone(); | ||
|
||
let loader = Loader(module); | ||
let sp = loader.require("simple-prefs").prefs; | ||
|
||
sp.test = true; | ||
test.assert(("test" in sp), "Value exists"); | ||
delete sp.test; | ||
test.assertEqual(sp.test, undefined, "Value should be undefined"); | ||
|
||
loader.unload(); | ||
test.done(); | ||
|
||
}; | ||
|
||
exports.testPrefListener = function(test) { | ||
test.waitUntilDone(); | ||
|
||
let loader = Loader(module); | ||
let sp = loader.require("simple-prefs"); | ||
|
||
let listener = function(prefName) { | ||
test.assertEqual(prefName, "test-listen", "The prefs listener heard the right event"); | ||
test.done(); | ||
}; | ||
|
||
sp.on("test-listen", listener); | ||
|
||
sp.prefs["test-listen"] = true; | ||
loader.unload(); | ||
}; | ||
|
||
exports.testBtnListener = function(test) { | ||
test.waitUntilDone(); | ||
|
||
let loader = Loader(module); | ||
let sp = loader.require("simple-prefs"); | ||
|
||
sp.on("test-btn-listen", function() { | ||
test.pass("Button press event was heard"); | ||
test.done(); | ||
}); | ||
notify((jetpackID + "-cmdPressed"), "", "test-btn-listen"); | ||
|
||
loader.unload(); | ||
}; | ||
|
||
exports.testPrefRemoveListener = function(test) { | ||
test.waitUntilDone(); | ||
|
||
let loader = Loader(module); | ||
let sp = loader.require("simple-prefs"); | ||
let counter = 0; | ||
|
||
let listener = function() { | ||
test.pass("The prefs listener was not removed yet"); | ||
|
||
if (++counter > 1) | ||
test.fail("The prefs listener was not removed"); | ||
|
||
sp.removeListener("test-listen2", listener); | ||
|
||
sp.prefs["test-listen2"] = false; | ||
|
||
setTimeout(function() { | ||
test.pass("The prefs listener was removed"); | ||
loader.unload(); | ||
test.done(); | ||
}, 250); | ||
}; | ||
|
||
sp.on("test-listen2", listener); | ||
|
||
sp.prefs["test-listen2"] = true; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: is has -> is