gecko-dev/toolkit/components/telemetry/hybrid-content/content-HybridContentTelemetry.js
Kris Maglione bb97eb4646 Bug 1524688: Part 2 - Convert BrowserGlue and friends to static registration. r=mconley
--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
2019-01-29 17:44:35 -08:00

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);