зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1338713 Extension install telemetry r=bsmedberg,rhelmer
MozReview-Commit-ID: KFd7k7zaDL6 --HG-- extra : rebase_source : 3aabdbafb1c91d49b76813c2400a2e48a3909fff extra : source : a4b9c0de633a13ce350500f5b618efbc45acf89c
This commit is contained in:
Родитель
b91c8d76ac
Коммит
97e0cc9666
|
@ -172,6 +172,8 @@ add_task(function* () {
|
|||
yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
});
|
||||
|
||||
hookExtensionsTelemetry();
|
||||
|
||||
let changePromise = new Promise(resolve => {
|
||||
ExtensionsUI.on("change", function listener() {
|
||||
ExtensionsUI.off("change", listener);
|
||||
|
@ -327,6 +329,9 @@ add_task(function* () {
|
|||
addon4 = yield AddonManager.getAddonByID(ID4);
|
||||
is(addon4.userDisabled, false, "Addon 4 should be enabled");
|
||||
|
||||
// We should have recorded 1 cancelled followed by 3 accepted sideloads.
|
||||
expectTelemetry(["sideloadRejected", "sideloadAccepted", "sideloadAccepted", "sideloadAccepted"]);
|
||||
|
||||
isnot(menuButton.getAttribute("badge-status"), "addon-alert", "Should no longer have addon alert badge");
|
||||
|
||||
yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
|
|
|
@ -52,6 +52,8 @@ add_task(function* setup() {
|
|||
});
|
||||
});
|
||||
|
||||
hookExtensionsTelemetry();
|
||||
|
||||
// Helper function to test background updates.
|
||||
function* backgroundUpdateTest(url, id, checkIconFn) {
|
||||
yield SpecialPowers.pushPrefEnv({set: [
|
||||
|
@ -163,6 +165,9 @@ function* backgroundUpdateTest(url, id, checkIconFn) {
|
|||
|
||||
is(getBadgeStatus(), "", "Addon alert badge should be gone");
|
||||
|
||||
// Should have recorded 1 canceled followed by 1 accepted update.
|
||||
expectTelemetry(["updateRejected", "updateAccepted"]);
|
||||
|
||||
addon.uninstall();
|
||||
yield SpecialPowers.popPrefEnv();
|
||||
}
|
||||
|
|
|
@ -39,4 +39,4 @@ async function installSearch(filename) {
|
|||
EventUtils.synthesizeMouseAtCenter(button, {}, win);
|
||||
}
|
||||
|
||||
add_task(() => testInstallMethod(installSearch));
|
||||
add_task(() => testInstallMethod(installSearch, "installAmo"));
|
||||
|
|
|
@ -11,4 +11,4 @@ async function installTrigger(filename) {
|
|||
});
|
||||
}
|
||||
|
||||
add_task(() => testInstallMethod(installTrigger));
|
||||
add_task(() => testInstallMethod(installTrigger, "installAmo"));
|
||||
|
|
|
@ -20,4 +20,4 @@ async function installFile(filename) {
|
|||
contentWin.gViewController.doCommand("cmd_installFromFile");
|
||||
}
|
||||
|
||||
add_task(() => testInstallMethod(installFile));
|
||||
add_task(() => testInstallMethod(installFile, "installLocal"));
|
||||
|
|
|
@ -11,4 +11,4 @@ async function installMozAM(filename) {
|
|||
});
|
||||
}
|
||||
|
||||
add_task(() => testInstallMethod(installMozAM));
|
||||
add_task(() => testInstallMethod(installMozAM, "installAmo"));
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
const BASE = getRootDirectory(gTestPath)
|
||||
.replace("chrome://mochitests/content/", "https://example.com/");
|
||||
|
||||
Cu.import("resource:///modules/ExtensionsUI.jsm");
|
||||
|
||||
/**
|
||||
* Wait for the given PopupNotification to display
|
||||
*
|
||||
|
@ -201,10 +203,13 @@ function checkNotification(panel, checkIcon, permissions) {
|
|||
* Callable that takes the name of an xpi file to install and
|
||||
* starts to install it. Should return a Promise that resolves
|
||||
* when the install is finished or rejects if the install is canceled.
|
||||
* @param {string} telemetryBase
|
||||
* If supplied, the base type for telemetry events that should be
|
||||
* recorded for this install method.
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async function testInstallMethod(installFn) {
|
||||
async function testInstallMethod(installFn, telemetryBase) {
|
||||
const PERMS_XPI = "browser_webext_permissions.xpi";
|
||||
const NO_PERMS_XPI = "browser_webext_nopermissions.xpi";
|
||||
const ID = "permissions@test.mozilla.org";
|
||||
|
@ -217,6 +222,10 @@ async function testInstallMethod(installFn) {
|
|||
["extensions.webextPermissionPrompts", true],
|
||||
]});
|
||||
|
||||
if (telemetryBase !== undefined) {
|
||||
hookExtensionsTelemetry();
|
||||
}
|
||||
|
||||
let testURI = makeURI("https://example.com/");
|
||||
Services.perms.add(testURI, "install", Services.perms.ALLOW_ACTION);
|
||||
registerCleanupFunction(() => Services.perms.remove(testURI, "install"));
|
||||
|
@ -316,6 +325,12 @@ async function testInstallMethod(installFn) {
|
|||
// the extension to clean up.)
|
||||
await runOnce(PERMS_XPI, false);
|
||||
|
||||
if (telemetryBase !== undefined) {
|
||||
// Should see 2 canceled installs followed by 1 successful install
|
||||
// for this method.
|
||||
expectTelemetry([`${telemetryBase}Rejected`, `${telemetryBase}Rejected`, `${telemetryBase}Accepted`]);
|
||||
}
|
||||
|
||||
await SpecialPowers.popPrefEnv();
|
||||
}
|
||||
|
||||
|
@ -347,3 +362,21 @@ add_task(async function() {
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
let collectedTelemetry = [];
|
||||
function hookExtensionsTelemetry() {
|
||||
let originalHistogram = ExtensionsUI.histogram;
|
||||
ExtensionsUI.histogram = {
|
||||
add(value) { collectedTelemetry.push(value); },
|
||||
};
|
||||
registerCleanupFunction(() => {
|
||||
is(collectedTelemetry.length, 0, "No unexamined telemetry after test is finished");
|
||||
ExtensionsUI.histogram = originalHistogram;
|
||||
});
|
||||
}
|
||||
|
||||
function expectTelemetry(values) {
|
||||
Assert.deepEqual(values, collectedTelemetry);
|
||||
collectedTelemetry = [];
|
||||
}
|
||||
|
||||
|
|
|
@ -33,8 +33,11 @@ this.ExtensionsUI = {
|
|||
sideloaded: new Set(),
|
||||
updates: new Set(),
|
||||
sideloadListener: null,
|
||||
histogram: null,
|
||||
|
||||
init() {
|
||||
this.histogram = Services.telemetry.getHistogramById("EXTENSION_INSTALL_PROMPT_RESULT");
|
||||
|
||||
Services.obs.addObserver(this, "webextension-permission-prompt", false);
|
||||
Services.obs.addObserver(this, "webextension-update-permissions", false);
|
||||
Services.obs.addObserver(this, "webextension-install-notify", false);
|
||||
|
@ -88,13 +91,13 @@ this.ExtensionsUI = {
|
|||
});
|
||||
},
|
||||
|
||||
showAddonsManager(browser, strings, icon) {
|
||||
showAddonsManager(browser, strings, icon, histkey) {
|
||||
let global = browser.selectedBrowser.ownerGlobal;
|
||||
return global.BrowserOpenAddonsMgr("addons://list/extension").then(aomWin => {
|
||||
let aomBrowser = aomWin.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDocShell)
|
||||
.chromeEventHandler;
|
||||
return this.showPermissionsPrompt(aomBrowser, strings, icon);
|
||||
return this.showPermissionsPrompt(aomBrowser, strings, icon, histkey);
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -108,13 +111,14 @@ this.ExtensionsUI = {
|
|||
permissions: addon.userPermissions,
|
||||
type: "sideload",
|
||||
});
|
||||
this.showAddonsManager(browser, strings, addon.iconURL).then(answer => {
|
||||
addon.userDisabled = !answer;
|
||||
});
|
||||
this.showAddonsManager(browser, strings, addon.iconURL, "sideload")
|
||||
.then(answer => {
|
||||
addon.userDisabled = !answer;
|
||||
});
|
||||
},
|
||||
|
||||
showUpdate(browser, info) {
|
||||
this.showAddonsManager(browser, info.strings, info.addon.iconURL)
|
||||
this.showAddonsManager(browser, info.strings, info.addon.iconURL, "update")
|
||||
.then(answer => {
|
||||
if (answer) {
|
||||
info.resolve();
|
||||
|
@ -147,13 +151,27 @@ this.ExtensionsUI = {
|
|||
return;
|
||||
}
|
||||
|
||||
this.showPermissionsPrompt(target, strings, info.icon).then(answer => {
|
||||
if (answer) {
|
||||
info.resolve();
|
||||
} else {
|
||||
info.reject();
|
||||
}
|
||||
});
|
||||
let histkey;
|
||||
if (info.type == "sideload") {
|
||||
histkey = "sideload";
|
||||
} else if (info.type == "update") {
|
||||
histkey = "update";
|
||||
} else if (info.source == "AMO") {
|
||||
histkey = "installAmo";
|
||||
} else if (info.source == "local") {
|
||||
histkey = "installLocal";
|
||||
} else {
|
||||
histkey = "installWeb";
|
||||
}
|
||||
|
||||
this.showPermissionsPrompt(target, strings, info.icon, histkey)
|
||||
.then(answer => {
|
||||
if (answer) {
|
||||
info.resolve();
|
||||
} else {
|
||||
info.reject();
|
||||
}
|
||||
});
|
||||
} else if (topic == "webextension-update-permissions") {
|
||||
let info = subject.wrappedJSObject;
|
||||
info.type = "update";
|
||||
|
@ -307,7 +325,7 @@ this.ExtensionsUI = {
|
|||
return result;
|
||||
},
|
||||
|
||||
showPermissionsPrompt(browser, strings, icon) {
|
||||
showPermissionsPrompt(browser, strings, icon, histkey) {
|
||||
function eventCallback(topic) {
|
||||
if (topic == "showing") {
|
||||
let doc = this.browser.ownerDocument;
|
||||
|
@ -349,13 +367,19 @@ this.ExtensionsUI = {
|
|||
let action = {
|
||||
label: strings.acceptText,
|
||||
accessKey: strings.acceptKey,
|
||||
callback: () => resolve(true),
|
||||
callback: () => {
|
||||
this.histogram.add(histkey + "Accepted");
|
||||
resolve(true);
|
||||
},
|
||||
};
|
||||
let secondaryActions = [
|
||||
{
|
||||
label: strings.cancelText,
|
||||
accessKey: strings.cancelKey,
|
||||
callback: () => resolve(false),
|
||||
callback: () => {
|
||||
this.histogram.add(histkey + "Rejected");
|
||||
resolve(false);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -11077,5 +11077,14 @@
|
|||
"bug_numbers": [1307689],
|
||||
"description": "Records a value each time a tab that is showing the loading throbber is interrupted. Desktop only.",
|
||||
"labels": ["stop", "back", "forward", "historyNavigation", "reload", "tabClosed", "newURI"]
|
||||
},
|
||||
"EXTENSION_INSTALL_PROMPT_RESULT": {
|
||||
"alert_emails": ["aswan@mozilla.com", "andym@mozilla.com"],
|
||||
"bug_numbers": [1338713],
|
||||
"expires_in_version": "60",
|
||||
"kind": "categorical",
|
||||
"labels": ["installAmoAccepted", "installAmoRejected", "installLocalAccepted", "installLocalRejected", "installWebAccepted", "installWebRejected", "sideloadAccepted", "sideloadRejected", "updateAccepted", "updateRejected"],
|
||||
"description": "Results of displaying add-on installation notifications.",
|
||||
"releaseChannelCollection": "opt-out"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2191,17 +2191,17 @@ var AddonManagerInternal = {
|
|||
// install in that case.
|
||||
new BrowserListener(aBrowser, aInstallingPrincipal, aInstall);
|
||||
|
||||
AddonManagerInternal.setupPromptHandler(aBrowser, aInstallingPrincipal.URI, aInstall, true);
|
||||
let startInstall = (source) => {
|
||||
AddonManagerInternal.setupPromptHandler(aBrowser, aInstallingPrincipal.URI, aInstall, true, source);
|
||||
|
||||
let startInstall = () => {
|
||||
AddonManagerInternal.startInstall(aBrowser, aInstallingPrincipal.URI, aInstall);
|
||||
};
|
||||
if (!this.isInstallAllowed(aMimetype, aInstallingPrincipal)) {
|
||||
this.installNotifyObservers("addon-install-blocked", topBrowser,
|
||||
aInstallingPrincipal.URI, aInstall,
|
||||
startInstall);
|
||||
() => startInstall("other"));
|
||||
} else {
|
||||
startInstall();
|
||||
startInstall("AMO");
|
||||
}
|
||||
} catch (e) {
|
||||
// In the event that the weblistener throws during instantiation or when
|
||||
|
@ -2228,7 +2228,7 @@ var AddonManagerInternal = {
|
|||
throw Components.Exception("AddonManager is not initialized",
|
||||
Cr.NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
AddonManagerInternal.setupPromptHandler(browser, uri, install, true);
|
||||
AddonManagerInternal.setupPromptHandler(browser, uri, install, true, "local");
|
||||
AddonManagerInternal.startInstall(browser, uri, install);
|
||||
},
|
||||
|
||||
|
@ -2825,7 +2825,7 @@ var AddonManagerInternal = {
|
|||
return gHotfixID;
|
||||
},
|
||||
|
||||
setupPromptHandler(browser, url, install, requireConfirm) {
|
||||
setupPromptHandler(browser, url, install, requireConfirm, source) {
|
||||
install.promptHandler = info => new Promise((resolve, _reject) => {
|
||||
let reject = () => {
|
||||
this.installNotifyObservers("addon-install-cancelled",
|
||||
|
@ -2852,7 +2852,7 @@ var AddonManagerInternal = {
|
|||
let subject = {
|
||||
wrappedJSObject: {
|
||||
target: browser,
|
||||
info: Object.assign({resolve, reject}, info),
|
||||
info: Object.assign({resolve, reject, source}, info),
|
||||
}
|
||||
};
|
||||
subject.wrappedJSObject.info.permissions = info.addon.userPermissions;
|
||||
|
@ -3041,7 +3041,7 @@ var AddonManagerInternal = {
|
|||
|
||||
return AddonManagerInternal.getInstallForURL(options.url, "application/x-xpinstall", options.hash)
|
||||
.then(install => {
|
||||
AddonManagerInternal.setupPromptHandler(target, null, install, false);
|
||||
AddonManagerInternal.setupPromptHandler(target, null, install, false, "AMO");
|
||||
|
||||
let id = this.nextInstall++;
|
||||
let {listener, installPromise} = this.makeListener(id, target.messageManager);
|
||||
|
|
|
@ -665,6 +665,7 @@
|
|||
.getInterface(Ci.nsIDocShell).chromeEventHandler,
|
||||
info: {
|
||||
addon: info.addon,
|
||||
source: "AMO",
|
||||
icon: info.addon.iconURL,
|
||||
permissions: info.addon.userPermissions,
|
||||
resolve,
|
||||
|
|
Загрузка…
Ссылка в новой задаче