зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1486507 - Record telemetry for browser language changes r=rpl,flod,chutten
Differential Revision: https://phabricator.services.mozilla.com/D11795 --HG-- extra : rebase_source : 0f41affb5790bc14f467c7956ca7eb179ab3f9e5 extra : amend_source : efb27b06212185e373c5711243a79ea793b32291
This commit is contained in:
Родитель
6aa1593b81
Коммит
b5de939bac
|
@ -27,9 +27,15 @@ ChromeUtils.defineModuleGetter(this, "SelectionChangedMenulist",
|
|||
* the user has opted to search for more languages.
|
||||
*/
|
||||
|
||||
async function installFromUrl(url, hash) {
|
||||
async function installFromUrl(url, hash, callback) {
|
||||
let telemetryInfo = {
|
||||
source: "about:preferences",
|
||||
};
|
||||
let install = await AddonManager.getInstallForURL(
|
||||
url, "application/x-xpinstall", hash);
|
||||
url, "application/x-xpinstall", hash, null, null, null, null, telemetryInfo);
|
||||
if (callback) {
|
||||
callback(install.installId.toString());
|
||||
}
|
||||
await install.install();
|
||||
return install.addon;
|
||||
}
|
||||
|
@ -45,12 +51,13 @@ async function dictionaryIdsForLocale(locale) {
|
|||
}
|
||||
|
||||
class OrderedListBox {
|
||||
constructor({richlistbox, upButton, downButton, removeButton, onRemove}) {
|
||||
constructor({richlistbox, upButton, downButton, removeButton, onRemove, onReorder}) {
|
||||
this.richlistbox = richlistbox;
|
||||
this.upButton = upButton;
|
||||
this.downButton = downButton;
|
||||
this.removeButton = removeButton;
|
||||
this.onRemove = onRemove;
|
||||
this.onReorder = onReorder;
|
||||
|
||||
this.items = [];
|
||||
|
||||
|
@ -87,6 +94,8 @@ class OrderedListBox {
|
|||
this.richlistbox.insertBefore(selectedEl, prevEl);
|
||||
this.richlistbox.ensureElementIsVisible(selectedEl);
|
||||
this.setButtonState();
|
||||
|
||||
this.onReorder();
|
||||
}
|
||||
|
||||
moveDown() {
|
||||
|
@ -104,6 +113,8 @@ class OrderedListBox {
|
|||
this.richlistbox.insertBefore(nextEl, selectedEl);
|
||||
this.richlistbox.ensureElementIsVisible(selectedEl);
|
||||
this.setButtonState();
|
||||
|
||||
this.onReorder();
|
||||
}
|
||||
|
||||
removeItem() {
|
||||
|
@ -305,6 +316,8 @@ function compareItems(a, b) {
|
|||
}
|
||||
|
||||
var gBrowserLanguagesDialog = {
|
||||
telemetryId: null,
|
||||
accepted: false,
|
||||
_availableLocales: null,
|
||||
_selectedLocales: null,
|
||||
selectedLocales: null,
|
||||
|
@ -314,14 +327,21 @@ var gBrowserLanguagesDialog = {
|
|||
return Services.prefs.getBoolPref("intl.multilingual.downloadEnabled");
|
||||
},
|
||||
|
||||
recordTelemetry(method, extra = null) {
|
||||
Services.telemetry.recordEvent(
|
||||
"intl.ui.browserLanguage", method, "dialog", this.telemetryId, extra);
|
||||
},
|
||||
|
||||
beforeAccept() {
|
||||
this.selected = this.getSelectedLocales();
|
||||
this.accepted = true;
|
||||
return true;
|
||||
},
|
||||
|
||||
async onLoad() {
|
||||
// Maintain the previously selected locales even if we cancel out.
|
||||
let {selected, search} = window.arguments[0] || {};
|
||||
let {telemetryId, selected, search} = window.arguments[0];
|
||||
this.telemetryId = telemetryId;
|
||||
this.selectedLocales = selected;
|
||||
|
||||
// This is a list of available locales that the user selected. It's more
|
||||
|
@ -352,6 +372,7 @@ var gBrowserLanguagesDialog = {
|
|||
downButton: document.getElementById("down"),
|
||||
removeButton: document.getElementById("remove"),
|
||||
onRemove: (item) => this.selectedLocaleRemoved(item),
|
||||
onReorder: () => this.recordTelemetry("reorder"),
|
||||
});
|
||||
this._selectedLocales.setItems(getLocaleDisplayInfo(selectedLocales));
|
||||
},
|
||||
|
@ -365,6 +386,9 @@ var gBrowserLanguagesDialog = {
|
|||
onChange: (item) => {
|
||||
this.hideError();
|
||||
if (item.value == "search") {
|
||||
// Record the search event here so we don't track the search from
|
||||
// the main preferences pane twice.
|
||||
this.recordTelemetry("search");
|
||||
this.loadLocalesFromAMO();
|
||||
}
|
||||
},
|
||||
|
@ -450,8 +474,10 @@ var gBrowserLanguagesDialog = {
|
|||
|
||||
async availableLanguageSelected(item) {
|
||||
if (Services.locale.availableLocales.includes(item.value)) {
|
||||
this.recordTelemetry("add");
|
||||
this.requestLocalLanguage(item);
|
||||
} else if (this.availableLangpacks.has(item.value)) {
|
||||
// Telemetry is tracked in requestRemoteLanguage.
|
||||
await this.requestRemoteLanguage(item);
|
||||
} else {
|
||||
this.showError();
|
||||
|
@ -479,7 +505,8 @@ var gBrowserLanguagesDialog = {
|
|||
let addon;
|
||||
|
||||
try {
|
||||
addon = await installFromUrl(url, hash);
|
||||
addon = await installFromUrl(
|
||||
url, hash, (installId) => this.recordTelemetry("add", {installId}));
|
||||
} catch (e) {
|
||||
this.showError();
|
||||
return;
|
||||
|
@ -535,6 +562,8 @@ var gBrowserLanguagesDialog = {
|
|||
},
|
||||
|
||||
async selectedLocaleRemoved(item) {
|
||||
this.recordTelemetry("remove");
|
||||
|
||||
this._availableLocales.addItem(item);
|
||||
|
||||
// If the item we added is at the top of the list, it needs the label.
|
||||
|
|
|
@ -756,6 +756,15 @@ var gMainPane = {
|
|||
},
|
||||
|
||||
initBrowserLocale() {
|
||||
// Enable telemetry.
|
||||
Services.telemetry.setEventRecordingEnabled("intl.ui.browserLanguage", true);
|
||||
|
||||
// This will register the "command" listener.
|
||||
let menulist = document.getElementById("defaultBrowserLanguage");
|
||||
new SelectionChangedMenulist(menulist, event => {
|
||||
gMainPane.onBrowserLanguageChange(event);
|
||||
});
|
||||
|
||||
gMainPane.setBrowserLocales(Services.locale.appLocaleAsBCP47);
|
||||
},
|
||||
|
||||
|
@ -794,11 +803,6 @@ var gMainPane = {
|
|||
menupopup.appendChild(fragment);
|
||||
menulist.value = selected;
|
||||
|
||||
// This will register the "command" listener.
|
||||
new SelectionChangedMenulist(menulist, event => {
|
||||
gMainPane.onBrowserLanguageChange(event);
|
||||
});
|
||||
|
||||
document.getElementById("browserLanguagesBox").hidden = false;
|
||||
},
|
||||
|
||||
|
@ -867,6 +871,9 @@ var gMainPane = {
|
|||
let locales = localesString.split(",");
|
||||
Services.locale.requestedLocales = locales;
|
||||
|
||||
// Record the change in telemetry before we restart.
|
||||
gMainPane.recordBrowserLanguagesTelemetry("apply");
|
||||
|
||||
// Restart with the new locale.
|
||||
let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
|
||||
Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
|
||||
|
@ -887,6 +894,9 @@ var gMainPane = {
|
|||
return;
|
||||
}
|
||||
|
||||
// Note the change in telemetry.
|
||||
gMainPane.recordBrowserLanguagesTelemetry("reorder");
|
||||
|
||||
let locales = Array.from(new Set([
|
||||
locale,
|
||||
...Services.locale.requestedLocales,
|
||||
|
@ -1024,8 +1034,17 @@ var gMainPane = {
|
|||
gSubDialog.open("chrome://browser/content/preferences/languages.xul");
|
||||
},
|
||||
|
||||
recordBrowserLanguagesTelemetry(method, value = null) {
|
||||
Services.telemetry.recordEvent("intl.ui.browserLanguage", method, "main", value);
|
||||
},
|
||||
|
||||
showBrowserLanguages({search}) {
|
||||
let opts = {selected: gMainPane.selectedLocales, search};
|
||||
// Record the telemetry event with an id to associate related actions.
|
||||
let telemetryId = parseInt(Services.telemetry.msSinceProcessStart(), 10).toString();
|
||||
let method = search ? "search" : "manage";
|
||||
gMainPane.recordBrowserLanguagesTelemetry(method, telemetryId);
|
||||
|
||||
let opts = {selected: gMainPane.selectedLocales, search, telemetryId};
|
||||
gSubDialog.open(
|
||||
"chrome://browser/content/preferences/browserLanguages.xul",
|
||||
null, opts, this.browserLanguagesClosed);
|
||||
|
@ -1033,9 +1052,11 @@ var gMainPane = {
|
|||
|
||||
/* Show or hide the confirm change message bar based on the updated ordering. */
|
||||
browserLanguagesClosed() {
|
||||
let selected = this.gBrowserLanguagesDialog.selected;
|
||||
let {accepted, selected} = this.gBrowserLanguagesDialog;
|
||||
let active = Services.locale.appLocalesAsBCP47;
|
||||
|
||||
this.gBrowserLanguagesDialog.recordTelemetry(accepted ? "accept" : "cancel");
|
||||
|
||||
// Prepare for changing the locales if they are different than the current locales.
|
||||
if (selected && selected.join(",") != active.join(",")) {
|
||||
gMainPane.showConfirmLanguageChangeMessageBar(selected);
|
||||
|
|
|
@ -9,6 +9,7 @@ AddonTestUtils.initMochitest(this);
|
|||
|
||||
const BROWSER_LANGUAGES_URL = "chrome://browser/content/preferences/browserLanguages.xul";
|
||||
const DICTIONARY_ID_PL = "pl@dictionaries.addons.mozilla.org";
|
||||
const TELEMETRY_CATEGORY = "intl.ui.browserLanguage";
|
||||
|
||||
function langpackId(locale) {
|
||||
return `langpack-${locale}@firefox.mozilla.org`;
|
||||
|
@ -149,6 +150,26 @@ function assertAvailableLocales(list, locales) {
|
|||
is(items[0].getAttribute("class"), "label-item", "The first row is a label");
|
||||
}
|
||||
|
||||
function getDialogId(dialogDoc) {
|
||||
return dialogDoc.ownerGlobal.arguments[0].telemetryId;
|
||||
}
|
||||
|
||||
function assertTelemetryRecorded(events) {
|
||||
let snapshot = Services.telemetry.snapshotEvents(
|
||||
Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true);
|
||||
|
||||
// Make sure we got some data.
|
||||
ok(snapshot.parent && snapshot.parent.length > 0, "Got parent telemetry events in the snapshot");
|
||||
|
||||
// Only look at the related events after stripping the timestamp and category.
|
||||
let relatedEvents = snapshot.parent
|
||||
.filter(([timestamp, category]) => category == TELEMETRY_CATEGORY)
|
||||
.map(relatedEvent => relatedEvent.slice(2, 6));
|
||||
|
||||
// Events are now an array of: method, object[, value[, extra]] as expected.
|
||||
Assert.deepEqual(relatedEvents, events, "The events are recorded correctly");
|
||||
}
|
||||
|
||||
function selectLocale(localeCode, available, dialogDoc) {
|
||||
let [locale] = Array.from(available.firstElementChild.children)
|
||||
.filter(item => item.value == localeCode);
|
||||
|
@ -248,8 +269,23 @@ add_task(async function testDisabledBrowserLanguages() {
|
|||
is(pl.userDisabled, false, "pl is now enabled");
|
||||
is(pl.version, "2.0", "pl is upgraded to version 2.0");
|
||||
|
||||
let dialogId = getDialogId(dialogDoc);
|
||||
ok(dialogId, "There's a dialogId");
|
||||
let {installId} = pl.install;
|
||||
ok(installId, "There's an installId");
|
||||
|
||||
await Promise.all(addons.map(addon => addon.uninstall()));
|
||||
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
|
||||
// FIXME: Also here
|
||||
assertTelemetryRecorded([
|
||||
["manage", "main", dialogId],
|
||||
["search", "dialog", dialogId],
|
||||
["add", "dialog", dialogId, {installId}],
|
||||
|
||||
// Cancel is recorded when the tab is closed.
|
||||
["cancel", "dialog", dialogId],
|
||||
]);
|
||||
});
|
||||
|
||||
add_task(async function testReorderingBrowserLanguages() {
|
||||
|
@ -277,6 +313,7 @@ add_task(async function testReorderingBrowserLanguages() {
|
|||
|
||||
// Open the dialog.
|
||||
let {dialog, dialogDoc, selected} = await openDialog(doc);
|
||||
let firstDialogId = getDialogId(dialogDoc);
|
||||
|
||||
// The initial order is set by the pref, filtered by available.
|
||||
assertLocaleOrder(selected, "en-US,pl,he");
|
||||
|
@ -298,6 +335,7 @@ add_task(async function testReorderingBrowserLanguages() {
|
|||
let newDialog = await openDialog(doc);
|
||||
dialog = newDialog.dialog;
|
||||
dialogDoc = newDialog.dialogDoc;
|
||||
let secondDialogId = getDialogId(dialogDoc);
|
||||
selected = newDialog.selected;
|
||||
|
||||
// The initial order comes from the previous settings.
|
||||
|
@ -315,9 +353,22 @@ add_task(async function testReorderingBrowserLanguages() {
|
|||
await dialogClosed;
|
||||
is(messageBar.hidden, true, "The message bar is hidden again");
|
||||
|
||||
await Promise.all(addons.map(addon => addon.uninstall()));
|
||||
ok(firstDialogId, "There was an id on the first dialog");
|
||||
ok(secondDialogId, "There was an id on the second dialog");
|
||||
ok(firstDialogId != secondDialogId, "The dialog ids are different");
|
||||
ok(firstDialogId < secondDialogId, "The second dialog id is larger than the first");
|
||||
|
||||
await Promise.all(addons.map(addon => addon.uninstall()));
|
||||
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
|
||||
assertTelemetryRecorded([
|
||||
["manage", "main", firstDialogId],
|
||||
["reorder", "dialog", firstDialogId],
|
||||
["accept", "dialog", firstDialogId],
|
||||
["manage", "main", secondDialogId],
|
||||
["reorder", "dialog", secondDialogId],
|
||||
["accept", "dialog", secondDialogId],
|
||||
]);
|
||||
});
|
||||
|
||||
add_task(async function testAddAndRemoveSelectedLanguages() {
|
||||
|
@ -344,6 +395,7 @@ add_task(async function testAddAndRemoveSelectedLanguages() {
|
|||
|
||||
// Open the dialog.
|
||||
let {dialog, dialogDoc, available, selected} = await openDialog(doc);
|
||||
let dialogId = getDialogId(dialogDoc);
|
||||
|
||||
// The initial order is set by the pref.
|
||||
assertLocaleOrder(selected, "en-US");
|
||||
|
@ -382,8 +434,21 @@ add_task(async function testAddAndRemoveSelectedLanguages() {
|
|||
"The locales are set on the message bar button");
|
||||
|
||||
await Promise.all(addons.map(addon => addon.uninstall()));
|
||||
|
||||
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
|
||||
assertTelemetryRecorded([
|
||||
["manage", "main", dialogId],
|
||||
|
||||
// Install id is not recorded since it was already installed.
|
||||
["add", "dialog", dialogId],
|
||||
["add", "dialog", dialogId],
|
||||
|
||||
["remove", "dialog", dialogId],
|
||||
["remove", "dialog", dialogId],
|
||||
|
||||
["add", "dialog", dialogId],
|
||||
["accept", "dialog", dialogId],
|
||||
]);
|
||||
});
|
||||
|
||||
add_task(async function testInstallFromAMO() {
|
||||
|
@ -414,6 +479,7 @@ add_task(async function testInstallFromAMO() {
|
|||
|
||||
// Open the dialog.
|
||||
let {dialog, dialogDoc, available, selected} = await openDialog(doc, true);
|
||||
let firstDialogId = getDialogId(dialogDoc);
|
||||
|
||||
// Make sure the message bar is still hidden.
|
||||
is(messageBar.hidden, true, "The message bar is still hidden after searching");
|
||||
|
@ -445,6 +511,12 @@ add_task(async function testInstallFromAMO() {
|
|||
{childList: true},
|
||||
target => selectedLocales.itemCount == 2);
|
||||
|
||||
let langpack = await AddonManager.getAddonByID(langpackId("pl"));
|
||||
Assert.deepEqual(
|
||||
langpack.installTelemetryInfo,
|
||||
{source: "about:preferences"},
|
||||
"The source is set to preferences");
|
||||
|
||||
// Verify the list is correct.
|
||||
assertLocaleOrder(selected, "pl,en-US");
|
||||
assertAvailableLocales(available, ["fr", "he"]);
|
||||
|
@ -472,10 +544,12 @@ add_task(async function testInstallFromAMO() {
|
|||
await dialogClosed;
|
||||
|
||||
// Disable the Polish langpack.
|
||||
let langpack = await AddonManager.getAddonByID("langpack-pl@firefox.mozilla.org");
|
||||
langpack = await AddonManager.getAddonByID("langpack-pl@firefox.mozilla.org");
|
||||
let installId = langpack.install.installId;
|
||||
await langpack.disable();
|
||||
|
||||
({dialogDoc, available, selected} = await openDialog(doc, true));
|
||||
let secondDialogId = getDialogId(dialogDoc);
|
||||
|
||||
// Wait for the available langpacks to load.
|
||||
if (available.itemCount == 1) {
|
||||
|
@ -493,6 +567,22 @@ add_task(async function testInstallFromAMO() {
|
|||
await Promise.all(installs.map(item => item.uninstall()));
|
||||
|
||||
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
|
||||
ok(installId, "The langpack has an installId");
|
||||
// FIXME: Most are here
|
||||
assertTelemetryRecorded([
|
||||
// First dialog installs a locale and accepts.
|
||||
["search", "main", firstDialogId],
|
||||
// It has an installId since it was downloaded.
|
||||
["add", "dialog", firstDialogId, {installId}],
|
||||
// It got moved down to avoid errors with finding translations.
|
||||
["reorder", "dialog", firstDialogId],
|
||||
["accept", "dialog", firstDialogId],
|
||||
|
||||
// The second dialog just checks the state and is closed with the tab.
|
||||
["search", "main", secondDialogId],
|
||||
["cancel", "dialog", secondDialogId],
|
||||
]);
|
||||
});
|
||||
|
||||
let hasSearchOption = popup => Array.from(popup.children).some(el => el.value == "search");
|
||||
|
@ -537,3 +627,55 @@ add_task(async function testDownloadDisabled() {
|
|||
|
||||
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
});
|
||||
|
||||
add_task(async function testReorderMainPane() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["intl.multilingual.enabled", true],
|
||||
["intl.multilingual.downloadEnabled", false],
|
||||
["intl.locale.requested", "en-US"],
|
||||
["extensions.langpacks.signatures.required", false],
|
||||
],
|
||||
});
|
||||
|
||||
// Clear the telemetry from other tests.
|
||||
Services.telemetry.clearEvents();
|
||||
|
||||
let langpacks = await createTestLangpacks();
|
||||
let addons = await Promise.all(langpacks.map(async ([locale, file]) => {
|
||||
let install = await AddonTestUtils.promiseInstallFile(file);
|
||||
return install.addon;
|
||||
}));
|
||||
|
||||
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
|
||||
let doc = gBrowser.contentDocument;
|
||||
|
||||
let messageBar = doc.getElementById("confirmBrowserLanguage");
|
||||
is(messageBar.hidden, true, "The message bar is hidden at first");
|
||||
|
||||
let available = doc.getElementById("defaultBrowserLanguage");
|
||||
let availableLocales = Array.from(available.firstElementChild.children);
|
||||
let availableCodes = availableLocales.map(item => item.value).sort().join(",");
|
||||
is(availableCodes, "en-US,fr,he,pl",
|
||||
"All of the available locales are listed");
|
||||
|
||||
is(available.selectedItem.value, "en-US", "English is selected");
|
||||
|
||||
let hebrew = availableLocales[availableLocales.findIndex(item => item.value == "he")];
|
||||
hebrew.click();
|
||||
available.firstElementChild.hidePopup();
|
||||
|
||||
await BrowserTestUtils.waitForCondition(
|
||||
() => !messageBar.hidden, "Wait for message bar to show");
|
||||
|
||||
is(messageBar.hidden, false, "The message bar is now shown");
|
||||
is(messageBar.querySelector("button").getAttribute("locales"), "he,en-US",
|
||||
"The locales are set on the message bar button");
|
||||
|
||||
await Promise.all(addons.map(addon => addon.uninstall()));
|
||||
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
|
||||
assertTelemetryRecorded([
|
||||
["reorder", "main"],
|
||||
]);
|
||||
});
|
||||
|
|
|
@ -652,3 +652,34 @@ security.ui.identitypopup:
|
|||
cr: Whether Cookie Restrictions was active while the user interacted with the UI
|
||||
products:
|
||||
- firefox
|
||||
|
||||
intl.ui.browserLanguage:
|
||||
action:
|
||||
description: >
|
||||
User interactions for the browser language within about-preferences in the main pane and in
|
||||
the browser language dialog. Each dialog event (on the dialog object, and the manage and
|
||||
search methods of the main object) has a value which is a monotonically increasing number
|
||||
that links it with other events related to the same dialog instance.
|
||||
objects:
|
||||
- dialog
|
||||
- main
|
||||
methods:
|
||||
- manage
|
||||
- search
|
||||
- add
|
||||
- remove
|
||||
- reorder
|
||||
- apply
|
||||
- accept
|
||||
- cancel
|
||||
extra_keys:
|
||||
installId: The id for an install.
|
||||
products:
|
||||
- firefox
|
||||
expiry_version: "70"
|
||||
notification_emails:
|
||||
- flod@mozilla.com
|
||||
- mstriemer@mozilla.com
|
||||
release_channel_collection: opt-out
|
||||
record_in_processes: ["main"]
|
||||
bug_numbers: [1486507]
|
||||
|
|
Загрузка…
Ссылка в новой задаче