Bug 835352 - Forward content preference change notifications to child processes. r=mak

This commit is contained in:
Gabriele Svelto 2013-01-31 18:43:56 +01:00
parent 2d6f3e7bea
commit 2852357eff
2 changed files with 88 additions and 29 deletions

View File

@ -195,7 +195,7 @@ ContentPrefService2.prototype = {
if (context && context.usePrivateBrowsing) {
this._pbStore.set(group, name, value);
this._schedule(function () {
this._cps._notifyPrefSet(group, name, value);
this._cps._broadcastPrefSet(group, name, value);
cbHandleCompletion(callback, Ci.nsIContentPrefCallback2.COMPLETE_OK);
});
return;
@ -260,7 +260,7 @@ ContentPrefService2.prototype = {
onDone: function onDone(reason, ok) {
if (ok) {
this._cache.setWithCast(group, name, value);
this._cps._notifyPrefSet(group, name, value);
this._cps._broadcastPrefSet(group, name, value);
}
cbHandleCompletion(callback, reason);
},
@ -343,7 +343,7 @@ ContentPrefService2.prototype = {
}
}
for (let [sgroup, , ] in prefs) {
this._cps._notifyPrefRemoved(sgroup, name);
this._cps._broadcastPrefRemoved(sgroup, name);
}
}
cbHandleCompletion(callback, reason);
@ -429,7 +429,7 @@ ContentPrefService2.prototype = {
}
}
for (let [sgroup, sname, ] in prefs) {
this._cps._notifyPrefRemoved(sgroup, sname);
this._cps._broadcastPrefRemoved(sgroup, sname);
}
}
cbHandleCompletion(callback, reason);
@ -481,7 +481,7 @@ ContentPrefService2.prototype = {
this._pbStore.removeGrouped();
}
for (let [sgroup, sname, ] in prefs) {
this._cps._notifyPrefRemoved(sgroup, sname);
this._cps._broadcastPrefRemoved(sgroup, sname);
}
}
cbHandleCompletion(callback, reason);
@ -554,7 +554,7 @@ ContentPrefService2.prototype = {
}
}
for (let [sgroup, , ] in prefs) {
this._cps._notifyPrefRemoved(sgroup, name);
this._cps._broadcastPrefRemoved(sgroup, name);
}
}
cbHandleCompletion(callback, reason);

View File

