mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
Bug 1238128 - Ensure that the details passed to WebChannelMessageToChrome is a string, with a whitelist for messages from existing users r=Margaret,markh,MattN
MozReview-Commit-ID: DpdJ5bUcBdQ --HG-- extra : rebase_source : b240484b44e9040b2d4e8509589f288e896a4267
This commit is contained in:
parent
5b39e168bb
commit
838730ae1b
@ -1454,3 +1454,6 @@ pref("signon.schemeUpgrades", true);
|
||||
// Enable the "Simplify Page" feature in Print Preview
|
||||
pref("print.use_simplify_page", true);
|
||||
|
||||
// Space separated list of URLS that are allowed to send objects (instead of
|
||||
// only strings) through webchannels.
|
||||
pref("webchannel.allowObject.urlWhitelist", "https://accounts.firefox.com https://content.cdn.mozilla.net https://hello.firefox.com https://input.mozilla.org https://support.mozilla.org https://install.mozilla.org");
|
||||
|
@ -8,6 +8,8 @@
|
||||
<script>
|
||||
window.onload = function(){
|
||||
var event = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
// Note: This intentionally sends an object instead of a string, to ensure both work
|
||||
// (see browser_fxa_oauth_with_keys.html for the other test)
|
||||
detail: {
|
||||
id: "oauth_client_id",
|
||||
message: {
|
||||
|
@ -309,9 +309,17 @@ function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
Task.spawn(function () {
|
||||
for (let test of gTests) {
|
||||
info("Running: " + test.desc);
|
||||
yield test.run();
|
||||
const webchannelWhitelistPref = "webchannel.allowObject.urlWhitelist";
|
||||
let origWhitelist = Services.prefs.getCharPref(webchannelWhitelistPref);
|
||||
let newWhitelist = origWhitelist + " http://example.com";
|
||||
Services.prefs.setCharPref(webchannelWhitelistPref, newWhitelist);
|
||||
try {
|
||||
for (let test of gTests) {
|
||||
info("Running: " + test.desc);
|
||||
yield test.run();
|
||||
}
|
||||
} finally {
|
||||
Services.prefs.clearUserPref(webchannelWhitelistPref);
|
||||
}
|
||||
}).then(finish, ex => {
|
||||
Assert.ok(false, "Unexpected Exception: " + ex);
|
||||
|
@ -8,7 +8,9 @@
|
||||
<script>
|
||||
window.onload = function(){
|
||||
var event = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
// Note: This intentionally sends a string instead of an object, to ensure both work
|
||||
// (see browser_fxa_oauth.html for the other test)
|
||||
detail: JSON.stringify({
|
||||
id: "oauth_client_id",
|
||||
message: {
|
||||
command: "oauth_complete",
|
||||
@ -21,7 +23,7 @@
|
||||
keys: { kAr: { k: 'kAr' }, kBr: { k: 'kBr' }},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
window.dispatchEvent(event);
|
||||
|
@ -32,7 +32,7 @@
|
||||
|
||||
function test_profile_change() {
|
||||
var event = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: webChannelId,
|
||||
message: {
|
||||
command: "profile:change",
|
||||
@ -40,7 +40,7 @@
|
||||
uid: "abc123",
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
window.dispatchEvent(event);
|
||||
@ -48,7 +48,7 @@
|
||||
|
||||
function test_login() {
|
||||
var event = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: webChannelId,
|
||||
message: {
|
||||
command: "fxaccounts:login",
|
||||
@ -63,7 +63,7 @@
|
||||
},
|
||||
messageId: 1,
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
window.dispatchEvent(event);
|
||||
@ -75,17 +75,17 @@
|
||||
// fxaccounts_webchannel_response_echo WebChannel. The tests are
|
||||
// listening for events and do the appropriate checks.
|
||||
var event = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: 'fxaccounts_webchannel_response_echo',
|
||||
message: e.detail.message,
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
window.dispatchEvent(event);
|
||||
}, true);
|
||||
|
||||
var event = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: webChannelId,
|
||||
message: {
|
||||
command: "fxaccounts:can_link_account",
|
||||
@ -94,7 +94,7 @@
|
||||
},
|
||||
messageId: 2,
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
window.dispatchEvent(event);
|
||||
@ -102,7 +102,7 @@
|
||||
|
||||
function test_logout() {
|
||||
var event = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: webChannelId,
|
||||
message: {
|
||||
command: "fxaccounts:logout",
|
||||
@ -111,7 +111,7 @@
|
||||
},
|
||||
messageId: 3,
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
window.dispatchEvent(event);
|
||||
@ -119,7 +119,7 @@
|
||||
|
||||
function test_delete() {
|
||||
var event = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: webChannelId,
|
||||
message: {
|
||||
command: "fxaccounts:delete",
|
||||
@ -128,7 +128,7 @@
|
||||
},
|
||||
messageId: 4,
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
window.dispatchEvent(event);
|
||||
|
@ -7,6 +7,7 @@ var {WebChannel} = Cu.import("resource://gre/modules/WebChannel.jsm", {});
|
||||
const TEST_URL_TAIL = "example.com/browser/browser/base/content/test/general/test_remoteTroubleshoot.html"
|
||||
const TEST_URI_GOOD = Services.io.newURI("https://" + TEST_URL_TAIL, null, null);
|
||||
const TEST_URI_BAD = Services.io.newURI("http://" + TEST_URL_TAIL, null, null);
|
||||
const TEST_URI_GOOD_OBJECT = Services.io.newURI("https://" + TEST_URL_TAIL + "?object", null, null);
|
||||
|
||||
// Creates a one-shot web-channel for the test data to be sent back from the test page.
|
||||
function promiseChannelResponse(channelID, originOrPermission) {
|
||||
@ -78,4 +79,15 @@ add_task(function*() {
|
||||
// Now a http:// URI - should get nothing even with the permission setup.
|
||||
got = yield promiseNewChannelResponse(TEST_URI_BAD);
|
||||
Assert.ok(got.message === undefined, "should have failed to get any data");
|
||||
|
||||
// Check that the page can send an object as well if it's in the whitelist
|
||||
let webchannelWhitelistPref = "webchannel.allowObject.urlWhitelist";
|
||||
let origWhitelist = Services.prefs.getCharPref(webchannelWhitelistPref);
|
||||
let newWhitelist = origWhitelist + " https://example.com";
|
||||
Services.prefs.setCharPref(webchannelWhitelistPref, newWhitelist);
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref(webchannelWhitelistPref);
|
||||
});
|
||||
got = yield promiseNewChannelResponse(TEST_URI_GOOD_OBJECT);
|
||||
Assert.ok(got.message, "should have gotten some data back");
|
||||
});
|
||||
|
@ -33,6 +33,9 @@
|
||||
case "bubbles":
|
||||
test_bubbles();
|
||||
break;
|
||||
case "object":
|
||||
test_object();
|
||||
break;
|
||||
default:
|
||||
throw new Error(`INVALID TEST NAME ${testName}`);
|
||||
break;
|
||||
@ -41,14 +44,14 @@
|
||||
|
||||
function test_generic() {
|
||||
var event = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: "generic",
|
||||
message: {
|
||||
something: {
|
||||
nested: "hello",
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
window.dispatchEvent(event);
|
||||
@ -56,23 +59,23 @@
|
||||
|
||||
function test_twoWay() {
|
||||
var firstMessage = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: "twoway",
|
||||
message: {
|
||||
command: "one",
|
||||
},
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
window.addEventListener("WebChannelMessageToContent", function(e) {
|
||||
var secondMessage = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: "twoway",
|
||||
message: {
|
||||
command: "two",
|
||||
detail: e.detail.message,
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
if (!e.detail.message.error) {
|
||||
@ -85,17 +88,17 @@
|
||||
|
||||
function test_multichannel() {
|
||||
var event1 = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: "wrongchannel",
|
||||
message: {},
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
var event2 = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: "multichannel",
|
||||
message: {},
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
window.dispatchEvent(event1);
|
||||
@ -132,12 +135,12 @@
|
||||
|
||||
function test_bubbles() {
|
||||
var event = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: "not_a_window",
|
||||
message: {
|
||||
command: "start"
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
var nonWindowTarget = document.getElementById("not_a_window");
|
||||
@ -150,12 +153,32 @@
|
||||
nonWindowTarget.dispatchEvent(event);
|
||||
}
|
||||
|
||||
function test_object() {
|
||||
let objectMessage = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
id: "objects",
|
||||
message: { type: "object" }
|
||||
}
|
||||
});
|
||||
|
||||
let stringMessage = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: JSON.stringify({
|
||||
id: "objects",
|
||||
message: { type: "string" }
|
||||
})
|
||||
});
|
||||
// Test fails if objectMessage is received, we send stringMessage to know
|
||||
// when we should stop listening for objectMessage
|
||||
window.dispatchEvent(objectMessage);
|
||||
window.dispatchEvent(stringMessage);
|
||||
}
|
||||
|
||||
function echoEventToChannel(e, channelId) {
|
||||
var echoedEvent = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: channelId,
|
||||
message: e.detail.message,
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
e.target.dispatchEvent(echoedEvent);
|
||||
|
@ -44,8 +44,8 @@ var gTests = [
|
||||
let channel = new WebChannel("twoway", Services.io.newURI(HTTP_PATH, null, null));
|
||||
|
||||
channel.listen(function (id, message, sender) {
|
||||
is(id, "twoway");
|
||||
ok(message.command);
|
||||
is(id, "twoway", "bad id");
|
||||
ok(message.command, "command not ok");
|
||||
|
||||
if (message.command === "one") {
|
||||
channel.send({ data: { nested: true } }, sender);
|
||||
@ -74,8 +74,8 @@ var gTests = [
|
||||
});
|
||||
|
||||
iframeChannel.listen(function (id, message, sender) {
|
||||
is(id, "twoway");
|
||||
ok(message.command);
|
||||
is(id, "twoway", "bad id (2)");
|
||||
ok(message.command, "command not ok (2)");
|
||||
|
||||
if (message.command === "one") {
|
||||
iframeChannel.send({ data: { nested: true } }, sender);
|
||||
@ -328,6 +328,75 @@ var gTests = [
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: "WebChannel disallows non-string message from non-whitelisted origin",
|
||||
run: function* () {
|
||||
/**
|
||||
* This test ensures that non-string messages can't be sent via WebChannels.
|
||||
* We create a page (on a non-whitelisted origin) which should send us two
|
||||
* messages immediately. The first message has an object for it's detail,
|
||||
* and the second has a string. We check that we only get the second
|
||||
* message.
|
||||
*/
|
||||
let channel = new WebChannel("objects", Services.io.newURI(HTTP_PATH, null, null));
|
||||
let testDonePromise = new Promise((resolve, reject) => {
|
||||
channel.listen((id, message, sender) => {
|
||||
is(id, "objects");
|
||||
is(message.type, "string");
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: HTTP_PATH + HTTP_ENDPOINT + "?object"
|
||||
}, function* () {
|
||||
yield testDonePromise;
|
||||
channel.stopListening();
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: "WebChannel allows both string and non-string message from whitelisted origin",
|
||||
run: function* () {
|
||||
/**
|
||||
* Same process as above, but we whitelist the origin before loading the page,
|
||||
* and expect to get *both* messages back (each exactly once).
|
||||
*/
|
||||
let channel = new WebChannel("objects", Services.io.newURI(HTTP_PATH, null, null));
|
||||
|
||||
let testDonePromise = new Promise((resolve, reject) => {
|
||||
let sawObject = false;
|
||||
let sawString = false;
|
||||
channel.listen((id, message, sender) => {
|
||||
is(id, "objects");
|
||||
if (message.type === "object") {
|
||||
ok(!sawObject);
|
||||
sawObject = true;
|
||||
} else if (message.type === "string") {
|
||||
ok(!sawString);
|
||||
sawString = true;
|
||||
} else {
|
||||
reject(new Error(`Unknown message type: ${message.type}`))
|
||||
}
|
||||
if (sawObject && sawString) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
const webchannelWhitelistPref = "webchannel.allowObject.urlWhitelist";
|
||||
let origWhitelist = Services.prefs.getCharPref(webchannelWhitelistPref);
|
||||
let newWhitelist = origWhitelist + " " + HTTP_PATH;
|
||||
Services.prefs.setCharPref(webchannelWhitelistPref, newWhitelist);
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: HTTP_PATH + HTTP_ENDPOINT + "?object"
|
||||
}, function* () {
|
||||
yield testDonePromise;
|
||||
Services.prefs.setCharPref(webchannelWhitelistPref, origWhitelist);
|
||||
channel.stopListening();
|
||||
});
|
||||
}
|
||||
}
|
||||
]; // gTests
|
||||
|
||||
function test() {
|
||||
|
@ -28,23 +28,23 @@
|
||||
|
||||
function test_iframe() {
|
||||
var firstMessage = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: "twoway",
|
||||
message: {
|
||||
command: "one",
|
||||
},
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
window.addEventListener("WebChannelMessageToContent", function(e) {
|
||||
var secondMessage = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: "twoway",
|
||||
message: {
|
||||
command: "two",
|
||||
detail: e.detail.message,
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
if (!e.detail.message.error) {
|
||||
@ -58,12 +58,12 @@
|
||||
|
||||
function test_iframe_pre_redirect() {
|
||||
var firstMessage = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: "pre_redirect",
|
||||
message: {
|
||||
command: "redirecting",
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
window.dispatchEvent(firstMessage);
|
||||
document.location = REDIRECTED_IFRAME_SRC_ROOT + "?iframe_post_redirect";
|
||||
@ -72,10 +72,10 @@
|
||||
function test_iframe_post_redirect() {
|
||||
window.addEventListener("WebChannelMessageToContent", function(e) {
|
||||
var echoMessage = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: "post_redirect",
|
||||
message: e.detail.message,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
window.dispatchEvent(echoMessage);
|
||||
@ -83,12 +83,12 @@
|
||||
|
||||
// Let the test parent know the page has loaded and is ready to echo events
|
||||
var loadedMessage = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: "post_redirect",
|
||||
message: {
|
||||
command: "loaded",
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
window.dispatchEvent(loadedMessage);
|
||||
}
|
||||
|
@ -1,17 +1,26 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<script>
|
||||
// This test is run multiple times, once with only strings allowed through the
|
||||
// WebChannel, and once with objects allowed. This function allows us to handle
|
||||
// both cases without too much pain.
|
||||
function makeDetails(object) {
|
||||
if (window.location.search.indexOf("object") >= 0) {
|
||||
return object;
|
||||
}
|
||||
return JSON.stringify(object)
|
||||
}
|
||||
// Add a listener for responses to our remote requests.
|
||||
window.addEventListener("WebChannelMessageToContent", function (event) {
|
||||
if (event.detail.id == "remote-troubleshooting") {
|
||||
// Send what we got back to the test.
|
||||
var backEvent = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: makeDetails({
|
||||
id: "test-remote-troubleshooting-backchannel",
|
||||
message: {
|
||||
message: event.detail.message,
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
window.dispatchEvent(backEvent);
|
||||
// and stick it in our DOM just for good measure/diagnostics.
|
||||
@ -23,12 +32,12 @@ window.addEventListener("WebChannelMessageToContent", function (event) {
|
||||
// Make a request for the troubleshooting data as we load.
|
||||
window.onload = function() {
|
||||
var event = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: makeDetails({
|
||||
id: "remote-troubleshooting",
|
||||
message: {
|
||||
command: "request",
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
window.dispatchEvent(event);
|
||||
}
|
||||
|
@ -152,12 +152,14 @@ add_task(function* webchannel_switch() {
|
||||
}
|
||||
|
||||
let replyCount = 0;
|
||||
let replyPromise = new Promise(resolve => {
|
||||
NewTabWebChannel.on("reply", function() {
|
||||
replyCount += 1;
|
||||
resolve();
|
||||
}.bind(this));
|
||||
});
|
||||
function newReplyPromise() {
|
||||
return new Promise(resolve => {
|
||||
NewTabWebChannel.on("reply", function() {
|
||||
replyCount += 1;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
let unloadPromise = new Promise(resolve => {
|
||||
NewTabWebChannel.once("targetUnload", function() {
|
||||
@ -188,9 +190,20 @@ add_task(function* webchannel_switch() {
|
||||
is(NewTabWebChannel.numBrowsers, 1, "Correct number of targets");
|
||||
|
||||
NewTabWebChannel.broadcast("respond", null);
|
||||
yield replyPromise;
|
||||
yield newReplyPromise();
|
||||
is(replyCount, 1, "only current channel is listened to for replies");
|
||||
|
||||
const webchannelWhitelistPref = "webchannel.allowObject.urlWhitelist";
|
||||
let origWhitelist = Services.prefs.getCharPref(webchannelWhitelistPref);
|
||||
let newWhitelist = origWhitelist + " http://mochi.test:8888";
|
||||
Services.prefs.setCharPref(webchannelWhitelistPref, newWhitelist);
|
||||
try {
|
||||
NewTabWebChannel.broadcast("respond_object", null);
|
||||
yield newReplyPromise();
|
||||
} finally {
|
||||
Services.prefs.clearUserPref(webchannelWhitelistPref);
|
||||
}
|
||||
|
||||
for (let tab of tabs) {
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
|
@ -11,20 +11,20 @@
|
||||
switch (e.detail.message.type) {
|
||||
case "RECEIVE_FRECENT":
|
||||
reply = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: "newtab",
|
||||
message: JSON.stringify({type: "numItemsAck", data: e.detail.message.data.length}),
|
||||
}
|
||||
})
|
||||
});
|
||||
window.dispatchEvent(reply);
|
||||
break;
|
||||
case "RECEIVE_PLACES_CHANGE":
|
||||
if (e.detail.message.data.type === "clearHistory") {
|
||||
reply = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: "newtab",
|
||||
message: JSON.stringify({type: "clearHistoryAck", data: e.detail.message.data.type}),
|
||||
}
|
||||
})
|
||||
});
|
||||
window.dispatchEvent(reply);
|
||||
}
|
||||
@ -36,10 +36,10 @@
|
||||
document.onreadystatechange = function () {
|
||||
if (document.readyState === "complete") {
|
||||
let msg = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: "newtab",
|
||||
message: JSON.stringify({type: "REQUEST_FRECENT"}),
|
||||
}
|
||||
})
|
||||
});
|
||||
window.dispatchEvent(msg);
|
||||
}
|
||||
|
@ -8,10 +8,10 @@
|
||||
window.addEventListener("WebChannelMessageToContent", function(e) {
|
||||
if (e.detail.message && e.detail.message.type === "RECEIVE_PREFS") {
|
||||
let reply = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: "newtab",
|
||||
message: JSON.stringify({type: "responseAck"}),
|
||||
}
|
||||
})
|
||||
});
|
||||
window.dispatchEvent(reply);
|
||||
}
|
||||
@ -19,10 +19,10 @@
|
||||
|
||||
document.onreadystatechange = function () {
|
||||
let msg = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: "newtab",
|
||||
message: JSON.stringify({type: "REQUEST_PREFS"}),
|
||||
}
|
||||
})
|
||||
});
|
||||
window.dispatchEvent(msg);
|
||||
};
|
||||
|
@ -11,10 +11,10 @@
|
||||
if (e.detail.message && e.detail.message.type === "RECEIVE_THUMB") {
|
||||
if (e.detail.message.data.imgData && e.detail.message.data.url === thumbURL) {
|
||||
let reply = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: "newtab",
|
||||
message: JSON.stringify({type: "responseAck"}),
|
||||
}
|
||||
})
|
||||
});
|
||||
window.dispatchEvent(reply);
|
||||
}
|
||||
@ -24,10 +24,10 @@
|
||||
document.onreadystatechange = function () {
|
||||
if (document.readyState === "complete") {
|
||||
let msg = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: "newtab",
|
||||
message: JSON.stringify({type: "REQUEST_THUMB", data: thumbURL}),
|
||||
}
|
||||
})
|
||||
});
|
||||
window.dispatchEvent(msg);
|
||||
}
|
||||
|
@ -7,21 +7,25 @@
|
||||
<script>
|
||||
document.onreadystatechange = function () {
|
||||
let msg = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: "newtab",
|
||||
message: JSON.stringify({type: "foo", data: "bar"}),
|
||||
}
|
||||
})
|
||||
});
|
||||
window.dispatchEvent(msg);
|
||||
};
|
||||
|
||||
window.addEventListener("WebChannelMessageToContent", function(e) {
|
||||
if (e.detail.message && e.detail.message.type === "respond") {
|
||||
if (e.detail.message && e.detail.message.type.startsWith("respond")) {
|
||||
var detail = {
|
||||
id: "newtab",
|
||||
message: JSON.stringify({type: "reply", data: "quuz"}),
|
||||
};
|
||||
if (e.detail.message.type !== "respond_object") {
|
||||
detail = JSON.stringify(detail);
|
||||
}
|
||||
let reply = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
id: "newtab",
|
||||
message: JSON.stringify({type: "reply", data: "quuz"}),
|
||||
}
|
||||
detail: detail
|
||||
});
|
||||
window.dispatchEvent(reply);
|
||||
}
|
||||
|
@ -495,11 +495,11 @@ loop.store.ActiveRoomStore = function (mozL10n) {
|
||||
|
||||
// Now send a message to the chrome to see if it can handle this room.
|
||||
window.dispatchEvent(new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: "loop-link-clicker",
|
||||
message: {
|
||||
command: "checkWillOpenRoom",
|
||||
roomToken: this._storeState.roomToken } } }));}.
|
||||
roomToken: this._storeState.roomToken } }) }));}.
|
||||
|
||||
|
||||
|
||||
@ -621,11 +621,11 @@ loop.store.ActiveRoomStore = function (mozL10n) {
|
||||
|
||||
// Now we're set up, dispatch an event.
|
||||
window.dispatchEvent(new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: "loop-link-clicker",
|
||||
message: {
|
||||
command: "openRoom",
|
||||
roomToken: this._storeState.roomToken } } }));},
|
||||
roomToken: this._storeState.roomToken } }) }));},
|
||||
|
||||
|
||||
|
||||
|
@ -890,11 +890,11 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
sinon.assert.calledOnce(window.dispatchEvent);
|
||||
sinon.assert.calledWithExactly(window.dispatchEvent, new window.CustomEvent(
|
||||
"WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: "loop-link-clicker",
|
||||
message: {
|
||||
command: "openRoom",
|
||||
roomToken: "fakeToken" } } }));});
|
||||
roomToken: "fakeToken" } }) }));});
|
||||
|
||||
|
||||
|
||||
|
@ -15,6 +15,7 @@ const TEST_URI =
|
||||
"example.com/browser/browser/extensions/loop/chrome/test/mochitest/test_loopLinkClicker_channel.html";
|
||||
const TEST_URI_GOOD = Services.io.newURI("https://" + TEST_URI, null, null);
|
||||
const TEST_URI_BAD = Services.io.newURI("http://" + TEST_URI, null, null);
|
||||
const TEST_URI_GOOD_OBJECT = Services.io.newURI("https://" + TEST_URI + "?object", null, null);
|
||||
|
||||
const ROOM_TOKEN = "fake1234";
|
||||
const LINKCLICKER_URL_PREFNAME = "loop.linkClicker.url";
|
||||
@ -165,4 +166,18 @@ add_task(function* test_loopRooms_webchannel_openRoom() {
|
||||
|
||||
Assert.equal(got.message.response, true, "should have got a response of true");
|
||||
Assert.equal(got.message.alreadyOpen, true, "should indicate the room is already open");
|
||||
|
||||
// Ensure this still works properly when passing an object through the WebChannel
|
||||
let webchannelWhitelistPref = "webchannel.allowObject.urlWhitelist";
|
||||
let origWhitelist = Services.prefs.getCharPref(webchannelWhitelistPref);
|
||||
let newWhitelist = origWhitelist + " https://example.com";
|
||||
Services.prefs.setCharPref(webchannelWhitelistPref, newWhitelist);
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref(webchannelWhitelistPref);
|
||||
});
|
||||
got = yield promiseNewChannelResponse(TEST_URI_GOOD_OBJECT, gGoodBackChannel, "openRoom");
|
||||
|
||||
Assert.equal(got.message.response, true, "should have got a response of true with objects");
|
||||
Assert.equal(got.message.alreadyOpen, true, "should indicate the room is already open with objects");
|
||||
|
||||
});
|
||||
|
@ -2,18 +2,27 @@
|
||||
<html>
|
||||
<script>
|
||||
"use strict";
|
||||
// This test is run multiple times, once with only strings allowed through the
|
||||
// WebChannel, and once with objects allowed. This function allows us to handle
|
||||
// both cases without too much pain.
|
||||
function makeDetails(object) {
|
||||
if (window.location.search.indexOf("object") >= 0) {
|
||||
return object;
|
||||
}
|
||||
return JSON.stringify(object)
|
||||
}
|
||||
|
||||
// Add a listener for responses to our remote requests.
|
||||
window.addEventListener("WebChannelMessageToContent", function (event) {
|
||||
if (event.detail.id == "loop-link-clicker") {
|
||||
// Send what we got back to the test.
|
||||
var backEvent = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: makeDetails({
|
||||
id: "test-loop-link-clicker-backchannel",
|
||||
message: {
|
||||
message: event.detail.message
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
window.dispatchEvent(backEvent);
|
||||
// and stick it in our DOM just for good measure/diagnostics.
|
||||
@ -27,13 +36,13 @@ window.onload = function() {
|
||||
var hash = window.location.hash;
|
||||
|
||||
var event = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: makeDetails({
|
||||
id: "loop-link-clicker",
|
||||
message: {
|
||||
command: hash.substring(1, hash.length),
|
||||
roomToken: "fake1234"
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
window.dispatchEvent(event);
|
||||
};
|
||||
|
@ -24,14 +24,14 @@
|
||||
|
||||
function test_generic() {
|
||||
var event = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: "generic",
|
||||
message: {
|
||||
something: {
|
||||
nested: "hello",
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
window.dispatchEvent(event);
|
||||
@ -39,23 +39,23 @@
|
||||
|
||||
function test_twoWay() {
|
||||
var firstMessage = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: "twoway",
|
||||
message: {
|
||||
command: "one",
|
||||
},
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
window.addEventListener("WebChannelMessageToContent", function(e) {
|
||||
var secondMessage = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: "twoway",
|
||||
message: {
|
||||
command: "two",
|
||||
detail: e.detail.message,
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
if (!e.detail.message.error) {
|
||||
@ -68,17 +68,17 @@
|
||||
|
||||
function test_multichannel() {
|
||||
var event1 = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: "wrongchannel",
|
||||
message: {},
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
var event2 = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
detail: JSON.stringify({
|
||||
id: "multichannel",
|
||||
message: {},
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
window.dispatchEvent(event1);
|
||||
|
@ -814,17 +814,56 @@ var FindBar = {
|
||||
};
|
||||
FindBar.init();
|
||||
|
||||
// An event listener for custom "WebChannelMessageToChrome" events on pages.
|
||||
addEventListener("WebChannelMessageToChrome", function (e) {
|
||||
// If target is window then we want the document principal, otherwise fallback to target itself.
|
||||
let principal = e.target.nodePrincipal ? e.target.nodePrincipal : e.target.document.nodePrincipal;
|
||||
let WebChannelMessageToChromeListener = {
|
||||
// Preference containing the list (space separated) of origins that are
|
||||
// allowed to send non-string values through a WebChannel, mainly for
|
||||
// backwards compatability. See bug 1238128 for more information.
|
||||
URL_WHITELIST_PREF: "webchannel.allowObject.urlWhitelist",
|
||||
|
||||
if (e.detail) {
|
||||
sendAsyncMessage("WebChannelMessageToChrome", e.detail, { eventTarget: e.target }, principal);
|
||||
} else {
|
||||
Cu.reportError("WebChannel message failed. No message detail.");
|
||||
// Cached list of whitelisted principals, we avoid constructing this if the
|
||||
// value in `_lastWhitelistValue` hasn't changed since we constructed it last.
|
||||
_cachedWhitelist: [],
|
||||
_lastWhitelistValue: "",
|
||||
|
||||
init() {
|
||||
addEventListener("WebChannelMessageToChrome", e => {
|
||||
this._onMessageToChrome(e);
|
||||
}, true, true);
|
||||
},
|
||||
|
||||
_getWhitelistedPrincipals() {
|
||||
let whitelist = Services.prefs.getCharPref(this.URL_WHITELIST_PREF);
|
||||
if (whitelist != this._lastWhitelistValue) {
|
||||
let urls = whitelist.split(/\s+/);
|
||||
this._cachedWhitelist = urls.map(origin =>
|
||||
Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(origin));
|
||||
}
|
||||
return this._cachedWhitelist;
|
||||
},
|
||||
|
||||
_onMessageToChrome(e) {
|
||||
// If target is window then we want the document principal, otherwise fallback to target itself.
|
||||
let principal = e.target.nodePrincipal ? e.target.nodePrincipal : e.target.document.nodePrincipal;
|
||||
|
||||
if (e.detail) {
|
||||
if (typeof e.detail != 'string') {
|
||||
// Check if the principal is one of the ones that's allowed to send
|
||||
// non-string values for e.detail.
|
||||
let objectsAllowed = this._getWhitelistedPrincipals().some(whitelisted =>
|
||||
principal.originNoSuffix == whitelisted.originNoSuffix);
|
||||
if (!objectsAllowed) {
|
||||
Cu.reportError("WebChannelMessageToChrome sent with an object from a non-whitelisted principal");
|
||||
return;
|
||||
}
|
||||
}
|
||||
sendAsyncMessage("WebChannelMessageToChrome", e.detail, { eventTarget: e.target }, principal);
|
||||
} else {
|
||||
Cu.reportError("WebChannel message failed. No message detail.");
|
||||
}
|
||||
}
|
||||
}, true, true);
|
||||
};
|
||||
|
||||
WebChannelMessageToChromeListener.init();
|
||||
|
||||
// This should be kept in sync with /browser/base/content.js.
|
||||
// Add message listener for "WebChannelMessageToContent" messages from chrome scripts.
|
||||
|
@ -71,6 +71,15 @@ var WebChannelBroker = Object.create({
|
||||
eventTarget: event.objects.eventTarget,
|
||||
principal: event.principal,
|
||||
};
|
||||
// data must be a string except for a few legacy origins allowed by browser-content.js.
|
||||
if (typeof data == "string") {
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch (e) {
|
||||
Cu.reportError("Failed to parse WebChannel data as a JSON object");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (data && data.id) {
|
||||
if (!event.principal) {
|
||||
|
Loading…
Reference in New Issue
Block a user