bug 1071880 - Notify user of addons that are slowing their browser down significantly r=mossop

This commit is contained in:
Brad Lassey 2015-02-12 19:22:53 -05:00
parent 6bfb1f09b4
commit 334118dc00
5 changed files with 176 additions and 0 deletions

View File

@ -138,6 +138,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "WebChannel",
XPCOMUtils.defineLazyModuleGetter(this, "ReaderParent",
"resource:///modules/ReaderParent.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AddonWatcher",
"resource://gre/modules/AddonWatcher.jsm");
const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser";
const PREF_PLUGINS_UPDATEURL = "plugins.update.url";
@ -564,6 +567,76 @@ BrowserGlue.prototype = {
this._distributionCustomizer.applyPrefDefaults();
},
_notifySlowAddon: function BG_notifySlowAddon(addonId) {
let addonCallback = function(addon) {
if (!addon) {
Cu.reportError("couldn't look up addon: " + addonId);
return;
}
let win = RecentWindow.getMostRecentBrowserWindow();
if (!win) {
return;
}
let brandBundle = win.document.getElementById("bundle_brand");
let brandShortName = brandBundle.getString("brandShortName");
let message = win.gNavigatorBundle.getFormattedString("addonwatch.slow", [addon.name, brandShortName]);
let notificationBox = win.document.getElementById("global-notificationbox");
let notificationId = 'addon-slow:' + addonId;
let notification = notificationBox.getNotificationWithValue(notificationId);
if(notification) {
notification.label = message;
} else {
let buttons = [
{
label: win.gNavigatorBundle.getFormattedString("addonwatch.disable.label", [addon.name]),
accessKey: win.gNavigatorBundle.getString("addonwatch.disable.accesskey"),
callback: function() {
addon.userDisabled = true;
if (addon.pendingOperations != addon.PENDING_NONE) {
let restartMessage = win.gNavigatorBundle.getFormattedString("addonwatch.restart.message", [addon.name, brandShortName]);
let restartButton = [
{
label: win.gNavigatorBundle.getFormattedString("addonwatch.restart.label", [brandShortName]),
accessKey: win.gNavigatorBundle.getString("addonwatch.restart.accesskey"),
callback: function() {
let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]
.getService(Ci.nsIAppStartup);
appStartup.quit(appStartup.eForceQuit | appStartup.eRestart);
}
}
];
const priority = notificationBox.PRIORITY_WARNING_MEDIUM;
notificationBox.appendNotification(restartMessage, "restart-" + addonId, "",
priority, restartButton);
}
}
},
{
label: win.gNavigatorBundle.getString("addonwatch.ignoreSession.label"),
accessKey: win.gNavigatorBundle.getString("addonwatch.ignoreSession.accesskey"),
callback: function() {
AddonWatcher.ignoreAddonForSession(addonId);
}
},
{
label: win.gNavigatorBundle.getString("addonwatch.ignorePerm.label"),
accessKey: win.gNavigatorBundle.getString("addonwatch.ignorePerm.accesskey"),
callback: function() {
AddonWatcher.ignoreAddonPermanently(addonId);
}
},
];
const priority = notificationBox.PRIORITY_WARNING_MEDIUM;
notificationBox.appendNotification(message, notificationId, "",
priority, buttons);
}
};
AddonManager.getAddonByID(addonId, addonCallback);
},
// runs on startup, before the first command line handler is invoked
// (i.e. before the first window is opened)
_finalUIStartup: function BG__finalUIStartup() {
@ -612,6 +685,8 @@ BrowserGlue.prototype = {
#endif
Services.obs.notifyObservers(null, "browser-ui-startup-complete", "");
AddonWatcher.init(this._notifySlowAddon);
},
_checkForOldBuildUpdates: function () {

View File

@ -40,6 +40,17 @@ addonDownloadRestart=Restart Download;Restart Downloads
addonDownloadRestart.accessKey=R
addonDownloadCancelTooltip=Cancel
addonwatch.slow=%S might be making %S run slowly
addonwatch.disable.label=Disable %S
addonwatch.disable.accesskey=D
addonwatch.ignoreSession.label=Ignore for now
addonwatch.ignoreSession.accesskey=I
addonwatch.ignorePerm.label=Ignore permanently
addonwatch.ignorePerm.accesskey=p
addonwatch.restart.message=To disable %S you must restart %S
addonwatch.restart.label=Restart %s
addonwatch.restart.accesskey=R
# LOCALIZATION NOTE (addonsInstalled, addonsInstalledNeedsRestart):
# Semicolon-separated list of plural forms. See:
# http://developer.mozilla.org/en/docs/Localization_and_Plurals

View File

@ -4466,6 +4466,12 @@ pref("dom.mozSettings.SettingsService.verbose.enabled", false);
// readwrite.
pref("dom.mozSettings.allowForceReadOnly", false);
// The interval at which to check for slow running addons
pref("browser.addon-watch.interval", 120000);
pref("browser.addon-watch.ignore", "[\"mochikit@mozilla.org\",\"special-powers@mozilla.org\"]");
// the percentage of time addons are allowed to use without being labeled slow
pref("browser.addon-watch.percentage-limit", 1);
// RequestSync API is disabled by default.
pref("dom.requestSync.enabled", false);

View File

@ -0,0 +1,83 @@
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
/* 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";
this.EXPORTED_SYMBOLS = ["AddonWatcher"];
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
"resource://gre/modules/Preferences.jsm");
let AddonWatcher = {
_lastAddonTime: {},
_timer: Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer),
_callback: null,
_interval: 1500,
_ignoreList: null,
init: function(callback) {
if (!callback) {
return;
}
if (this._callback) {
return;
}
this._callback = callback;
try {
this._ignoreList = new Set(JSON.parse(Preferences.get("browser.addon-watch.ignore", null)));
} catch (ex) {
// probably some malformed JSON, ignore and carry on
this._ignoreList = new Set();
}
this._interval = Preferences.get("browser.addon-watch.interval", 15000);
this._timer.initWithCallback(this._checkAddons.bind(this), this._interval, Ci.nsITimer.TYPE_REPEATING_SLACK);
},
_checkAddons: function() {
let compartmentInfo = Cc["@mozilla.org/compartment-info;1"]
.getService(Ci.nsICompartmentInfo);
let compartments = compartmentInfo.getCompartments();
let count = compartments.length;
let addons = {};
for (let i = 0; i < count; i++) {
let compartment = compartments.queryElementAt(i, Ci.nsICompartment);
if (compartment.addonId) {
if (addons[compartment.addonId]) {
addons[compartment.addonId] += compartment.time;
} else {
addons[compartment.addonId] = compartment.time;
}
}
}
let limit = this._interval * Preferences.get("browser.addon-watch.percentage-limit", 75) * 10;
for (let addonId in addons) {
if (!this._ignoreList.has(addonId)) {
if (this._lastAddonTime[addonId] && (addons[addonId] - this._lastAddonTime[addonId]) > limit) {
this._callback(addonId);
}
this._lastAddonTime[addonId] = addons[addonId];
}
}
},
ignoreAddonForSession: function(addonid) {
this._ignoreList.add(addonid);
},
ignoreAddonPermanently: function(addonid) {
this._ignoreList.add(addonid);
try {
let ignoreList = JSON.parse(Preferences.get("browser.addon-watch.ignore", "[]"))
if (!ignoreList.includes(addonid)) {
ignoreList.push(addonid);
Preferences.set("browser.addon-watch.ignore", JSON.stringify(ignoreList));
}
} catch (ex) {
Preferences.set("browser.addon-watch.ignore", JSON.stringify([addonid]));
}
}
};

View File

@ -12,6 +12,7 @@ MOCHITEST_CHROME_MANIFESTS += ['tests/chrome/chrome.ini']
SPHINX_TREES['toolkit_modules'] = 'docs'
EXTRA_JS_MODULES += [
'AddonWatcher.jsm',
'Battery.jsm',
'BinarySearch.jsm',
'BrowserUtils.jsm',