@ -9,6 +9,25 @@ const Cu = Components.utils;
const CACHE_MAX_GROUP_ENTRIES = 100;
// We have a whitelist for getting/setting. This is because
// there are potential privacy issues with a compromised
// content process checking the user's content preferences
// and using that to discover all the websites visited, etc.
// Also there are both potential race conditions (if two processes
// set more than one value in succession, and the values
// only make sense together), as well as security issues, if
// a compromised content process can send arbitrary setPref
// messages. The whitelist contains only those settings that
// are not at risk for either.
// We currently whitelist saving/reading the last directory of file
// uploads, and the last current spellchecker dictionary which are so far
// the only need we have identified.
const REMOTE_WHITELIST = [
"browser.upload.lastDir",
"spellcheck.lang",
];
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
/**
@ -26,25 +45,15 @@ function electrolify(service) {
Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
// Parent process
service.messageManager = Cc["@mozilla.org/parentprocessmessagemanager;1"].
getService(Ci.nsIMessageBroadcaster);
// Setup listener for child messages. We don't need to call
// addMessageListener as the wakeup service will do that for us.
service.receiveMessage = function(aMessage) {
var json = aMessage.json;
// We have a whitelist for getting/setting. This is because
// there are potential privacy issues with a compromised
// content process checking the user's content preferences
// and using that to discover all the websites visited, etc.
// Also there are both potential race conditions (if two processes
// set more than one value in succession, and the values
// only make sense together), as well as security issues, if
// a compromised content process can send arbitrary setPref
// messages. The whitelist contains only those settings that
// are not at risk for either.
// We currently whitelist saving/reading the last directory of file
// uploads, and the last current spellchecker dictionary which are so far
// the only need we have identified.
const NAME_WHITELIST = ["browser.upload.lastDir", "spellcheck.lang"];
if (NAME_WHITELIST.indexOf(json.name) == -1)
if (REMOTE_WHITELIST.indexOf(json.name) == -1)
return { succeeded: false };
switch (aMessage.name) {
@ -88,6 +97,19 @@ function electrolify(service) {
return ret.value;
};
});
// Listen to preference change notifications from the parent and notify
// observers in the child process according to the change
service.messageManager.addMessageListener("ContentPref:notifyPrefSet",
function(aMessage) {
var json = aMessage.json;
service._notifyPrefSet(json.group, json.name, json.value);
});
service.messageManager.addMessageListener("ContentPref:notifyPrefRemoved",
function(aMessage) {
var json = aMessage.json;
service._notifyPrefRemoved(json.group, json.name);
});
}
}
@ -328,7 +350,7 @@ ContentPrefService.prototype = {
if (aContext && aContext.usePrivateBrowsing) {
this._privModeStorage.setWithCast(group, aName, aValue);
this._notifyPrefSet(group, aName, aValue);
this._broadcastPrefSet(group, aName, aValue);
return;
}
@ -350,8 +372,7 @@ ContentPrefService.prototype = {
this._insertPref(groupID, settingID, aValue);
this._cache.setWithCast(group, aName, aValue);
this._notifyPrefSet(group, aName, aValue);
this._broadcastPrefSet(group, aName, aValue);
},
hasPref: function ContentPrefService_hasPref(aGroup, aName, aContext) {
@ -379,7 +400,7 @@ ContentPrefService.prototype = {
if (aContext && aContext.usePrivateBrowsing) {
this._privModeStorage.remove(group, aName);
this._notifyPrefRemoved(group, aName);
this._broadcastPrefRemoved(group, aName);
return;
}
@ -402,7 +423,7 @@ ContentPrefService.prototype = {
this._deleteGroupIfUnused(groupID);
this._cache.remove(group, aName);
this._notifyPrefRemoved(group, aName);
this._broadcastPrefRemoved(group, aName);
},
removeGroupedPrefs: function ContentPrefService_removeGroupedPrefs(aContext) {
@ -437,7 +458,7 @@ ContentPrefService.prototype = {
for (let [group, name, ] in this._privModeStorage) {
if (name === aName) {
this._privModeStorage.remove(group, aName);
this._notifyPrefRemoved(group, aName);
this._broadcastPrefRemoved(group, aName);
}
}
}
@ -479,7 +500,7 @@ ContentPrefService.prototype = {
if (groupNames[i]) // ie. not null, which will be last (and i == groupIDs.length)
this._deleteGroupIfUnused(groupIDs[i]);
if (!aContext || !aContext.usePrivateBrowsing) {
this._notifyPrefRemoved(groupNames[i], aName);
this._broadcastPrefRemoved(groupNames[i], aName);
}
}
},
@ -570,6 +591,9 @@ ContentPrefService.prototype = {
return observers;
},
/**
* Notify all observers about the removal of a preference.
*/
_notifyPrefRemoved: function ContentPrefService__notifyPrefRemoved(aGroup, aName) {
for each (var observer in this._getObservers(aName)) {
try {
@ -581,6 +605,9 @@ ContentPrefService.prototype = {
}
},
/**
* Notify all observers about a preference change.
*/
_notifyPrefSet: function ContentPrefService__notifyPrefSet(aGroup, aName, aValue) {
for each (var observer in this._getObservers(aName)) {
try {
@ -592,6 +619,38 @@ ContentPrefService.prototype = {
}
},
/**
* Notify all observers in the current process about the removal of a
* preference and send a message to all other processes so that they can in
* turn notify their observers about the change. This is meant to be called
* only in the parent process. Only whitelisted preferences are broadcast to
* the child processes.
*/
_broadcastPrefRemoved: function ContentPrefService__broadcastPrefRemoved(aGroup, aName) {
this._notifyPrefRemoved(aGroup, aName);
if (REMOTE_WHITELIST.indexOf(aName) != -1) {
this.messageManager.broadcastAsyncMessage('ContentPref:notifyPrefRemoved',
{ "group": aGroup, "name": aName } );
}
},
/**
* Notify all observers in the current process about a preference change and
* send a message to all other processes so that they can in turn notify
* their observers about the change. This is meant to be called only in the
* parent process. Only whitelisted preferences are broadcast to the child
* processes.
*/
_broadcastPrefSet: function ContentPrefService__broadcastPrefSet(aGroup, aName, aValue) {
this._notifyPrefSet(aGroup, aName, aValue);
if (REMOTE_WHITELIST.indexOf(aName) != -1) {
this.messageManager.broadcastAsyncMessage('ContentPref:notifyPrefSet',
{ "group": aGroup, "name": aName, "value": aValue } );
}
},
_grouper: null,
get grouper() {
if (!this._grouper)