Bug 943251 - [app-manager] Add preference actor. r=jryans
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -1141,17 +1141,18 @@ let RemoteDebugger = {
getList: function() {
return promise.resolve([]);
}
},
// Use an explicit global actor list to prevent exposing
// unexpected actors
globalActorFactories: restrictPrivileges ? {
webappsActor: DebuggerServer.globalActorFactories.webappsActor,
- deviceActor: DebuggerServer.globalActorFactories.deviceActor
+ deviceActor: DebuggerServer.globalActorFactories.deviceActor,
+ preferenceActor: DebuggerServer.globalActorFactories.preferenceActor,
} : DebuggerServer.globalActorFactories
};
let root = new DebuggerServer.RootActor(connection, parameters);
root.applicationType = "operating-system";
return root;
};
#ifdef MOZ_WIDGET_GONK
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/server/actors/preference.js
@@ -0,0 +1,164 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const {Cc, Ci, Cu, CC} = require("chrome");
+const protocol = require("devtools/server/protocol");
+const {Arg, method, RetVal} = protocol;
+const promise = require("sdk/core/promise");
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import('resource://gre/modules/devtools/dbg-server.jsm');
+
+exports.register = function(handle) {
+ handle.addGlobalActor(PreferenceActor, "preferenceActor");
+};
+
+exports.unregister = function(handle) {
+};
+
+let PreferenceActor = protocol.ActorClass({
+ typeName: "preference",
+
+ _arePrefsAccessible: function() {
+ // We are using the authorization to debug certified apps as a proxy for the
+ // authorization to read and write preferences. We also perform this check
+ // inside every operation.
+ return !Services.prefs.getBoolPref("devtools.debugger.forbid-certified-apps");
+ },
+ arePrefsAccessible: method(function() {
+ return this._arePrefsAccessible();
+ }, {
+ request: {},
+ response: { value: RetVal("boolean") },
+ }),
+
+ getBoolPref: method(function(name) {
+ if (!this._arePrefsAccessible()) {
+ throw new Error("Operation not permitted");
+ }
+ return Services.prefs.getBoolPref(name);
+ }, {
+ request: { value: Arg(0) },
+ response: { value: RetVal("boolean") }
+ }),
+
+ getCharPref: method(function(name) {
+ if (!this._arePrefsAccessible()) {
+ throw new Error("Operation not permitted");
+ }
+ return Services.prefs.getCharPref(name);
+ }, {
+ request: { value: Arg(0) },
+ response: { value: RetVal("string") }
+ }),
+
+ getIntPref: method(function(name) {
+ if (!this._arePrefsAccessible()) {
+ throw new Error("Operation not permitted");
+ }
+ return Services.prefs.getIntPref(name);
+ }, {
+ request: { value: Arg(0) },
+ response: { value: RetVal("number") }
+ }),
+
+ getAllPrefs: method(function() {
+ if (!this._arePrefsAccessible()) {
+ throw new Error("Operation not permitted");
+ }
+ let prefs = {};
+ Services.prefs.getChildList("").forEach(function(name, index) {
+ // append all key/value pairs into a huge json object.
+ try {
+ let value;
+ switch (Services.prefs.getPrefType(name)) {
+ case Ci.nsIPrefBranch.PREF_STRING:
+ value = Services.prefs.getCharPref(name);
+ break;
+ case Ci.nsIPrefBranch.PREF_INT:
+ value = Services.prefs.getIntPref(name);
+ break;
+ case Ci.nsIPrefBranch.PREF_BOOL:
+ value = Services.prefs.getBoolPref(name);
+ break;
+ default:
+ }
+ prefs[name] = {
+ value: value,
+ hasUserValue: Services.prefs.prefHasUserValue(name)
+ };
+ } catch (e) {
+ // pref exists but has no user or default value
+ }
+ });
+ return prefs;
+ }, {
+ request: {},
+ response: { value: RetVal("json") }
+ }),
+
+ setBoolPref: method(function(name, value) {
+ if (!this._arePrefsAccessible()) {
+ throw new Error("Operation not permitted");
+ }
+ Services.prefs.setBoolPref(name, value);
+ Services.prefs.savePrefFile(null);
+ }, {
+ request: { name: Arg(0), value: Arg(1) },
+ response: {}
+ }),
+
+ setCharPref: method(function(name, value) {
+ if (!this._arePrefsAccessible()) {
+ throw new Error("Operation not permitted");
+ }
+ Services.prefs.setCharPref(name, value);
+ Services.prefs.savePrefFile(null);
+ }, {
+ request: { name: Arg(0), value: Arg(1) },
+ response: {}
+ }),
+
+ setIntPref: method(function(name, value) {
+ if (!this._arePrefsAccessible()) {
+ throw new Error("Operation not permitted");
+ }
+ Services.prefs.setIntPref(name, value);
+ Services.prefs.savePrefFile(null);
+ }, {
+ request: { name: Arg(0), value: Arg(1) },
+ response: {}
+ }),
+
+ clearUserPref: method(function(name) {
+ if (!this._arePrefsAccessible()) {
+ throw new Error("Operation not permitted");
+ }
+ Services.prefs.clearUserPref(name);
+ Services.prefs.savePrefFile(null);
+ }, {
+ request: { name: Arg(0) },
+ response: {}
+ }),
+});
+
+let PreferenceFront = protocol.FrontClass(PreferenceActor, {
+ initialize: function(client, form) {
+ protocol.Front.prototype.initialize.call(this, client);
+ this.actorID = form.preferenceActor;
+ client.addActorPool(this);
+ this.manage(this);
+ },
+});
+
+const _knownPreferenceFronts = new WeakMap();
+
+exports.getPreferenceFront = function(client, form) {
+ if (_knownPreferenceFronts.has(client))
+ return _knownPreferenceFronts.get(client);
+
+ let front = new PreferenceFront(client, form);
+ _knownPreferenceFronts.set(client, front);
+ return front;
+};
--- a/toolkit/devtools/server/main.js
+++ b/toolkit/devtools/server/main.js
@@ -355,16 +355,17 @@ var DebuggerServer = {
if (!restrictPrivileges) {
this.addTabActors();
this.addGlobalActor(this.ChromeDebuggerActor, "chromeDebugger");
}
this.addActors("resource://gre/modules/devtools/server/actors/webapps.js");
this.registerModule("devtools/server/actors/device");
+ this.registerModule("devtools/server/actors/preference");
},
/**
* Install tab actors in documents loaded in content childs
*/
addChildActors: function () {
// In case of apps being loaded in parent process, DebuggerServer is already
// initialized and browser actors are already loaded,
--- a/toolkit/devtools/server/tests/mochitest/chrome.ini
+++ b/toolkit/devtools/server/tests/mochitest/chrome.ini
@@ -37,8 +37,9 @@ support-files =
[test_styles-computed.html]
[test_styles-matched.html]
[test_styles-modify.html]
[test_styles-svg.html]
[test_unsafeDereference.html]
[test_evalInGlobal-outerized_this.html]
[test_inspector_getImageData.html]
[test_memory.html]
+[test_preference.html]
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/server/tests/mochitest/test_preference.html
@@ -0,0 +1,114 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Bug 943251 - Allow accessing about:config from app-manager
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test Preference Actor</title>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script>
+
+function runTests() {
+ var Cu = Components.utils;
+ var Cc = Components.classes;
+ var Ci = Components.interfaces;
+
+ Cu.import("resource://gre/modules/devtools/Loader.jsm");
+ Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
+ Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
+ Cu.import("resource://gre/modules/Services.jsm");
+
+ SimpleTest.waitForExplicitFinish();
+
+ var {getPreferenceFront} = devtools.require("devtools/server/actors/preference");
+
+ DebuggerServer.init(function () { return true; });
+ DebuggerServer.addBrowserActors();
+
+ var client = new DebuggerClient(DebuggerServer.connectPipe());
+ client.connect(function onConnect() {
+ client.listTabs(function onListTabs(aResponse) {
+ var p = getPreferenceFront(client, aResponse);
+
+ var prefs = {};
+
+ var localPref = {
+ accessible: true,
+ boolPref: true,
+ intPref: 0x1234,
+ charPref: "Hello World",
+ };
+
+
+ function checkValues() {
+ is(prefs.accessible, localPref.accessible, "preference is accessible");
+ is(prefs.boolPref, localPref.boolPref, "read/write bool pref");
+ is(prefs.intPref, localPref.intPref, "read/write int pref");
+ is(prefs.charPref, localPref.charPref, "read/write string pref");
+
+ for (var key in prefs.allPrefs) {
+ var expectedValue;
+ switch(Services.prefs.getPrefType(key)) {
+ case Ci.nsIPrefBranch.PREF_STRING:
+ expectedValue = Services.prefs.getCharPref(key);
+ break;
+ case Ci.nsIPrefBranch.PREF_INT:
+ expectedValue = Services.prefs.getIntPref(key);
+ break;
+ case Ci.nsIPrefBranch.PREF_BOOL:
+ expectedValue = Services.prefs.getBoolPref(key);
+ break;
+ default:
+ ok(false, "unexpected pref type (" + key + ")");
+ break;
+ }
+
+ is(prefs.allPrefs[key].value, expectedValue, "valid preference value (" + key + ")");
+ is(prefs.allPrefs[key].hasUserValue, Services.prefs.prefHasUserValue(key), "valid hasUserValue (" + key + ")");
+ }
+
+ ["test.bool", "test.int", "test.string"].forEach(function(key) {
+ is(Services.prefs.getPrefType(key), Ci.nsIPrefBranch.PREF_INVALID, "pref (" + key + ") is clear");
+ });
+
+ client.close(() => {
+ DebuggerServer.destroy();
+ SimpleTest.finish()
+ });
+ }
+
+
+ p.arePrefsAccessible().then((value) => prefs["accessible"] = value)
+ .then(() => p.getAllPrefs()).then((json) => prefs["allPrefs"] = json)
+ .then(() => p.setBoolPref("test.bool", localPref.boolPref))
+ .then(() => p.setIntPref("test.int", localPref.intPref))
+ .then(() => p.setCharPref("test.string", localPref.charPref))
+ .then(() => p.getBoolPref("test.bool")).then((value) => prefs["boolPref"] = value)
+ .then(() => p.getIntPref("test.int")).then((value) => prefs["intPref"] = value)
+ .then(() => p.getCharPref("test.string")).then((value) => prefs["charPref"] = value)
+ .then(() => p.clearUserPref("test.bool"))
+ .then(() => p.clearUserPref("test.int"))
+ .then(() => p.clearUserPref("test.string"))
+ .then(checkValues);
+
+ });
+ });
+
+}
+
+window.onload = function () {
+ SpecialPowers.pushPrefEnv({
+ "set": [
+ ["devtools.debugger.forbid-certified-apps", false],
+ ]
+ }, runTests);
+}
+</script>
+</pre>
+</body>
+</html>