Skip to content
This repository has been archived by the owner on Feb 26, 2022. It is now read-only.

simple-prefs #270

Merged
merged 42 commits into from Nov 28, 2011
Merged
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
7efa45e
First working draft for inline preferences
peregrinogris Jun 8, 2011
0fb88ad
missing option type and first draft of the API
peregrinogris Jun 9, 2011
c38ece3
Working version of the simple preferences API
peregrinogris Jun 10, 2011
58fd871
Merge remote-tracking branch 'main/master' into preferences
peregrinogris Jun 10, 2011
7d2346f
Changed the property name in package.json to a more descriptive one
peregrinogris Jun 10, 2011
50ca0ac
Merge branch 'master' into preferences
peregrinogris Sep 12, 2011
1812ce5
Addressed review in preferences.js and removed the nesting in packag…
peregrinogris Sep 12, 2011
0edb23b
Merge branch 'master' into preferences
erikvold Nov 15, 2011
420aca5
minor whitespace changes to the preferences API
erikvold Nov 16, 2011
c75fc72
keep the icon and icon64 code together
erikvold Nov 16, 2011
bc9f47e
adding a couple of very simple tests in cfx testcfx for options.xul
erikvold Nov 16, 2011
e48f08b
fixing failing tests added in parent commit
erikvold Nov 16, 2011
e787ebf
add <em:optionsType> to install.rdf
erikvold Nov 16, 2011
4d6f92a
Merge branch 'master' into prefs
erikvold Nov 16, 2011
8e59e87
creating 'defaults/preferences/prefs.js' with default values (needs t…
erikvold Nov 17, 2011
fd07f2b
adding test to check that 'defaults/preferences/prefs.js' exists when…
erikvold Nov 17, 2011
85511f1
Loading default addon prefs on startup (workaround for bug 564675)
erikvold Nov 18, 2011
0329bcd
rename preferences.js module to prefs.js
erikvold Nov 18, 2011
e9d6b60
allow preferences to include a description
erikvold Nov 18, 2011
b697bdd
rename prefs.js to simple-prefs.js
erikvold Nov 18, 2011
2340b6d
adding simple set & get test for simple-prefs
erikvold Nov 18, 2011
bce02bf
simple-prefs module was not passing set & get test
erikvold Nov 18, 2011
5c75456
mention "preferences" key for package.json
erikvold Nov 18, 2011
5b2d8b9
- simple-prefs exports 'prefs' instead of 'simple'
erikvold Nov 18, 2011
903d908
adding simple-prefs test that complex values are not allowed
erikvold Nov 18, 2011
315c6de
adding simple docs for simple-prefs
erikvold Nov 18, 2011
10da2c0
preferences-service module already is throwing appropriate errors
erikvold Nov 19, 2011
299ec79
bug fix: need to make sure that numeric default values for prefs are …
erikvold Nov 19, 2011
234cb71
adding a simple event listener test for simple-prefs
erikvold Nov 19, 2011
af6cce0
minor changes to simple-prefs so that functions have the same code style
erikvold Nov 19, 2011
9435bb0
testing the removeListener method of the simple-prefs module
erikvold Nov 19, 2011
c163a67
adding some validation for options_xul.py (but I'm not sure if this i…
erikvold Nov 19, 2011
77b8f4f
- adding docs for the 'on' and 'removeListener' functions for simple-…
erikvold Nov 19, 2011
5c6d88f
adding a link to the MDN inline options page, and to the simple-prefs…
erikvold Nov 19, 2011
2e1835e
adding a example showing how to add preferences to a package.json
erikvold Nov 19, 2011
21f1cf0
adding a test for the button pressed event
erikvold Nov 21, 2011
e638955
Merge remote-tracking branch 'mozilla/master' into prefs
erikvold Nov 23, 2011
4cee3d5
adding tests for has and delete operations
erikvold Nov 24, 2011
ef31837
replacing the shim function to get `resourceURI` in pre Gecko 7.0.
erikvold Nov 24, 2011
56f4aae
making some refinements that were suggested to the simple-prefs docs
erikvold Nov 26, 2011
8bda536
- bug fix: validate prefs before creating the .options.xul file
erikvold Nov 26, 2011
b7a357a
fixing the test that was broken in the parent commit, and now the pac…
erikvold Nov 26, 2011
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions doc/dev-guide-source/addon-development/package-spec.md
Expand Up @@ -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`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: is has -> is

`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).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: a options.xul file -> an options.xul file

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.
Expand Down
65 changes: 65 additions & 0 deletions packages/addon-kit/docs/simple-prefs.md
@@ -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).
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor

Choose a reason for hiding this comment

The 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"
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The 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 preferences value should rather be an array of objects that have "name" keys.



<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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: add a semi-colon at the end of the statement.

Copy link
Contributor

Choose a reason for hiding this comment

The 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:

function onPrefChange(prefName) {
    console.log("The " + prefName + " preference changed.");
}
require("simple-prefs").on("somePreference", onPrefChange);
require("simple-prefs").on("someOtherPreference", onPrefChange);

});

@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>

107 changes: 107 additions & 0 deletions packages/addon-kit/lib/simple-prefs.js
@@ -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;
180 changes: 180 additions & 0 deletions packages/addon-kit/tests/test-simple-prefs.js
@@ -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;
};
4 changes: 4 additions & 0 deletions python-lib/cuddlefish/__init__.py
Expand Up @@ -721,6 +721,10 @@ def run(arguments=sys.argv[1:], target_cfg=None, pkg_cfg=None,
mydir = os.path.dirname(os.path.abspath(__file__))
app_extension_dir = os.path.join(mydir, "app-extension")


if target_cfg.get('preferences'):
harness_options['preferences'] = target_cfg.get('preferences')

harness_options['manifest'] = manifest.get_harness_options_manifest(uri_prefix)
harness_options['allTestModules'] = manifest.get_all_test_modules()

Expand Down