gecko-dev/browser/modules/PermissionUITelemetry.jsm

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

179 lines
4.9 KiB
JavaScript
Raw Normal View History

/* 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/. */
"use strict";
var EXPORTED_SYMBOLS = ["PermissionUITelemetry"];
ChromeUtils.defineModuleGetter(
this,
"Services",
"resource://gre/modules/Services.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"CryptoUtils",
"resource://services-crypto/utils.js"
);
const TELEMETRY_STAT_REMOVAL_LEAVE_PAGE = 6;
var PermissionUITelemetry = {
// Returns a hash of the host name in combination with a unique local user id.
// This allows us to track duplicate prompts on sites while not revealing the user's
// browsing history.
_uniqueHostHash(host) {
// Gets a unique user ID as salt, that needs to stay local to this profile and not be
// sent to any server!
let salt = Services.prefs.getStringPref(
"permissions.eventTelemetry.salt",
null
);
if (!salt) {
salt = Cc["@mozilla.org/uuid-generator;1"]
.getService(Ci.nsIUUIDGenerator)
.generateUUID()
.toString();
Services.prefs.setStringPref("permissions.eventTelemetry.salt", salt);
}
let domain;
try {
domain = Services.eTLD.getBaseDomainFromHost(host);
} catch (e) {
domain = host;
}
return CryptoUtils.sha256(domain + salt);
},
_previousVisitCount(host) {
let historyService = Cc[
"@mozilla.org/browser/nav-history-service;1"
].getService(Ci.nsINavHistoryService);
let options = historyService.getNewQueryOptions();
options.resultType = options.RESULTS_AS_VISIT;
// Search for visits to this host before today
let query = historyService.getNewQuery();
query.endTimeReference = query.TIME_RELATIVE_TODAY;
query.endTime = 0;
query.domain = host;
let result = historyService.executeQuery(query, options);
result.root.containerOpen = true;
let cc = result.root.childCount;
result.root.containerOpen = false;
return cc;
},
_collectExtraKeys(prompt) {
let lastInteraction = 0;
// "storageAccessAPI" is the name of the permission that tells us whether the
// user has interacted with a particular site in the first-party context before.
let interactionPermission = Services.perms.getPermissionObject(
prompt.principal,
"storageAccessAPI",
false
);
if (interactionPermission) {
lastInteraction = interactionPermission.modificationTime;
}
let allPermsDenied = 0;
let allPermsGranted = 0;
let thisPermDenied = 0;
let thisPermGranted = 0;
let commonPermissions = [
"geo",
"desktop-notification",
"camera",
"microphone",
"screen",
];
for (let perm of Services.perms.enumerator) {
if (!commonPermissions.includes(perm.type)) {
continue;
}
if (perm.capability == Services.perms.ALLOW_ACTION) {
allPermsGranted++;
if (perm.type == prompt.permissionKey) {
thisPermGranted++;
}
}
if (perm.capability == Services.perms.DENY_ACTION) {
allPermsDenied++;
if (perm.type == prompt.permissionKey) {
thisPermDenied++;
}
}
}
let promptHost = prompt.principal.URI.host;
return {
previousVisits: this._previousVisitCount(promptHost).toString(),
timeOnPage: (
Date.now() - prompt.documentDOMContentLoadedTimestamp
).toString(),
hasUserInput: prompt.isHandlingUserInput.toString(),
docHasUserInput: prompt.userHadInteractedWithDocument.toString(),
lastInteraction: lastInteraction.toString(),
allPermsDenied: allPermsDenied.toString(),
allPermsGranted: allPermsGranted.toString(),
thisPermDenied: thisPermDenied.toString(),
thisPermGranted: thisPermGranted.toString(),
};
},
onShow(prompt) {
let object = prompt.permissionTelemetryKey;
if (!object) {
return;
}
let extraKeys = this._collectExtraKeys(prompt);
let hostHash = this._uniqueHostHash(prompt.principal.URI.host);
Services.telemetry.recordEvent(
"security.ui.permissionprompt",
"show",
object,
hostHash,
extraKeys
);
},
onRemoved(prompt, buttonAction, telemetryReason) {
let object = prompt.permissionTelemetryKey;
if (!object) {
return;
}
let method = "other";
if (buttonAction == "accept") {
method = "accept";
} else if (buttonAction == "deny") {
method = "deny";
} else if (buttonAction == "never") {
method = "never";
} else if (telemetryReason == TELEMETRY_STAT_REMOVAL_LEAVE_PAGE) {
method = "leave";
}
let extraKeys = this._collectExtraKeys(prompt);
let hostHash = this._uniqueHostHash(prompt.principal.URI.host);
Services.telemetry.recordEvent(
"security.ui.permissionprompt",
method,
object,
hostHash,
extraKeys
);
},
};