Bug 914633 - Implement SpecialPowers.loadChromeScript to easily add new chrome features without having to modify SpecialPowers. r=ted

This commit is contained in:
Alexandre Poirot 2013-09-14 22:09:13 -04:00
Родитель c23595f345
Коммит 756c89efbe
8 изменённых файлов: 148 добавлений и 0 удалений

Просмотреть файл

@ -46,6 +46,8 @@ if (outOfProcess) {
mm.addMessageListener("SpecialPowers.Quit", specialPowersObserver);
mm.addMessageListener("SpecialPowers.Focus", specialPowersObserver);
mm.addMessageListener("SPPermissionManager", specialPowersObserver);
mm.addMessageListener("SPLoadChromeScript", specialPowersObserver);
mm.addMessageListener("SPChromeScriptMessage", specialPowersObserver);
mm.loadFrameScript(CHILD_LOGGER_SCRIPT, true);
mm.loadFrameScript(CHILD_SCRIPT_API, true);

Просмотреть файл

@ -16,6 +16,8 @@ _TEST_FILES = \
test_SpecialPowersPushPrefEnv.html \
file_SpecialPowersFrame1.html \
test_SimpleTestGetTestFileURL.html \
test_SpecialPowersLoadChromeScript.html \
SpecialPowersLoadChromeScript.js \
test_bug816847.html \
test_sanity_cleanup.html \
test_sanity_cleanup2.html \

Просмотреть файл

@ -0,0 +1,6 @@
// Just receive 'foo' message and forward it back
// as 'bar' message
addMessageListener("foo", function (message) {
sendAsyncMessage("bar", message);
});

Просмотреть файл

@ -0,0 +1,28 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for SpecialPowers extension</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
var url = SimpleTest.getTestFileURL("SpecialPowersLoadChromeScript.js");
var script = SpecialPowers.loadChromeScript(url);
var MESSAGE = { bar: true };
script.addMessageListener("bar", function (message) {
is(JSON.stringify(message), JSON.stringify(MESSAGE));
script.destroy();
SimpleTest.finish();
});
script.sendAsyncMessage("foo", MESSAGE);
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -59,6 +59,8 @@ SpecialPowersObserver.prototype = new SpecialPowersObserverAPI();
this._messageManager.addMessageListener("SPPermissionManager", this);
this._messageManager.addMessageListener("SPWebAppService", this);
this._messageManager.addMessageListener("SPObserverService", this);
this._messageManager.addMessageListener("SPLoadChromeScript", this);
this._messageManager.addMessageListener("SPChromeScriptMessage", this);
this._messageManager.loadFrameScript(CHILD_LOGGER_SCRIPT, true);
this._messageManager.loadFrameScript(CHILD_SCRIPT_API, true);

Просмотреть файл

@ -23,6 +23,7 @@ SpecialPowersException.prototype.toString = function() {
this.SpecialPowersObserverAPI = function SpecialPowersObserverAPI() {
this._crashDumpDir = null;
this._processCrashObserversRegistered = false;
this._chromeScriptListeners = [];
}
function parseKeyValuePairs(text) {
@ -288,6 +289,55 @@ SpecialPowersObserverAPI.prototype = {
}
break;
case "SPLoadChromeScript":
var url = aMessage.json.url;
var id = aMessage.json.id;
// Fetch script content as we can't use scriptloader's loadSubScript
// to evaluate http:// urls...
var scriptableStream = Cc["@mozilla.org/scriptableinputstream;1"]
.getService(Ci.nsIScriptableInputStream);
var channel = Services.io.newChannel(url, null, null);
var input = channel.open();
scriptableStream.init(input);
var jsScript = scriptableStream.read(input.available());
scriptableStream.close();
input.close();
// Setup a chrome sandbox that has access to sendAsyncMessage
// and addMessageListener in order to communicate with
// the mochitest.
var systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
var sb = Components.utils.Sandbox(systemPrincipal);
var mm = aMessage.target
.QueryInterface(Ci.nsIFrameLoaderOwner)
.frameLoader
.messageManager;
sb.sendAsyncMessage = (name, message) => {
mm.sendAsyncMessage("SPChromeScriptMessage",
{ id: id, name: name, message: message });
};
sb.addMessageListener = (name, listener) => {
this._chromeScriptListeners.push({ id: id, name: name, listener: listener });
};
// Evaluate the chrome script
try {
Components.utils.evalInSandbox(jsScript, sb, "1.8", url, 1);
} catch(e) {
throw new SpecialPowersException("Error while executing chrome " +
"script '" + url + "':\n" + e);
}
break;
case "SPChromeScriptMessage":
var id = aMessage.json.id;
var name = aMessage.json.name;
var message = aMessage.json.message;
this._chromeScriptListeners
.filter(o => (o.name == name && o.id == id))
.forEach(o => o.listener(message));
break;
default:
throw new SpecialPowersException("Unrecognized Special Powers API");
}

Просмотреть файл

@ -40,6 +40,14 @@ SpecialPowers.prototype._sendAsyncMessage = function(msgname, msg) {
sendAsyncMessage(msgname, msg);
};
SpecialPowers.prototype._addMessageListener = function(msgname, listener) {
addMessageListener(msgname, listener);
};
SpecialPowers.prototype._removeMessageListener = function(msgname, listener) {
removeMessageListener(msgname, listener);
};
SpecialPowers.prototype.registerProcessCrashObservers = function() {
addMessageListener("SPProcessCrashService", this._messageListener);
sendSyncMessage("SPProcessCrashService", { op: "register-observer" });

Просмотреть файл

@ -494,6 +494,56 @@ SpecialPowersAPI.prototype = {
return MockPermissionPrompt
},
loadChromeScript: function (url) {
// Create a unique id for this chrome script
let uuidGenerator = Cc["@mozilla.org/uuid-generator;1"]
.getService(Ci.nsIUUIDGenerator);
let id = uuidGenerator.generateUUID().toString();
// Tells chrome code to evaluate this chrome script
this._sendSyncMessage("SPLoadChromeScript",
{ url: url, id: id });
// Returns a MessageManager like API in order to be
// able to communicate with this chrome script
let listeners = [];
let chromeScript = {
addMessageListener: (name, listener) => {
listeners.push({ name: name, listener: listener });
},
removeMessageListener: (name, listener) => {
listeners = listeners.filter(
o => (o.name != name || o.listener != listener)
);
},
sendAsyncMessage: (name, message) => {
this._sendSyncMessage("SPChromeScriptMessage",
{ id: id, name: name, message: message });
},
destroy: () => {
listeners = [];
this._removeMessageListener("SPChromeScriptMessage", chromeScript);
},
receiveMessage: (aMessage) => {
let messageId = aMessage.json.id;
let name = aMessage.json.name;
let message = aMessage.json.message;
// Ignore message from other chrome script
if (messageId != id)
return;
listeners.filter(o => (o.name == name))
.forEach(o => o.listener(this.wrap(message)));
}
};
this._addMessageListener("SPChromeScriptMessage", chromeScript);
return this.wrap(chromeScript);
},
get Services() {
return wrapPrivileged(Services);
},