mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-11-10 05:08:36 +02:00
--HG-- rename : browser/components/nsBrowserContentHandler.js => browser/components/BrowserContentHandler.jsm rename : browser/components/nsBrowserGlue.js => browser/components/BrowserGlue.jsm rename : browser/components/protocolhandler/WebProtocolHandlerRegistrar.js => browser/components/protocolhandler/WebProtocolHandlerRegistrar.jsm extra : source : 9201a7ea3c543310df1612b57ab72b883c94e07e
172 lines
6.4 KiB
JavaScript
172 lines
6.4 KiB
JavaScript
/* 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/. */
|
|
|
|
/* eslint-env mozilla/frame-script */
|
|
|
|
"use strict";
|
|
|
|
const {Log} = ChromeUtils.import("resource://gre/modules/Log.jsm");
|
|
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
const {TelemetryUtils} = ChromeUtils.import("resource://gre/modules/TelemetryUtils.jsm");
|
|
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
|
|
const TelemetryPrefs = TelemetryUtils.Preferences;
|
|
|
|
// The permission to be granted to pages that want to use HCT.
|
|
const HCT_PERMISSION = "hc_telemetry";
|
|
// The message that chrome uses to communicate back to content, when the
|
|
// upload policy changes.
|
|
const HCT_POLICY_CHANGE_MSG = "HybridContentTelemetry:PolicyChanged";
|
|
|
|
var HybridContentTelemetryListener = {
|
|
_logger: null,
|
|
_hasListener: false,
|
|
|
|
get _log() {
|
|
if (!this._logger) {
|
|
this._logger =
|
|
Log.repository.getLoggerWithMessagePrefix("Toolkit.Telemetry",
|
|
"HybridContentTelemetryListener::");
|
|
}
|
|
|
|
return this._logger;
|
|
},
|
|
|
|
/**
|
|
* Verifies that the hybrid content telemetry request comes
|
|
* from a trusted origin.
|
|
* @oaram {nsIDomEvent} event Optional object containing the data for the event coming from
|
|
* the content. The "detail" section of this event holds the
|
|
* hybrid content specific payload. It looks like:
|
|
* {name: "apiEndpoint", data: { ... endpoint specific data ... }}
|
|
* @return {Boolean} true if we are on a trusted page, false otherwise.
|
|
*/
|
|
isTrustedOrigin(aEvent) {
|
|
// Make sure that events only come from the toplevel frame. We need to check the
|
|
// event's target as |content| always represents the current top level window in
|
|
// the frame (or null). This means that a check like |content.top != content| will
|
|
// always be false. See nsIMessageManager.idl for more info.
|
|
if (aEvent &&
|
|
(!("ownerGlobal" in aEvent.target) ||
|
|
aEvent.target.ownerGlobal != content)) {
|
|
return false;
|
|
}
|
|
|
|
const principal =
|
|
aEvent ? aEvent.target.ownerGlobal.document.nodePrincipal : content.document.nodePrincipal;
|
|
if (principal.isSystemPrincipal) {
|
|
return true;
|
|
}
|
|
|
|
const allowedSchemes = ["https", "about"];
|
|
if (!allowedSchemes.includes(principal.URI.scheme)) {
|
|
return false;
|
|
}
|
|
|
|
// If this is not a system principal, it needs to have the
|
|
// HCT_PERMISSION to use this API.
|
|
let permission =
|
|
Services.perms.testPermissionFromPrincipal(principal, HCT_PERMISSION);
|
|
if (permission == Services.perms.ALLOW_ACTION) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
},
|
|
|
|
/**
|
|
* Handles incoming events from the content library.
|
|
* This verifies that:
|
|
*
|
|
* - the hybrid content telemetry is on;
|
|
* - the page is trusted and allowed to use it;
|
|
* - the CustomEvent coming from the "content" has the expected format and
|
|
* is for a known API end-point.
|
|
*
|
|
* If the page is allowed to use the API and the event validates, the call
|
|
* is passed on to Telemetry.
|
|
*
|
|
* @param {nsIDomEvent} event An object containing the data for the event coming from
|
|
* the content. The "detail" section of this event holds the
|
|
* hybrid content specific payload. It looks like:
|
|
* {name: "apiEndpoint", data: { ... endpoint specific data ... }}
|
|
*/
|
|
handleEvent(event) {
|
|
const originNotTrusted = !this.isTrustedOrigin(event);
|
|
if (!this._hybridContentEnabled || originNotTrusted) {
|
|
this._log.warn(`handleEvent - hct disabled ${!this._hybridContentEnabled}, `
|
|
+ `untrusted origin ${originNotTrusted}.`);
|
|
let errorEvent = Cu.cloneInto({bubbles: true, detail: {}}, content);
|
|
content.document.dispatchEvent(
|
|
new content.document.defaultView.CustomEvent("mozTelemetryUntrustedOrigin", errorEvent)
|
|
);
|
|
return;
|
|
}
|
|
|
|
if (!event ||
|
|
!("detail" in event) ||
|
|
!("name" in event.detail) ||
|
|
!("data" in event.detail) ||
|
|
typeof event.detail.name != "string" ||
|
|
typeof event.detail.data != "object") {
|
|
this._log.error("handleEvent - received a malformed message.");
|
|
return;
|
|
}
|
|
|
|
// We add the message listener here to guarantee it only gets added
|
|
// for trusted origins.
|
|
// Note that the name of the message must match the name of the one
|
|
// in HybridContentTelemetry.jsm.
|
|
if (!this._hasListener) {
|
|
addMessageListener(HCT_POLICY_CHANGE_MSG, this);
|
|
this._hasListener = true;
|
|
}
|
|
|
|
// Note that the name of the async message must match the name of
|
|
// the message in the related listener in BrowserGlue.jsm.
|
|
sendAsyncMessage("HybridContentTelemetry:onTelemetryMessage", {
|
|
name: event.detail.name,
|
|
data: event.detail.data,
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Handles the HCT_POLICY_CHANGE_MSG message coming from chrome and
|
|
* passes it back to the content.
|
|
* @param {Object} aMessage The incoming message. See nsIMessageListener docs.
|
|
*/
|
|
receiveMessage(aMessage) {
|
|
if (!this.isTrustedOrigin()) {
|
|
this._log.warn("receiveMessage - accessing telemetry from an untrusted origin.");
|
|
return;
|
|
}
|
|
|
|
if (aMessage.name != HCT_POLICY_CHANGE_MSG ||
|
|
!("data" in aMessage) ||
|
|
!("canUpload" in aMessage.data) ||
|
|
typeof aMessage.data.canUpload != "boolean") {
|
|
this._log.warn("receiveMessage - received an unexpected message.");
|
|
return;
|
|
}
|
|
|
|
// Finally send the message down to the page.
|
|
let event = Cu.cloneInto({
|
|
bubbles: true,
|
|
detail: {canUpload: aMessage.data.canUpload},
|
|
}, content);
|
|
|
|
content.document.dispatchEvent(
|
|
new content.document.defaultView.CustomEvent("mozTelemetryPolicyChange", event)
|
|
);
|
|
},
|
|
};
|
|
|
|
XPCOMUtils.defineLazyPreferenceGetter(HybridContentTelemetryListener, "_hybridContentEnabled",
|
|
TelemetryUtils.Preferences.HybridContentEnabled,
|
|
false /* aDefaultValue */);
|
|
|
|
// The following function installs the handler for "mozTelemetry"
|
|
// events in the chrome. Please note that the name of the message (i.e.
|
|
// "mozTelemetry") needs to match the one in HybridContentTelemetry-lib.js.
|
|
addEventListener("mozTelemetry", HybridContentTelemetryListener, false, true);
|