Bug 1146724 - Use a SendingContext for WebChannels. r=MattN/markh

This commit is contained in:
Shane Tomlinson 2015-04-24 16:07:33 +10:00
parent dc08d9fe3d
commit 4b8dab2a11
4 changed files with 91 additions and 34 deletions

View File

@ -277,7 +277,7 @@ addEventListener("WebChannelMessageToChrome", function (e) {
let principal = e.target.nodePrincipal ? e.target.nodePrincipal : e.target.document.nodePrincipal;
if (e.detail) {
sendAsyncMessage("WebChannelMessageToChrome", e.detail, null, principal);
sendAsyncMessage("WebChannelMessageToChrome", e.detail, { eventTarget: e.target }, principal);
} else {
Cu.reportError("WebChannel message failed. No message detail.");
}
@ -286,12 +286,30 @@ addEventListener("WebChannelMessageToChrome", function (e) {
// Add message listener for "WebChannelMessageToContent" messages from chrome scripts
addMessageListener("WebChannelMessageToContent", function (e) {
if (e.data) {
content.dispatchEvent(new content.CustomEvent("WebChannelMessageToContent", {
detail: Cu.cloneInto({
id: e.data.id,
message: e.data.message,
}, content),
}));
// e.objects.eventTarget will be defined if sending a response to
// a WebChannelMessageToChrome event. An unsolicited send
// may not have an eventTarget defined, in this case send to the
// main content window.
let eventTarget = e.objects.eventTarget || content;
// if eventTarget is window then we want the document principal,
// otherwise use target itself.
let targetPrincipal = eventTarget instanceof Ci.nsIDOMWindow ? eventTarget.document.nodePrincipal : eventTarget.nodePrincipal;
if (e.principal.subsumes(targetPrincipal)) {
// if eventTarget is a window, use it as the targetWindow, otherwise
// find the window that owns the eventTarget.
let targetWindow = eventTarget instanceof Ci.nsIDOMWindow ? eventTarget : eventTarget.ownerDocument.defaultView;
eventTarget.dispatchEvent(new targetWindow.CustomEvent("WebChannelMessageToContent", {
detail: Cu.cloneInto({
id: e.data.id,
message: e.data.message,
}, targetWindow),
}));
} else {
Cu.reportError("WebChannel message failed. Principal mismatch.");
}
} else {
Cu.reportError("WebChannel message failed. No message data.");
}

View File

@ -163,14 +163,15 @@ this.FxAccountsOAuthClient.prototype = {
* Command webChannelId
* @param message {Object}
* Command message
* @param target {EventTarget}
* Channel message event target
* @param sendingContext {Object}
* Channel message event sendingContext
* @private
*/
let listener = function (webChannelId, message, target) {
let listener = function (webChannelId, message, sendingContext) {
if (message) {
let command = message.command;
let data = message.data;
let target = sendingContext && sendingContext.browser;
switch (command) {
case "oauth_complete":

View File

@ -66,11 +66,15 @@ let WebChannelBroker = Object.create({
*/
_listener: function (event) {
let data = event.data;
let sender = event.target;
let sendingContext = {
browser: event.target,
eventTarget: event.objects.eventTarget,
principal: event.principal,
};
if (data && data.id) {
if (!event.principal) {
this._sendErrorEventToContent(data.id, sender, "Message principal missing");
this._sendErrorEventToContent(data.id, sendingContext, "Message principal missing");
} else {
let validChannelFound = false;
data.message = data.message || {};
@ -79,13 +83,13 @@ let WebChannelBroker = Object.create({
if (channel.id === data.id &&
channel._originCheckCallback(event.principal)) {
validChannelFound = true;
channel.deliver(data, sender);
channel.deliver(data, sendingContext);
}
}
// if no valid origins send an event that there is no such valid channel
if (!validChannelFound) {
this._sendErrorEventToContent(data.id, sender, "No Such Channel");
this._sendErrorEventToContent(data.id, sendingContext, "No Such Channel");
}
}
} else {
@ -108,20 +112,24 @@ let WebChannelBroker = Object.create({
*
* @param id {String}
* The WebChannel id to include in the message
* @param sender {EventTarget}
* EventTarget with a "messageManager" that will send be used to send the message
* @param sendingContext {Object}
* Message sending context
* @param [errorMsg] {String}
* Error message
* @private
*/
_sendErrorEventToContent: function (id, sender, errorMsg) {
_sendErrorEventToContent: function (id, sendingContext, errorMsg) {
let { browser: targetBrowser, eventTarget, principal: targetPrincipal } = sendingContext;
errorMsg = errorMsg || "Web Channel Broker error";
if (sender.messageManager) {
sender.messageManager.sendAsyncMessage("WebChannelMessageToContent", {
if (targetBrowser && targetBrowser.messageManager) {
targetBrowser.messageManager.sendAsyncMessage("WebChannelMessageToContent", {
id: id,
error: errorMsg,
}, sender);
}, { eventTarget: eventTarget }, targetPrincipal);
} else {
Cu.reportError("Failed to send a WebChannel error. Target invalid.");
}
Cu.reportError(id.toString() + " error message. " + errorMsg);
},
@ -211,8 +219,17 @@ this.WebChannel.prototype = {
* The WebChannel id that was used for this message
* @param {Object} message
* The message itself
* @param {EventTarget} sender
* The source of the message
* @param sendingContext {Object}
* The sending context of the source of the message. Can be passed to
* `send` to respond to a message.
* @param sendingContext.browser {browser}
* The <browser> object that captured the
* WebChannelMessageToChrome.
* @param sendingContext.eventTarget {EventTarget}
* The <EventTarget> where the message was sent.
* @param sendingContext.principal {Principal}
* The <Principal> of the EventTarget where the
* message was sent.
*/
listen: function (callback) {
if (this._deliverCallback) {
@ -239,16 +256,27 @@ this.WebChannel.prototype = {
*
* @param message {Object}
* The message object that will be sent
* @param target {browser}
* The <browser> object that has a "messageManager" that sends messages
*
* @param target {Object}
* A <target> with the information of where to send the message.
* @param target.browser {browser}
* The <browser> object with a "messageManager" that will
* be used to send the message.
* @param target.principal {Principal}
* Principal of the target. Prevents messages from
* being dispatched to unexpected origins. The system principal
* can be specified to send to any target.
* @param [target.eventTarget] {EventTarget}
* Optional eventTarget within the browser, use to send to a
* specific element, e.g., an iframe.
*/
send: function (message, target) {
if (message && target && target.messageManager) {
target.messageManager.sendAsyncMessage("WebChannelMessageToContent", {
let { browser, principal, eventTarget } = target;
if (message && browser && browser.messageManager && principal) {
browser.messageManager.sendAsyncMessage("WebChannelMessageToContent", {
id: this.id,
message: message
});
}, { eventTarget }, principal);
} else if (!message) {
Cu.reportError("Failed to send a WebChannel message. Message not set.");
} else {
@ -261,18 +289,26 @@ this.WebChannel.prototype = {
*
* @param data {Object}
* Message data
* @param sender {browser}
* Message sender
* @param sendingContext {Object}
* Message sending context.
* @param sendingContext.browser {browser}
* The <browser> object that captured the
* WebChannelMessageToChrome.
* @param sendingContext.eventTarget {EventTarget}
* The <EventTarget> where the message was sent.
* @param sendingContext.principal {Principal}
* The <Principal> of the EventTarget where the message was sent.
*
*/
deliver: function(data, sender) {
deliver: function(data, sendingContext) {
if (this._deliverCallback) {
try {
this._deliverCallback(data.id, data.message, sender);
this._deliverCallback(data.id, data.message, sendingContext);
} catch (ex) {
this.send({
errno: ERRNO_UNKNOWN_ERROR,
error: ex.message ? ex.message : ERROR_UNKNOWN
}, sender);
}, sendingContext);
Cu.reportError("Failed to execute callback:" + ex);
}
} else {

View File

@ -77,7 +77,9 @@ add_task(function test_web_channel_broker_listener() {
},
principal: {
origin: URL_STRING
}
},
objects: {
},
};
WebChannelBroker._listener(mockEvent);