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

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

View File

@@ -566,7 +566,7 @@ addEventListener("WebChannelMessageToChrome", function (e) {
let principal = e.target.nodePrincipal ? e.target.nodePrincipal : e.target.document.nodePrincipal; let principal = e.target.nodePrincipal ? e.target.nodePrincipal : e.target.document.nodePrincipal;
if (e.detail) { if (e.detail) {
sendAsyncMessage("WebChannelMessageToChrome", e.detail, null, principal); sendAsyncMessage("WebChannelMessageToChrome", e.detail, { eventTarget: e.target }, principal);
} else { } else {
Cu.reportError("WebChannel message failed. No message detail."); Cu.reportError("WebChannel message failed. No message detail.");
} }
@@ -575,12 +575,30 @@ addEventListener("WebChannelMessageToChrome", function (e) {
// Add message listener for "WebChannelMessageToContent" messages from chrome scripts // Add message listener for "WebChannelMessageToContent" messages from chrome scripts
addMessageListener("WebChannelMessageToContent", function (e) { addMessageListener("WebChannelMessageToContent", function (e) {
if (e.data) { if (e.data) {
content.dispatchEvent(new content.CustomEvent("WebChannelMessageToContent", { // 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({ detail: Cu.cloneInto({
id: e.data.id, id: e.data.id,
message: e.data.message, message: e.data.message,
}, content), }, targetWindow),
})); }));
} else {
Cu.reportError("WebChannel message failed. Principal mismatch.");
}
} else { } else {
Cu.reportError("WebChannel message failed. No message data."); Cu.reportError("WebChannel message failed. No message data.");
} }

View File

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

View File

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

View File

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