mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 05:41:12 +00:00
Bug 1079563 (part 2) - allow white-listed sites to request troubleshooting info. r=MattN
This commit is contained in:
parent
2b75638713
commit
e42b3b0a84
@ -14,3 +14,7 @@ host uitour 1 about:home
|
||||
# XPInstall
|
||||
host install 1 addons.mozilla.org
|
||||
host install 1 marketplace.firefox.com
|
||||
|
||||
# Remote troubleshooting
|
||||
host remote-troubleshooting 1 input.mozilla.org
|
||||
host remote-troubleshooting 1 support.mozilla.org
|
||||
|
@ -385,6 +385,9 @@ skip-if = buildapp == 'mulet'
|
||||
[browser_private_no_prompt.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_relatedTabs.js]
|
||||
[browser_remoteTroubleshoot.js]
|
||||
support-files =
|
||||
test_remoteTroubleshoot.html
|
||||
[browser_removeTabsToTheEnd.js]
|
||||
[browser_removeUnsafeProtocolsFromURLBarPaste.js]
|
||||
skip-if = e10s
|
||||
|
@ -0,0 +1,64 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
let {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);
|
||||
|
||||
// Creates a one-shot web-channel for the test data to be sent back from the test page.
|
||||
function promiseChannelResponse(channelID, originOrPermission) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let channel = new WebChannel(channelID, originOrPermission);
|
||||
channel.listen((id, data, target) => {
|
||||
channel.stopListening();
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Loads the specified URI in a new tab and waits for it to send us data on our
|
||||
// test web-channel and resolves with that data.
|
||||
function promiseNewChannelResponse(uri) {
|
||||
let channelPromise = promiseChannelResponse("test-remote-troubleshooting-backchannel",
|
||||
uri);
|
||||
let tab = gBrowser.loadOneTab(uri.spec, { inBackground: false });
|
||||
return promiseTabLoaded(tab).then(
|
||||
() => channelPromise
|
||||
).then(data => {
|
||||
gBrowser.removeTab(tab);
|
||||
return data;
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function*() {
|
||||
// We haven't set a permission yet - so even the "good" URI should fail.
|
||||
let got = yield promiseNewChannelResponse(TEST_URI_GOOD);
|
||||
// Should have no data.
|
||||
Assert.ok(got.message === undefined, "should have failed to get any data");
|
||||
|
||||
// Add a permission manager entry for our URI.
|
||||
Services.perms.add(TEST_URI_GOOD,
|
||||
"remote-troubleshooting",
|
||||
Services.perms.ALLOW_ACTION);
|
||||
registerCleanupFunction(() => {
|
||||
Services.perms.remove(TEST_URI_GOOD.spec, "remote-troubleshooting");
|
||||
});
|
||||
|
||||
// Try again - now we are expecting a response with the actual data.
|
||||
got = yield promiseNewChannelResponse(TEST_URI_GOOD);
|
||||
|
||||
// Check some keys we expect to always get.
|
||||
Assert.ok(got.message.extensions, "should have extensions");
|
||||
Assert.ok(got.message.graphics, "should have graphics");
|
||||
|
||||
// And check some keys we know we decline to return.
|
||||
Assert.ok(!got.message.modifiedPreferences, "should not have a modifiedPreferences key");
|
||||
Assert.ok(!got.message.crashes, "should not have crash info");
|
||||
|
||||
// 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");
|
||||
});
|
@ -0,0 +1,41 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<script>
|
||||
// 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: {
|
||||
id: "test-remote-troubleshooting-backchannel",
|
||||
message: {
|
||||
message: event.detail.message,
|
||||
},
|
||||
},
|
||||
});
|
||||
window.dispatchEvent(backEvent);
|
||||
// and stick it in our DOM just for good measure/diagnostics.
|
||||
document.getElementById("troubleshooting").textContent =
|
||||
JSON.stringify(event.detail.message, null, 2);
|
||||
}
|
||||
});
|
||||
|
||||
// Make a request for the troubleshooting data as we load.
|
||||
window.onload = function() {
|
||||
var event = new window.CustomEvent("WebChannelMessageToChrome", {
|
||||
detail: {
|
||||
id: "remote-troubleshooting",
|
||||
message: {
|
||||
command: "request",
|
||||
},
|
||||
},
|
||||
});
|
||||
window.dispatchEvent(event);
|
||||
}
|
||||
</script>
|
||||
|
||||
<body>
|
||||
<pre id="troubleshooting"/>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -132,6 +132,9 @@ XPCOMUtils.defineLazyGetter(this, "ShellService", function() {
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FormValidationHandler",
|
||||
"resource:///modules/FormValidationHandler.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "WebChannel",
|
||||
"resource://gre/modules/WebChannel.jsm");
|
||||
|
||||
const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser";
|
||||
const PREF_PLUGINS_UPDATEURL = "plugins.update.url";
|
||||
|
||||
@ -767,6 +770,21 @@ BrowserGlue.prototype = {
|
||||
}
|
||||
#endif
|
||||
|
||||
// A channel for "remote troubleshooting" code...
|
||||
let channel = new WebChannel("remote-troubleshooting", "remote-troubleshooting");
|
||||
channel.listen((id, data, target) => {
|
||||
if (data.command == "request") {
|
||||
let {Troubleshoot} = Cu.import("resource://gre/modules/Troubleshoot.jsm", {});
|
||||
Troubleshoot.snapshot(data => {
|
||||
// for privacy we remove crash IDs and all preferences (but bug 1091944
|
||||
// exists to expose prefs once we are confident of privacy implications)
|
||||
delete data.crashes;
|
||||
delete data.modifiedPreferences;
|
||||
channel.send(data, target);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this._trackSlowStartup();
|
||||
|
||||
// Offer to reset a user's profile if it hasn't been used for 60 days.
|
||||
|
@ -77,7 +77,7 @@ let WebChannelBroker = Object.create({
|
||||
|
||||
for (var channel of this._channelMap.keys()) {
|
||||
if (channel.id === data.id &&
|
||||
channel.origin.prePath === event.principal.origin) {
|
||||
channel._originCheckCallback(event.principal)) {
|
||||
validChannelFound = true;
|
||||
channel.deliver(data, sender);
|
||||
}
|
||||
@ -133,17 +133,43 @@ let WebChannelBroker = Object.create({
|
||||
*
|
||||
* @param id {String}
|
||||
* WebChannel id
|
||||
* @param origin {nsIURI}
|
||||
* Valid origin that should be part of requests for this channel
|
||||
* @param originOrPermission {nsIURI/string}
|
||||
* If an nsIURI, a valid origin that should be part of requests for
|
||||
* this channel. If a string, a permission for which the permission
|
||||
* manager will be checked to determine if the request is allowed. Note
|
||||
* that in addition to the permission manager check, the request must
|
||||
* be made over https://
|
||||
* @constructor
|
||||
*/
|
||||
this.WebChannel = function(id, origin) {
|
||||
if (!id || !origin) {
|
||||
throw new Error("WebChannel id and origin are required.");
|
||||
this.WebChannel = function(id, originOrPermission) {
|
||||
if (!id || !originOrPermission) {
|
||||
throw new Error("WebChannel id and originOrPermission are required.");
|
||||
}
|
||||
|
||||
this.id = id;
|
||||
this.origin = origin;
|
||||
// originOrPermission can be either an nsIURI or a string representing a
|
||||
// permission name.
|
||||
if (typeof originOrPermission == "string") {
|
||||
this._originCheckCallback = requestPrincipal => {
|
||||
// The permission manager operates on domain names rather than true
|
||||
// origins (bug 1066517). To mitigate that, we explicitly check that
|
||||
// the scheme is https://.
|
||||
let uri = Services.io.newURI(requestPrincipal.origin, null, null);
|
||||
if (uri.scheme != "https") {
|
||||
return false;
|
||||
}
|
||||
// OK - we have https - now we can check the permission.
|
||||
let perm = Services.perms.testExactPermissionFromPrincipal(requestPrincipal,
|
||||
originOrPermission);
|
||||
return perm == Ci.nsIPermissionManager.ALLOW_ACTION;
|
||||
}
|
||||
} else {
|
||||
// a simple URI, so just check for an exact match.
|
||||
this._originCheckCallback = requestPrincipal => {
|
||||
return originOrPermission.prePath === requestPrincipal.origin;
|
||||
}
|
||||
}
|
||||
this._originOrPermission = originOrPermission;
|
||||
};
|
||||
|
||||
this.WebChannel.prototype = {
|
||||
@ -154,9 +180,16 @@ this.WebChannel.prototype = {
|
||||
id: null,
|
||||
|
||||
/**
|
||||
* WebChannel origin
|
||||
* The originOrPermission value passed to the constructor, mainly for
|
||||
* debugging and tests.
|
||||
*/
|
||||
origin: null,
|
||||
_originOrPermission: null,
|
||||
|
||||
/**
|
||||
* Callback that will be called with the principal of an incoming message
|
||||
* to check if the request should be dispatched to the listeners.
|
||||
*/
|
||||
_originCheckCallback: null,
|
||||
|
||||
/**
|
||||
* WebChannelBroker that manages WebChannels
|
||||
|
@ -8,10 +8,11 @@ const Cu = Components.utils;
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/WebChannel.jsm");
|
||||
|
||||
const ERROR_ID_ORIGIN_REQUIRED = "WebChannel id and origin are required.";
|
||||
const ERROR_ID_ORIGIN_REQUIRED = "WebChannel id and originOrPermission are required.";
|
||||
const VALID_WEB_CHANNEL_ID = "id";
|
||||
const URL_STRING = "http://example.com";
|
||||
const VALID_WEB_CHANNEL_ORIGIN = Services.io.newURI(URL_STRING, null, null);
|
||||
const TEST_PERMISSION_NAME = "test-webchannel-permissions";
|
||||
|
||||
let MockWebChannelBroker = {
|
||||
_channelMap: new Map(),
|
||||
@ -34,7 +35,7 @@ function run_test() {
|
||||
*/
|
||||
|
||||
/**
|
||||
* Test channel listening
|
||||
* Test channel listening with originOrPermission being an nsIURI.
|
||||
*/
|
||||
add_task(function test_web_channel_listen() {
|
||||
return new Promise((resolve, reject) => {
|
||||
@ -43,7 +44,54 @@ add_task(function test_web_channel_listen() {
|
||||
});
|
||||
let delivered = 0;
|
||||
do_check_eq(channel.id, VALID_WEB_CHANNEL_ID);
|
||||
do_check_eq(channel.origin.spec, VALID_WEB_CHANNEL_ORIGIN.spec);
|
||||
do_check_eq(channel._originOrPermission.spec, VALID_WEB_CHANNEL_ORIGIN.spec);
|
||||
do_check_eq(channel._deliverCallback, null);
|
||||
|
||||
channel.listen(function(id, message, target) {
|
||||
do_check_eq(id, VALID_WEB_CHANNEL_ID);
|
||||
do_check_true(message);
|
||||
do_check_true(message.command);
|
||||
do_check_true(target.sender);
|
||||
delivered++;
|
||||
// 2 messages should be delivered
|
||||
if (delivered === 2) {
|
||||
channel.stopListening();
|
||||
do_check_eq(channel._deliverCallback, null);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
// send two messages
|
||||
channel.deliver({
|
||||
id: VALID_WEB_CHANNEL_ID,
|
||||
message: {
|
||||
command: "one"
|
||||
}
|
||||
}, { sender: true });
|
||||
|
||||
channel.deliver({
|
||||
id: VALID_WEB_CHANNEL_ID,
|
||||
message: {
|
||||
command: "two"
|
||||
}
|
||||
}, { sender: true });
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Test channel listening with originOrPermission being a permission string.
|
||||
*/
|
||||
add_task(function test_web_channel_listen_permission() {
|
||||
return new Promise((resolve, reject) => {
|
||||
// add a new permission
|
||||
Services.perms.add(VALID_WEB_CHANNEL_ORIGIN, TEST_PERMISSION_NAME, Services.perms.ALLOW_ACTION);
|
||||
do_register_cleanup(() => Services.perms.remove(VALID_WEB_CHANNEL_ORIGIN.spec, TEST_PERMISSION_NAME));
|
||||
let channel = new WebChannel(VALID_WEB_CHANNEL_ID, TEST_PERMISSION_NAME, {
|
||||
broker: MockWebChannelBroker
|
||||
});
|
||||
let delivered = 0;
|
||||
do_check_eq(channel.id, VALID_WEB_CHANNEL_ID);
|
||||
do_check_eq(channel._originOrPermission, TEST_PERMISSION_NAME);
|
||||
do_check_eq(channel._deliverCallback, null);
|
||||
|
||||
channel.listen(function(id, message, target) {
|
||||
|
@ -55,7 +55,9 @@ add_task(function test_web_channel_broker_listener() {
|
||||
return new Promise((resolve, reject) => {
|
||||
var channel = new Object({
|
||||
id: VALID_WEB_CHANNEL_ID,
|
||||
origin: VALID_WEB_CHANNEL_ORIGIN,
|
||||
_originCheckCallback: requestPrincipal => {
|
||||
return VALID_WEB_CHANNEL_ORIGIN.prePath === requestPrincipal.origin;
|
||||
},
|
||||
deliver: function(data, sender) {
|
||||
do_check_eq(data.id, VALID_WEB_CHANNEL_ID);
|
||||
do_check_eq(data.message.command, "hello");
|
||||
|
Loading…
Reference in New Issue
Block a user