Bug 1804502 - Add relevant telemetry events to relay integration. r=credential-management-reviewers,dimi,sgalich,settings-reviewers,mconley
Differential Revision: https://phabricator.services.mozilla.com/D163911
This commit is contained in:
@@ -42,6 +42,10 @@ var { Weave } = ChromeUtils.importESModule(
|
||||
"resource://services-sync/main.sys.mjs"
|
||||
);
|
||||
|
||||
var { FirefoxRelayTelemetry } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/FirefoxRelayTelemetry.mjs"
|
||||
);
|
||||
|
||||
var { FxAccounts, getFxAccountsSingleton } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/FxAccounts.sys.mjs"
|
||||
);
|
||||
|
||||
@@ -2486,11 +2486,12 @@ var gPrivacyPane = {
|
||||
|
||||
toggleRelayIntegration() {
|
||||
const checkbox = document.getElementById("relayIntegration");
|
||||
|
||||
if (checkbox.checked) {
|
||||
FirefoxRelay.markAsEnabled();
|
||||
FirefoxRelayTelemetry.recordRelayPrefEvent("enabled");
|
||||
} else {
|
||||
FirefoxRelay.markAsDisabled();
|
||||
FirefoxRelayTelemetry.recordRelayPrefEvent("disabled");
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -9,12 +9,21 @@ const EXPORTED_SYMBOLS = ["FirefoxRelay"];
|
||||
const { XPCOMUtils } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/XPCOMUtils.sys.mjs"
|
||||
);
|
||||
|
||||
const { FirefoxRelayTelemetry } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/FirefoxRelayTelemetry.mjs"
|
||||
);
|
||||
|
||||
const {
|
||||
LoginHelper,
|
||||
OptInFeature,
|
||||
ParentAutocompleteOption,
|
||||
} = ChromeUtils.import("resource://gre/modules/LoginHelper.jsm");
|
||||
|
||||
const { TelemetryUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/TelemetryUtils.jsm"
|
||||
);
|
||||
|
||||
const lazy = {};
|
||||
|
||||
// Static configuration
|
||||
@@ -137,11 +146,13 @@ async function getReusableMasksAsync(browser, _origin) {
|
||||
);
|
||||
|
||||
if (!response) {
|
||||
return undefined;
|
||||
// fetchWithReauth only returns undefined if login / obtaining a token failed.
|
||||
// Otherwise, it will return a response object.
|
||||
return [undefined, RelayFeature.AUTH_TOKEN_ERROR_CODE];
|
||||
}
|
||||
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
return [await response.json(), response.status];
|
||||
}
|
||||
|
||||
lazy.log.error(
|
||||
@@ -150,9 +161,8 @@ async function getReusableMasksAsync(browser, _origin) {
|
||||
await showErrorAsync(browser, "firefox-relay-get-reusable-masks-failed", {
|
||||
status: response.status,
|
||||
});
|
||||
// Services.telemetry.recordEvent("pwmgr", "make_relay_fail", "relay");
|
||||
|
||||
return undefined;
|
||||
return [undefined, response.status];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -220,9 +230,14 @@ async function formatMessages(...ids) {
|
||||
});
|
||||
}
|
||||
|
||||
async function showReusableMasksAsync(browser, origin, errorMessage) {
|
||||
const reusableMasks = await getReusableMasksAsync(browser, origin);
|
||||
async function showReusableMasksAsync(browser, origin, error) {
|
||||
const [reusableMasks, status] = await getReusableMasksAsync(browser, origin);
|
||||
if (!reusableMasks) {
|
||||
FirefoxRelayTelemetry.recordRelayReusePanelEvent(
|
||||
"shown",
|
||||
FirefoxRelay.flowId,
|
||||
status
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -236,6 +251,10 @@ async function showReusableMasksAsync(browser, origin, errorMessage) {
|
||||
accessKey: getUnlimitedMasksStrings.accesskey,
|
||||
dismiss: true,
|
||||
async callback() {
|
||||
FirefoxRelayTelemetry.recordRelayReusePanelEvent(
|
||||
"get_unlimited_masks",
|
||||
FirefoxRelay.flowId
|
||||
);
|
||||
browser.ownerGlobal.openWebLinkIn(config.learnMoreURL, "tab");
|
||||
},
|
||||
};
|
||||
@@ -253,7 +272,7 @@ async function showReusableMasksAsync(browser, origin, errorMessage) {
|
||||
|
||||
notification.owner.panel.getElementsByClassName(
|
||||
"error-message"
|
||||
)[0].textContent = errorMessage;
|
||||
)[0].textContent = error.detail || "";
|
||||
|
||||
// rebuild "reuse mask" buttons list
|
||||
const list = getReusableMasksList();
|
||||
@@ -279,6 +298,10 @@ async function showReusableMasksAsync(browser, origin, errorMessage) {
|
||||
notification.remove();
|
||||
lazy.log.info("Reusing Relay mask");
|
||||
fillUsername(mask.full_address);
|
||||
FirefoxRelayTelemetry.recordRelayReusePanelEvent(
|
||||
"reuse_mask",
|
||||
FirefoxRelay.flowId
|
||||
);
|
||||
});
|
||||
fragment.appendChild(button);
|
||||
});
|
||||
@@ -297,6 +320,10 @@ async function showReusableMasksAsync(browser, origin, errorMessage) {
|
||||
break;
|
||||
case "shown":
|
||||
notificationShown();
|
||||
FirefoxRelayTelemetry.recordRelayReusePanelEvent(
|
||||
"shown",
|
||||
FirefoxRelay.flowId
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -338,6 +365,11 @@ async function generateUsernameAsync(browser, origin) {
|
||||
);
|
||||
|
||||
if (!response) {
|
||||
FirefoxRelayTelemetry.recordRelayUsernameFilledEvent(
|
||||
"shown",
|
||||
FirefoxRelay.flowId,
|
||||
RelayFeature.AUTH_TOKEN_ERROR_CODE
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -345,14 +377,18 @@ async function generateUsernameAsync(browser, origin) {
|
||||
lazy.log.info(`generated Relay mask`);
|
||||
const result = await response.json();
|
||||
showConfirmation(browser, "confirmation-hint-firefox-relay-mask-generated");
|
||||
// Services.telemetry.recordEvent("pwmgr", "make_relay", "relay");
|
||||
return result.full_address;
|
||||
}
|
||||
|
||||
if (response.status == 403) {
|
||||
const error = await response.json();
|
||||
if (error?.error_code == "free_tier_limit") {
|
||||
return showReusableMasksAsync(browser, origin, error.detail);
|
||||
FirefoxRelayTelemetry.recordRelayUsernameFilledEvent(
|
||||
"shown",
|
||||
FirefoxRelay.flowId,
|
||||
error?.error_code
|
||||
);
|
||||
return showReusableMasksAsync(browser, origin, error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,7 +399,12 @@ async function generateUsernameAsync(browser, origin) {
|
||||
await showErrorAsync(browser, "firefox-relay-mask-generation-failed", {
|
||||
status: response.status,
|
||||
});
|
||||
// Services.telemetry.recordEvent("pwmgr", "make_relay_fail", "relay");
|
||||
|
||||
FirefoxRelayTelemetry.recordRelayReusePanelEvent(
|
||||
"shown",
|
||||
FirefoxRelay.flowId,
|
||||
response.status
|
||||
);
|
||||
|
||||
return undefined;
|
||||
}
|
||||
@@ -391,7 +432,19 @@ class RelayOffered {
|
||||
title,
|
||||
subtitle,
|
||||
"PasswordManager:offerRelayIntegration",
|
||||
null
|
||||
{
|
||||
telemetry: {
|
||||
flowId: FirefoxRelay.flowId,
|
||||
isRelayUser: this.#isRelayUser,
|
||||
scenarioName,
|
||||
},
|
||||
}
|
||||
);
|
||||
FirefoxRelayTelemetry.recordRelayOfferedEvent(
|
||||
"shown",
|
||||
FirefoxRelay.flowId,
|
||||
scenarioName,
|
||||
this.#isRelayUser
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -399,6 +452,7 @@ class RelayOffered {
|
||||
|
||||
async offerRelayIntegration(feature, browser, origin) {
|
||||
const fxaUser = await lazy.fxAccounts.getSignedInUser();
|
||||
|
||||
if (!fxaUser) {
|
||||
return null;
|
||||
}
|
||||
@@ -424,6 +478,10 @@ class RelayOffered {
|
||||
lazy.log.info("user opted in to Firefox Relay integration");
|
||||
feature.markAsEnabled();
|
||||
fillUsername(await generateUsernameAsync(browser, origin));
|
||||
FirefoxRelayTelemetry.recordRelayOptInPanelEvent(
|
||||
"enabled",
|
||||
FirefoxRelay.flowId
|
||||
);
|
||||
},
|
||||
};
|
||||
const postpone = {
|
||||
@@ -435,6 +493,10 @@ class RelayOffered {
|
||||
"user decided not to decide about Firefox Relay integration"
|
||||
);
|
||||
feature.markAsOffered();
|
||||
FirefoxRelayTelemetry.recordRelayOptInPanelEvent(
|
||||
"postponed",
|
||||
FirefoxRelay.flowId
|
||||
);
|
||||
},
|
||||
};
|
||||
const disableIntegration = {
|
||||
@@ -444,6 +506,10 @@ class RelayOffered {
|
||||
callback() {
|
||||
lazy.log.info("user opted out from Firefox Relay integration");
|
||||
feature.markAsDisabled();
|
||||
FirefoxRelayTelemetry.recordRelayOptInPanelEvent(
|
||||
"disabled",
|
||||
FirefoxRelay.flowId
|
||||
);
|
||||
},
|
||||
};
|
||||
let notification;
|
||||
@@ -478,6 +544,10 @@ class RelayOffered {
|
||||
useremail: fxaUser.email,
|
||||
}
|
||||
);
|
||||
FirefoxRelayTelemetry.recordRelayOptInPanelEvent(
|
||||
"shown",
|
||||
FirefoxRelay.flowId
|
||||
);
|
||||
break;
|
||||
}
|
||||
},
|
||||
@@ -504,7 +574,15 @@ class RelayEnabled {
|
||||
title,
|
||||
subtitle,
|
||||
"PasswordManager:generateRelayUsername",
|
||||
origin
|
||||
{
|
||||
telemetry: {
|
||||
flowId: FirefoxRelay.flowId,
|
||||
},
|
||||
}
|
||||
);
|
||||
FirefoxRelayTelemetry.recordRelayUsernameFilledEvent(
|
||||
"shown",
|
||||
FirefoxRelay.flowId
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -517,8 +595,12 @@ class RelayEnabled {
|
||||
class RelayDisabled {}
|
||||
|
||||
class RelayFeature extends OptInFeature {
|
||||
// Using 418 to avoid conflict with other standard http error code
|
||||
static AUTH_TOKEN_ERROR_CODE = 418;
|
||||
|
||||
constructor() {
|
||||
super(RelayOffered, RelayEnabled, RelayDisabled, config.relayFeaturePref);
|
||||
Services.telemetry.setEventRecordingEnabled("relay_integration", true);
|
||||
}
|
||||
|
||||
get learnMoreUrl() {
|
||||
@@ -528,6 +610,13 @@ class RelayFeature extends OptInFeature {
|
||||
async autocompleteItemsAsync({ origin, scenarioName, hasInput }) {
|
||||
const result = [];
|
||||
|
||||
// Generate a flowID to unique identify a series of user action. FlowId
|
||||
// allows us to link users' interaction on different UI component (Ex. autocomplete, notification)
|
||||
// We can use flowID to build the Funnel Diagram
|
||||
// This value need to always be regenerated in the entry point of an user
|
||||
// action so we overwrite the previous one.
|
||||
this.flowId = TelemetryUtils.generateUUID();
|
||||
|
||||
if (this.implementation.autocompleteItemsAsync) {
|
||||
for await (const item of this.implementation.autocompleteItemsAsync(
|
||||
origin,
|
||||
|
||||
74
toolkit/components/passwordmgr/FirefoxRelayTelemetry.mjs
Normal file
74
toolkit/components/passwordmgr/FirefoxRelayTelemetry.mjs
Normal file
@@ -0,0 +1,74 @@
|
||||
/* 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/. */
|
||||
|
||||
export const FirefoxRelayTelemetry = {
|
||||
recordRelayIntegrationTelemetryEvent(
|
||||
eventObject,
|
||||
eventMethod,
|
||||
eventFlowId,
|
||||
eventExtras
|
||||
) {
|
||||
Services.telemetry.recordEvent(
|
||||
"relay_integration",
|
||||
eventMethod,
|
||||
eventObject,
|
||||
eventFlowId ?? "",
|
||||
eventExtras ?? {}
|
||||
);
|
||||
},
|
||||
|
||||
recordRelayPrefEvent(eventMethod, eventFlowId, eventExtras) {
|
||||
this.recordRelayIntegrationTelemetryEvent(
|
||||
"pref_change",
|
||||
eventMethod,
|
||||
eventFlowId,
|
||||
eventExtras
|
||||
);
|
||||
},
|
||||
|
||||
recordRelayOfferedEvent(eventMethod, eventFlowId, scenarioName, isRelayUser) {
|
||||
return this.recordRelayIntegrationTelemetryEvent(
|
||||
"offer_relay",
|
||||
eventMethod,
|
||||
eventFlowId,
|
||||
{
|
||||
scenario: scenarioName,
|
||||
is_relay_user: (isRelayUser ?? "") + "",
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
recordRelayUsernameFilledEvent(eventMethod, eventFlowId, errorCode = 0) {
|
||||
return this.recordRelayIntegrationTelemetryEvent(
|
||||
"fill_username",
|
||||
eventMethod,
|
||||
eventFlowId,
|
||||
{
|
||||
error_code: errorCode + "",
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
recordRelayReusePanelEvent(eventMethod, eventFlowId, errorCode = 0) {
|
||||
return this.recordRelayIntegrationTelemetryEvent(
|
||||
"reuse_panel",
|
||||
eventMethod,
|
||||
eventFlowId,
|
||||
{
|
||||
error_code: errorCode + "",
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
recordRelayOptInPanelEvent(eventMethod, eventFlowId, eventExtras) {
|
||||
return this.recordRelayIntegrationTelemetryEvent(
|
||||
"opt_in_panel",
|
||||
eventMethod,
|
||||
eventFlowId,
|
||||
eventExtras
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export default FirefoxRelayTelemetry;
|
||||
@@ -8,6 +8,10 @@ const { XPCOMUtils } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/XPCOMUtils.sys.mjs"
|
||||
);
|
||||
|
||||
const { FirefoxRelayTelemetry } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/FirefoxRelayTelemetry.mjs"
|
||||
);
|
||||
|
||||
const LoginInfo = new Components.Constructor(
|
||||
"@mozilla.org/login-manager/loginInfo;1",
|
||||
Ci.nsILoginInfo,
|
||||
@@ -351,10 +355,20 @@ class LoginManagerParent extends JSWindowActorParent {
|
||||
}
|
||||
|
||||
case "PasswordManager:offerRelayIntegration": {
|
||||
FirefoxRelayTelemetry.recordRelayOfferedEvent(
|
||||
"clicked",
|
||||
data.telemetry.flowId,
|
||||
data.telemetry.scenarioName,
|
||||
data.telemetry.isRelayUser
|
||||
);
|
||||
return this.#offerRelayIntegration(context.origin);
|
||||
}
|
||||
|
||||
case "PasswordManager:generateRelayUsername": {
|
||||
FirefoxRelayTelemetry.recordRelayUsernameFilledEvent(
|
||||
"clicked",
|
||||
data.telemetry.flowId
|
||||
);
|
||||
return this.#generateRelayUsername(context.origin);
|
||||
}
|
||||
}
|
||||
@@ -778,6 +792,7 @@ class LoginManagerParent extends JSWindowActorParent {
|
||||
// Convert the array of nsILoginInfo to vanilla JS objects since nsILoginInfo
|
||||
// doesn't support structured cloning.
|
||||
let jsLogins = lazy.LoginHelper.loginsToVanillaObjects(matchingLogins);
|
||||
|
||||
return {
|
||||
generatedPassword,
|
||||
importable: await getImportableLogins(formOrigin),
|
||||
|
||||
@@ -32,6 +32,7 @@ XPIDL_MODULE = "loginmgr"
|
||||
EXTRA_JS_MODULES += [
|
||||
"crypto-SDR.js",
|
||||
"FirefoxRelay.jsm",
|
||||
"FirefoxRelayTelemetry.mjs",
|
||||
"InsecurePasswordUtils.jsm",
|
||||
"LoginAutoComplete.jsm",
|
||||
"LoginFormFactory.jsm",
|
||||
|
||||
@@ -1316,6 +1316,62 @@ pwmgr:
|
||||
Whether or not the saved/updated password was selected by the user choosing a suggested
|
||||
value from the autocomplete popup.
|
||||
|
||||
# Record telemetry based on individual Firefox relay UI (autocomplete popup, notification panel)
|
||||
relay_integration:
|
||||
popup_option:
|
||||
description: >
|
||||
Firefox relay integration autocomplete popup
|
||||
objects: ["offer_relay", "fill_username"]
|
||||
methods: ["shown", "clicked"]
|
||||
bug_numbers: [1804502]
|
||||
expiry_version: "never"
|
||||
products: ["firefox"]
|
||||
record_in_processes: [main]
|
||||
notification_emails: ["passwords-dev@mozilla.org"]
|
||||
extra_keys:
|
||||
scenario: Describes the auth context for now only SignupForm is supported
|
||||
error_code: >
|
||||
The error code after users click the fill username autocomplete entry.
|
||||
Only present if the object is "fill_username".
|
||||
When the event is successful, the error_code is 0.
|
||||
is_relay_user: >
|
||||
Whether the user is a relay user or not.
|
||||
Only present if the object is "offer_relay"
|
||||
mask_panel:
|
||||
description: >
|
||||
Panels to show the state of the email alias generation
|
||||
objects: ["reuse_panel"]
|
||||
methods: ["shown", "get_unlimited_masks", "reuse_mask"]
|
||||
bug_numbers: [1804502]
|
||||
expiry_version: "never"
|
||||
products: ["firefox"]
|
||||
record_in_processes: [main]
|
||||
notification_emails: ["passwords-dev@mozilla.org"]
|
||||
extra_keys:
|
||||
error_code: >
|
||||
The error code after users click the email alias generation panel.
|
||||
When the event is successful, the error_code is 0.
|
||||
opt_in_panel:
|
||||
description: >
|
||||
Panel to opt-in Firefox Relay Integration
|
||||
objects: ["opt_in_panel"]
|
||||
methods: ["shown", "enabled", "postponed", "disabled"]
|
||||
bug_numbers: [1804502]
|
||||
expiry_version: "never"
|
||||
products: ["firefox"]
|
||||
record_in_processes: [main]
|
||||
notification_emails: ["passwords-dev@mozilla.org"]
|
||||
pref_change:
|
||||
description: >
|
||||
Checkbox in the settings page to enable/disable relay
|
||||
objects: ["pref_change"]
|
||||
methods: ["enabled", "disabled"]
|
||||
bug_numbers: [1804502]
|
||||
expiry_version: "never"
|
||||
products: ["firefox"]
|
||||
record_in_processes: [main]
|
||||
notification_emails: ["passwords-dev@mozilla.org"]
|
||||
|
||||
jsonfile:
|
||||
load:
|
||||
description: >
|
||||
@@ -2985,8 +3041,6 @@ security.ui.certerror:
|
||||
products:
|
||||
- "firefox"
|
||||
record_in_processes: ["content"]
|
||||
products:
|
||||
- firefox
|
||||
extra_keys:
|
||||
is_frame: If the error page is loaded in an iframe.
|
||||
has_sts: If the error page is for a site with HSTS headers or with a pinned key.
|
||||
@@ -3018,8 +3072,6 @@ security.ui.certerror:
|
||||
products:
|
||||
- "firefox"
|
||||
record_in_processes: ["content"]
|
||||
products:
|
||||
- firefox
|
||||
extra_keys:
|
||||
is_frame: If the error page is loaded in an iframe.
|
||||
has_sts: If the error page is for a site with HSTS headers or with a pinned key.
|
||||
|
||||
Reference in New Issue
Block a user