зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1189799 - Make sure that about:performance displays each add-on only once (front-end);r=felipe
--HG-- extra : transplant_source : %FC%F1%C0N%F9L%D2%1A%EAwF%0E%94%B8%A4%29%FF%EA5%A7
This commit is contained in:
Родитель
7a8b91b810
Коммит
131d787115
|
@ -13,6 +13,7 @@ const { AddonWatcher } = Cu.import("resource://gre/modules/AddonWatcher.jsm", {}
|
|||
const { PerformanceStats } = Cu.import("resource://gre/modules/PerformanceStats.jsm", {});
|
||||
const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
|
||||
const { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
|
||||
const { ObjectUtils } = Cu.import("resource://gre/modules/ObjectUtils.jsm", {});
|
||||
|
||||
// about:performance observes notifications on this topic.
|
||||
// if a notification is sent, this causes the page to be updated immediately,
|
||||
|
@ -78,48 +79,214 @@ const MODE_RECENT = "recent";
|
|||
* @return {{tabbrowser: <xul:tabbrowser>, tab: <xul.tab>}} The
|
||||
* tabbrowser and tab if the latter could be found.
|
||||
*/
|
||||
function findTabFromWindow(windowId) {
|
||||
function findTabFromWindow(windowIds) {
|
||||
let windows = Services.wm.getEnumerator("navigator:browser");
|
||||
while (windows.hasMoreElements()) {
|
||||
let win = windows.getNext();
|
||||
let tabbrowser = win.gBrowser;
|
||||
let foundBrowser = tabbrowser.getBrowserForOuterWindowID(windowId);
|
||||
if (foundBrowser) {
|
||||
return {tabbrowser, tab: tabbrowser.getTabForBrowser(foundBrowser)};
|
||||
for (let windowId of windowIds) {
|
||||
let foundBrowser = tabbrowser.getBrowserForOuterWindowID(windowId);
|
||||
if (foundBrowser) {
|
||||
return {tabbrowser, tab: tabbrowser.getTabForBrowser(foundBrowser)};
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function wait(ms = 0) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
/**
|
||||
* Utilities for dealing with PerformanceDiff
|
||||
* The performance of a webpage or an add-on between two instants.
|
||||
*
|
||||
* Clients should call `promiseInit()` before using the methods of this object.
|
||||
*
|
||||
* @param {PerformanceDiff} The underlying performance data.
|
||||
* @param {"addons"|"webpages"} The kind of delta represented by this object.
|
||||
* @param {Map<groupId, timestamp>} ageMap A map containing the oldest known
|
||||
* appearance of each groupId, used to determine how long we have been monitoring
|
||||
* this item.
|
||||
* @param {Map<Delta key, Array>} alertMap A map containing the alerts that each
|
||||
* item has already triggered in the past.
|
||||
*/
|
||||
var Delta = {
|
||||
compare: function(a, b) {
|
||||
// By definition, if either `a` or `b` has CPOW time and the other
|
||||
// doesn't, it is larger.
|
||||
if (a.cpow.totalCPOWTime && !b.cpow.totalCPOWTime) {
|
||||
function Delta(diff, kind, snapshotDate, ageMap, alertMap) {
|
||||
if (kind != "addons" && kind != "webpages") {
|
||||
throw new TypeError(`Unknown kind: ${kind}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Either "addons" or "webpages".
|
||||
*/
|
||||
this.kind = kind;
|
||||
|
||||
/**
|
||||
* The underlying PerformanceDiff.
|
||||
* @type {PerformanceDiff}
|
||||
*/
|
||||
this.diff = diff;
|
||||
|
||||
/**
|
||||
* A key unique to the item (webpage or add-on), shared by successive
|
||||
* instances of `Delta`.
|
||||
* @type{string}
|
||||
*/
|
||||
this.key = kind + diff.key;
|
||||
|
||||
// Find the oldest occurrence of this item.
|
||||
let creationDate = snapshotDate;
|
||||
for (let groupId of diff.groupIds) {
|
||||
let date = ageMap.get(groupId);
|
||||
if (date && date <= creationDate) {
|
||||
creationDate = date;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The timestamp at which the data was measured.
|
||||
*/
|
||||
this.creationDate = creationDate;
|
||||
|
||||
/**
|
||||
* Number of milliseconds since the start of the measure.
|
||||
*/
|
||||
this.age = snapshotDate - creationDate;
|
||||
|
||||
/**
|
||||
* A UX-friendly, human-readable name for this item.
|
||||
*/
|
||||
this.readableName = null;
|
||||
|
||||
/**
|
||||
* A complete name, possibly useful for power users or debugging.
|
||||
*/
|
||||
this.fullName = null;
|
||||
|
||||
|
||||
// `true` once initialization is complete.
|
||||
this._initialized = false;
|
||||
// `true` if this item should be displayed
|
||||
this._show = false;
|
||||
|
||||
/**
|
||||
* All the alerts that this item has caused since about:performance
|
||||
* was opened.
|
||||
*/
|
||||
this.alerts = (alertMap.get(this.key) || []).slice();
|
||||
switch (this.slowness) {
|
||||
case 0: break;
|
||||
case 1: this.alerts[0] = (this.alerts[0] || 0) + 1; break;
|
||||
case 2: this.alerts[1] = (this.alerts[1] || 0) + 1; break;
|
||||
default: throw new Error();
|
||||
}
|
||||
}
|
||||
Delta.prototype = {
|
||||
/**
|
||||
* `true` if this item should be displayed, `false` otherwise.
|
||||
*/
|
||||
get show() {
|
||||
this._ensureInitialized();
|
||||
return this._show;
|
||||
},
|
||||
|
||||
/**
|
||||
* Estimate the slowness of this item.
|
||||
*
|
||||
* @return 0 if the item has good performance.
|
||||
* @return 1 if the item has average performance.
|
||||
* @return 2 if the item has poor performance.
|
||||
*/
|
||||
get slowness() {
|
||||
if (Delta.compare(this, Delta.MAX_DELTA_FOR_GOOD_RECENT_PERFORMANCE) <= 0) {
|
||||
return 0;
|
||||
}
|
||||
if (Delta.compare(this, Delta.MAX_DELTA_FOR_AVERAGE_RECENT_PERFORMANCE) <= 0) {
|
||||
return 1;
|
||||
}
|
||||
if (b.cpow.totalCPOWTime && !a.cpow.totalCPOWTime) {
|
||||
return -1;
|
||||
return 2;
|
||||
},
|
||||
_ensureInitialized() {
|
||||
if (!this._initialized) {
|
||||
throw new Error();
|
||||
}
|
||||
return (
|
||||
(a.jank.longestDuration - b.jank.longestDuration) ||
|
||||
(a.jank.totalUserTime - b.jank.totalUserTime) ||
|
||||
(a.jank.totalSystemTime - b.jank.totalSystemTime) ||
|
||||
(a.cpow.totalCPOWTime - b.cpow.totalCPOWTime) ||
|
||||
(a.ticks.ticks - b.ticks.ticks) ||
|
||||
0
|
||||
);
|
||||
},
|
||||
revCompare: function(a, b) {
|
||||
return -Delta.compare(a, b);
|
||||
},
|
||||
|
||||
/**
|
||||
* The highest value considered "good performance".
|
||||
* Initialize, asynchronously.
|
||||
*/
|
||||
MAX_DELTA_FOR_GOOD_RECENT_PERFORMANCE: {
|
||||
promiseInit: function() {
|
||||
if (this.kind == "webpages") {
|
||||
return this._initWebpage();
|
||||
} else if (this.kind == "addons") {
|
||||
return this._promiseInitAddon();
|
||||
}
|
||||
throw new TypeError();
|
||||
},
|
||||
_initWebpage: function() {
|
||||
this._initialized = true;
|
||||
if (!this.diff.title) {
|
||||
// Either this is not a real page or the page isn't restored yet.
|
||||
return;
|
||||
}
|
||||
|
||||
// Wallpaper hack. For some reason, about:performance (and only about:performance)
|
||||
// appears twice in the list. Only one of them is a window.
|
||||
|
||||
if (this.diff.title == document.title) {
|
||||
if (!findTabFromWindow(this.diff.windowIds)) {
|
||||
// Not a real page.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.readableName = this.diff.title;
|
||||
this.fullName = this.diff.names.join(", ");
|
||||
this._show = true;
|
||||
},
|
||||
_promiseInitAddon: Task.async(function*() {
|
||||
let found = yield (new Promise(resolve =>
|
||||
AddonManager.getAddonByID(this.diff.addonId, a => {
|
||||
if (a) {
|
||||
this.readableName = a.name;
|
||||
resolve(true);
|
||||
} else {
|
||||
resolve(false);
|
||||
}
|
||||
})));
|
||||
|
||||
this._initialized = true;
|
||||
|
||||
// If the add-on manager doesn't know about an add-on, it's
|
||||
// probably not a real add-on.
|
||||
this._show = found;
|
||||
this.fullName = this.diff.addonId;
|
||||
}),
|
||||
toString: function() {
|
||||
return `[Delta] ${this.diff.key} => ${this.readableName}, ${this.fullName}`;
|
||||
}
|
||||
};
|
||||
|
||||
Delta.compare = function(a, b) {
|
||||
return (
|
||||
(a.diff.jank.longestDuration - b.diff.jank.longestDuration) ||
|
||||
(a.diff.jank.totalUserTime - b.diff.jank.totalUserTime) ||
|
||||
(a.diff.jank.totalSystemTime - b.diff.jank.totalSystemTime) ||
|
||||
(a.diff.cpow.totalCPOWTime - b.diff.cpow.totalCPOWTime) ||
|
||||
(a.diff.ticks.ticks - b.diff.ticks.ticks) ||
|
||||
0
|
||||
);
|
||||
};
|
||||
|
||||
Delta.revCompare = function(a, b) {
|
||||
return -Delta.compare(a, b);
|
||||
};
|
||||
|
||||
/**
|
||||
* The highest value considered "good performance".
|
||||
*/
|
||||
Delta.MAX_DELTA_FOR_GOOD_RECENT_PERFORMANCE = {
|
||||
diff: {
|
||||
cpow: {
|
||||
totalCPOWTime: 0,
|
||||
},
|
||||
|
@ -131,11 +298,14 @@ var Delta = {
|
|||
ticks: {
|
||||
ticks: Number.POSITIVE_INFINITY,
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The highest value considered "average performance".
|
||||
*/
|
||||
MAX_DELTA_FOR_AVERAGE_RECENT_PERFORMANCE: {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The highest value considered "average performance".
|
||||
*/
|
||||
Delta.MAX_DELTA_FOR_AVERAGE_RECENT_PERFORMANCE = {
|
||||
diff: {
|
||||
cpow: {
|
||||
totalCPOWTime: Number.POSITIVE_INFINITY,
|
||||
},
|
||||
|
@ -148,12 +318,12 @@ var Delta = {
|
|||
ticks: Number.POSITIVE_INFINITY,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Utilities for dealing with state
|
||||
*/
|
||||
var State = {
|
||||
let State = {
|
||||
_monitor: PerformanceStats.getMonitor([
|
||||
"jank", "cpow", "ticks",
|
||||
]),
|
||||
|
@ -161,20 +331,20 @@ var State = {
|
|||
/**
|
||||
* Indexed by the number of minutes since the snapshot was taken.
|
||||
*
|
||||
* @type {Array<{snapshot: PerformanceData, date: Date}>}
|
||||
* @type {Array<ApplicationSnapshot>}
|
||||
*/
|
||||
_buffer: [],
|
||||
/**
|
||||
* The first snapshot since opening the page.
|
||||
*
|
||||
* @type {snapshot: PerformnceData, date: Date}
|
||||
* @type ApplicationSnapshot
|
||||
*/
|
||||
_oldest: null,
|
||||
|
||||
/**
|
||||
* The latest snapshot.
|
||||
*
|
||||
* @type As _oldest
|
||||
* @type ApplicationSnapshot
|
||||
*/
|
||||
_latest: null,
|
||||
|
||||
|
@ -184,8 +354,8 @@ var State = {
|
|||
* This map is cleaned up during each update to avoid leaking references
|
||||
* to groups that have been gc-ed.
|
||||
*
|
||||
* @type{Map<string, Array<number>} A map in which keys are
|
||||
* values for `delta.groupId` and values are arrays
|
||||
* @type{Map<Delta key, Array<number>} A map in which the keys are provided
|
||||
* by property `key` of instances of `Delta` and the values are arrays
|
||||
* [number of moderate-impact alerts, number of high-impact alerts]
|
||||
*/
|
||||
_alerts: new Map(),
|
||||
|
@ -196,49 +366,13 @@ var State = {
|
|||
* This map is cleaned up during each update to avoid leaking references
|
||||
* to groups that have been gc-ed.
|
||||
*
|
||||
* @type{Map<string, Date} A map in which keys are
|
||||
* @type{Map<string, timestamp} A map in which keys are
|
||||
* values for `delta.groupId` and values are approximate
|
||||
* dates at which the group was first encountered.
|
||||
* dates at which the group was first encountered, as provided
|
||||
* by `Cu.now()``.
|
||||
*/
|
||||
_firstSeen: new Map(),
|
||||
|
||||
/**
|
||||
* Produce an annotated performance snapshot.
|
||||
*
|
||||
* @return {Object} An object extending `PerformanceDiff`
|
||||
* with the following fields:
|
||||
* {Date} date The current date.
|
||||
* {Map<groupId, performanceData>} A map of performance data,
|
||||
* for faster access.
|
||||
*/
|
||||
_promiseSnapshot: Task.async(function*() {
|
||||
let snapshot = yield this._monitor.promiseSnapshot();
|
||||
snapshot.date = Date.now();
|
||||
let componentsMap = new Map();
|
||||
snapshot.componentsMap = componentsMap;
|
||||
for (let component of snapshot.componentsData) {
|
||||
componentsMap.set(component.groupId, component);
|
||||
}
|
||||
return snapshot;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Classify the "slowness" of a component.
|
||||
*
|
||||
* @return {number} 0 For a "fast" component.
|
||||
* @return {number} 1 For an "average" component.
|
||||
* @return {number} 2 For a "slow" component.
|
||||
*/
|
||||
_getSlowness: function(component) {
|
||||
if (Delta.compare(component, Delta.MAX_DELTA_FOR_GOOD_RECENT_PERFORMANCE) <= 0) {
|
||||
return 0;
|
||||
}
|
||||
if (Delta.compare(component, Delta.MAX_DELTA_FOR_AVERAGE_RECENT_PERFORMANCE) <= 0) {
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the internal state.
|
||||
*
|
||||
|
@ -250,32 +384,20 @@ var State = {
|
|||
if (this._oldest) {
|
||||
throw new Error("Internal Error, we shouldn't have a `_oldest` value yet.");
|
||||
}
|
||||
this._latest = this._oldest = yield this._promiseSnapshot();
|
||||
this._latest = this._oldest = yield this._monitor.promiseSnapshot();
|
||||
this._buffer.push(this._oldest);
|
||||
yield new Promise(resolve => setTimeout(resolve, BUFFER_SAMPLING_RATE_MS * 1.1));
|
||||
}
|
||||
|
||||
|
||||
let now = Date.now();
|
||||
|
||||
let now = Cu.now();
|
||||
|
||||
// If we haven't sampled in a while, add a sample to the buffer.
|
||||
let latestInBuffer = this._buffer[this._buffer.length - 1];
|
||||
let deltaT = now - latestInBuffer.date;
|
||||
if (deltaT > BUFFER_SAMPLING_RATE_MS) {
|
||||
this._latest = this._oldest = yield this._promiseSnapshot();
|
||||
this._latest = yield this._monitor.promiseSnapshot();
|
||||
this._buffer.push(this._latest);
|
||||
|
||||
// Update alerts
|
||||
let cleanedUpAlerts = new Map(); // A new map of alerts, without dead components.
|
||||
for (let component of this._latest.componentsData) {
|
||||
let slowness = this._getSlowness(component, deltaT);
|
||||
let myAlerts = this._alerts.get(component.groupId) || [0, 0];
|
||||
if (slowness > 0) {
|
||||
myAlerts[slowness - 1]++;
|
||||
}
|
||||
cleanedUpAlerts.set(component.groupId, myAlerts);
|
||||
this._alerts = cleanedUpAlerts;
|
||||
}
|
||||
}
|
||||
|
||||
// If we have too many samples, remove the oldest sample.
|
||||
|
@ -301,6 +423,12 @@ var State = {
|
|||
|
||||
/**
|
||||
* @return {Promise}
|
||||
* @resolve {{
|
||||
* addons: Array<Delta>,
|
||||
* webpages: Array<Delta>,
|
||||
* deltas: Set<Delta key>,
|
||||
* duration: number of milliseconds
|
||||
* }}
|
||||
*/
|
||||
_promiseDeltaSince: Task.async(function*(oldest) {
|
||||
let current = this._latest;
|
||||
|
@ -311,76 +439,47 @@ var State = {
|
|||
throw new TypeError();
|
||||
}
|
||||
|
||||
let addons = [];
|
||||
let webpages = [];
|
||||
let system = [];
|
||||
let groups = new Set();
|
||||
|
||||
// We rebuild the map during each iteration to make sure that
|
||||
// We rebuild the maps during each iteration to make sure that
|
||||
// we do not maintain references to groups that has been removed
|
||||
// (e.g. pages that have been closed).
|
||||
let oldFirstSeen = this._firstSeen;
|
||||
let cleanedUpFirstSeen = new Map();
|
||||
this._firstSeen = cleanedUpFirstSeen;
|
||||
for (let component of current.componentsData) {
|
||||
// Enrich `delta` with `alerts`.
|
||||
let delta = component.subtract(oldest.componentsMap.get(component.groupId));
|
||||
delta.alerts = (this._alerts.get(component.groupId) || []).slice();
|
||||
|
||||
// Enrich `delta` with `age`.
|
||||
let creationDate = oldFirstSeen.get(component.groupId) || current.date;
|
||||
cleanedUpFirstSeen.set(component.groupId, creationDate);
|
||||
delta.age = current.date - creationDate;
|
||||
let oldAlerts = this._alerts;
|
||||
let cleanedUpAlerts = new Map();
|
||||
|
||||
groups.add(component.groupId);
|
||||
let result = {
|
||||
addons: [],
|
||||
webpages: [],
|
||||
deltas: new Set(),
|
||||
duration: current.date - oldest.date
|
||||
};
|
||||
|
||||
// Enrich `delta` with `fullName` and `readableName`.
|
||||
delta.fullName = delta.name;
|
||||
delta.readableName = delta.name;
|
||||
if (component.addonId) {
|
||||
let found = yield new Promise(resolve =>
|
||||
AddonManager.getAddonByID(delta.addonId, a => {
|
||||
if (a) {
|
||||
delta.readableName = a.name;
|
||||
resolve(true);
|
||||
} else {
|
||||
resolve(false);
|
||||
}
|
||||
}));
|
||||
delta.fullName = delta.addonId;
|
||||
if (found) {
|
||||
// If the add-on manager doesn't know about an add-on, it's
|
||||
// probably not a real add-on.
|
||||
addons.push(delta);
|
||||
for (let kind of ["webpages", "addons"]) {
|
||||
for (let [key, value] of current[kind]) {
|
||||
let item = ObjectUtils.strict(new Delta(value.subtract(oldest[kind].get(key)), kind, current.date, oldFirstSeen, oldAlerts));
|
||||
yield item.promiseInit();
|
||||
|
||||
if (!item.show) {
|
||||
continue;
|
||||
}
|
||||
} else if (!delta.isSystem || delta.title) {
|
||||
// Wallpaper hack. For some reason, about:performance (and only about:performance)
|
||||
// appears twice in the list. Only one of them is a window.
|
||||
if (delta.title == document.title) {
|
||||
if (!findTabFromWindow(delta.windowId)) {
|
||||
// Not a real page.
|
||||
system.push(delta);
|
||||
continue;
|
||||
}
|
||||
result[kind].push(item);
|
||||
result.deltas.add(item.key);
|
||||
|
||||
for (let groupId of item.diff.groupIds) {
|
||||
cleanedUpFirstSeen.set(groupId, item.creationDate);
|
||||
}
|
||||
delta.readableName = delta.title || delta.name;
|
||||
webpages.push(delta);
|
||||
} else {
|
||||
system.push(delta);
|
||||
cleanedUpAlerts.set(item.key, item.alerts);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
addons,
|
||||
webpages,
|
||||
system,
|
||||
groups,
|
||||
duration: current.date - oldest.date
|
||||
};
|
||||
this._firstSeen = cleanedUpFirstSeen;
|
||||
this._alerts = cleanedUpAlerts;
|
||||
return result;
|
||||
}),
|
||||
};
|
||||
|
||||
var View = {
|
||||
let View = {
|
||||
/**
|
||||
* A cache for all the per-item DOM elements that are reused across refreshes.
|
||||
*
|
||||
|
@ -390,20 +489,20 @@ var View = {
|
|||
DOMCache: {
|
||||
_map: new Map(),
|
||||
/**
|
||||
* @param {string} groupId The groupId for the item that we are displaying.
|
||||
* @return {null} If the `groupId` doesn't have a component cached yet.
|
||||
* @param {string} deltaKey The key for the item that we are displaying.
|
||||
* @return {null} If the `deltaKey` doesn't have a component cached yet.
|
||||
* Otherwise, the value stored with `set`.
|
||||
*/
|
||||
get: function(groupId) {
|
||||
return this._map.get(groupId);
|
||||
get: function(deltaKey) {
|
||||
return this._map.get(deltaKey);
|
||||
},
|
||||
set: function(groupId, value) {
|
||||
this._map.set(groupId, value);
|
||||
set: function(deltaKey, value) {
|
||||
this._map.set(deltaKey, value);
|
||||
},
|
||||
/**
|
||||
* Remove all the elements whose key does not appear in `set`.
|
||||
*
|
||||
* @param {Set} set a set of groupId.
|
||||
* @param {Set} set a set of deltaKey.
|
||||
*/
|
||||
trimTo: function(set) {
|
||||
let remove = [];
|
||||
|
@ -426,7 +525,7 @@ var View = {
|
|||
* @param {string} nature The nature of the subset. One of "addons", "webpages" or "system".
|
||||
* @param {string} currentMode The current display mode. One of MODE_GLOBAL or MODE_RECENT.
|
||||
*/
|
||||
updateCategory: function(subset, id, nature, deltaT, currentMode) {
|
||||
updateCategory: function(subset, id, nature, currentMode) {
|
||||
subset = subset.slice().sort(Delta.revCompare);
|
||||
|
||||
let watcherAlerts = null;
|
||||
|
@ -440,25 +539,30 @@ var View = {
|
|||
// An array of `cachedElements` that need to be added
|
||||
let toAdd = [];
|
||||
for (let delta of subset) {
|
||||
if (!(delta instanceof Delta)) {
|
||||
throw new TypeError();
|
||||
}
|
||||
let cachedElements = this._grabOrCreateElements(delta, nature);
|
||||
toAdd.push(cachedElements);
|
||||
cachedElements.eltTitle.textContent = delta.readableName;
|
||||
cachedElements.eltName.textContent = `Full name: ${delta.fullName}.`;
|
||||
cachedElements.eltLoaded.textContent = `Measure start: ${Math.round(delta.age/1000)} seconds ago.`
|
||||
cachedElements.eltProcess.textContent = `Process: ${delta.processId} (${delta.isChildProcess?"child":"parent"}).`;
|
||||
|
||||
let processes = [for (proc of delta.diff.processes) `${proc.processId} (${proc.isChildProcess?"child":"parent"})`];
|
||||
cachedElements.eltProcess.textContent = `Processes: ${processes.join(", ")}`;
|
||||
let jankSuffix = "";
|
||||
let cpowSuffix = "";
|
||||
if (watcherAlerts) {
|
||||
let deltaAlerts = watcherAlerts.get(delta.addonId);
|
||||
let deltaAlerts = watcherAlerts.get(delta.diff.addonId);
|
||||
if (deltaAlerts) {
|
||||
jankSuffix = ` (${deltaAlerts.alerts.longestDuration} alerts)`;
|
||||
cpowSuffix = ` (${deltaAlerts.alerts.totalCPOWTime} alerts)`;
|
||||
jankSuffix = ` (${deltaAlerts.alerts.longestDuration || 0} alerts)`;
|
||||
cpowSuffix = ` (${deltaAlerts.alerts.totalCPOWTime || 0} alerts)`;
|
||||
}
|
||||
}
|
||||
|
||||
let eltImpact = cachedElements.eltImpact;
|
||||
if (currentMode == MODE_RECENT) {
|
||||
cachedElements.eltRoot.setAttribute("impact", delta.jank.longestDuration + 1);
|
||||
cachedElements.eltRoot.setAttribute("impact", delta.diff.jank.longestDuration + 1);
|
||||
if (Delta.compare(delta, Delta.MAX_DELTA_FOR_GOOD_RECENT_PERFORMANCE) <= 0) {
|
||||
eltImpact.textContent = ` currently performs well.`;
|
||||
} else if (Delta.compare(delta, Delta.MAX_DELTA_FOR_AVERAGE_RECENT_PERFORMANCE)) {
|
||||
|
@ -466,45 +570,54 @@ var View = {
|
|||
} else {
|
||||
eltImpact.textContent = ` is currently considerably slowing down ${BRAND_NAME}.`;
|
||||
}
|
||||
cachedElements.eltFPS.textContent = `Impact on framerate: ${delta.jank.longestDuration + 1}/${delta.jank.durations.length}${jankSuffix}.`;
|
||||
cachedElements.eltCPU.textContent = `CPU usage: ${Math.min(100, Math.ceil(delta.jank.totalUserTime/deltaT))}%.`;
|
||||
cachedElements.eltSystem.textContent = `System usage: ${Math.min(100, Math.ceil(delta.jank.totalSystemTime/deltaT))}%.`;
|
||||
cachedElements.eltCPOW.textContent = `Blocking process calls: ${Math.ceil(delta.cpow.totalCPOWTime/deltaT)}%${cpowSuffix}.`;
|
||||
|
||||
cachedElements.eltFPS.textContent = `Impact on framerate: ${delta.diff.jank.longestDuration + 1}/${delta.diff.jank.durations.length}${jankSuffix}.`;
|
||||
cachedElements.eltCPU.textContent = `CPU usage: ${Math.ceil(delta.diff.jank.totalCPUTime/delta.diff.deltaT)}%.`;
|
||||
cachedElements.eltSystem.textContent = `System usage: ${Math.ceil(delta.diff.jank.totalSystemTime/delta.diff.deltaT)}%.`;
|
||||
cachedElements.eltCPOW.textContent = `Blocking process calls: ${Math.ceil(delta.diff.cpow.totalCPOWTime/delta.diff.deltaT)}%${cpowSuffix}.`;
|
||||
} else {
|
||||
if (delta.alerts.length == 0) {
|
||||
eltImpact.textContent = " has performed well so far.";
|
||||
cachedElements.eltFPS.textContent = `Impact on framerate: no impact.`;
|
||||
cachedElements.eltRoot.setAttribute("impact", 0);
|
||||
} else {
|
||||
let impact = 0;
|
||||
let sum = /* medium impact */ delta.alerts[0] + /* high impact */ delta.alerts[1];
|
||||
let frequency = sum * 1000 / delta.age;
|
||||
let frequency = sum * 1000 / delta.diff.deltaT;
|
||||
|
||||
let describeFrequency;
|
||||
if (frequency <= MAX_FREQUENCY_FOR_NO_IMPACT) {
|
||||
describeFrequency = `has no impact on the performance of ${BRAND_NAME}.`
|
||||
cachedElements.eltRoot.classList.add("impact0");
|
||||
} else {
|
||||
let describeImpact;
|
||||
if (frequency <= MAX_FREQUENCY_FOR_RARE) {
|
||||
describeFrequency = `rarely slows down ${BRAND_NAME}.`;
|
||||
impact += 1;
|
||||
} else if (frequency <= MAX_FREQUENCY_FOR_FREQUENT) {
|
||||
describeFrequency = `has slown down ${BRAND_NAME} frequently.`;
|
||||
impact += 2.5;
|
||||
} else {
|
||||
describeFrequency = `seems to have slown down ${BRAND_NAME} very often.`;
|
||||
impact += 5;
|
||||
}
|
||||
// At this stage, `sum != 0`
|
||||
if (delta.alerts[1] / sum > MIN_PROPORTION_FOR_MAJOR_IMPACT) {
|
||||
describeImpact = "When this happens, the slowdown is generally important."
|
||||
impact *= 2;
|
||||
} else {
|
||||
describeImpact = "When this happens, the slowdown is generally noticeable."
|
||||
}
|
||||
|
||||
eltImpact.textContent = ` ${describeFrequency} ${describeImpact}`;
|
||||
cachedElements.eltFPS.textContent = `Impact on framerate: ${delta.alerts[1]} high-impacts, ${delta.alerts[0]} medium-impact${jankSuffix}.`;
|
||||
cachedElements.eltFPS.textContent = `Impact on framerate: ${delta.alerts[1] || 0} high-impacts, ${delta.alerts[0] || 0} medium-impact${jankSuffix}.`;
|
||||
}
|
||||
cachedElements.eltCPU.textContent = `CPU usage: ${Math.min(100, Math.ceil(delta.jank.totalUserTime/delta.age))}% (total ${delta.jank.totalUserTime}ms).`;
|
||||
cachedElements.eltSystem.textContent = `System usage: ${Math.min(100, Math.ceil(delta.jank.totalSystemTime/delta.age))}% (total ${delta.jank.totalSystemTime}ms).`;
|
||||
cachedElements.eltCPOW.textContent = `Blocking process calls: ${Math.ceil(delta.cpow.totalCPOWTime/delta.age)}% (total ${delta.cpow.totalCPOWTime}ms)${cpowSuffix}.`;
|
||||
cachedElements.eltRoot.setAttribute("impact", Math.round(impact));
|
||||
}
|
||||
|
||||
let result = delta.diff.jank.totalCPUTime/delta.diff.deltaT;
|
||||
cachedElements.eltCPU.textContent = `CPU usage: ${Math.ceil(delta.diff.jank.totalCPUTime/delta.diff.deltaT)}% (total ${delta.diff.jank.totalUserTime}ms).`;
|
||||
cachedElements.eltSystem.textContent = `System usage: ${Math.ceil(delta.diff.jank.totalSystemTime/delta.diff.deltaT)}% (total ${delta.diff.jank.totalSystemTime}ms).`;
|
||||
cachedElements.eltCPOW.textContent = `Blocking process calls: ${Math.ceil(delta.diff.cpow.totalCPOWTime/delta.diff.deltaT)}% (total ${delta.diff.cpow.totalCPOWTime}ms)${cpowSuffix}.`;
|
||||
}
|
||||
}
|
||||
this._insertElements(toAdd, id);
|
||||
|
@ -566,13 +679,13 @@ var View = {
|
|||
},
|
||||
|
||||
_grabOrCreateElements: function(delta, nature) {
|
||||
let cachedElements = this.DOMCache.get(delta.groupId);
|
||||
let cachedElements = this.DOMCache.get(delta.key);
|
||||
if (cachedElements) {
|
||||
if (cachedElements.eltRoot.parentElement) {
|
||||
cachedElements.eltRoot.parentElement.removeChild(cachedElements.eltRoot);
|
||||
}
|
||||
} else {
|
||||
this.DOMCache.set(delta.groupId, cachedElements = {});
|
||||
this.DOMCache.set(delta.key, cachedElements = {});
|
||||
|
||||
let eltDelta = document.createElement("li");
|
||||
eltDelta.classList.add("delta");
|
||||
|
@ -629,7 +742,7 @@ var View = {
|
|||
eltRestart.addEventListener("click", () => {
|
||||
Services.startup.quit(Services.startup.eForceQuit | Services.startup.eRestart);
|
||||
});
|
||||
AddonManager.getAddonByID(delta.addonId, addon => {
|
||||
AddonManager.getAddonByID(delta.diff.addonId, addon => {
|
||||
eltDisable.addEventListener("click", () => {
|
||||
addon.userDisabled = true;
|
||||
if (addon.pendingOperations == addon.PENDING_NONE) {
|
||||
|
@ -658,9 +771,9 @@ var View = {
|
|||
let eltCloseTab = document.createElement("button");
|
||||
eltCloseTab.textContent = "Close tab";
|
||||
eltSpan.appendChild(eltCloseTab);
|
||||
let windowId = delta.windowId;
|
||||
let windowIds = delta.diff.windowIds;
|
||||
eltCloseTab.addEventListener("click", () => {
|
||||
let found = findTabFromWindow(windowId);
|
||||
let found = findTabFromWindow(windowIds);
|
||||
if (!found) {
|
||||
// Cannot find the tab. Maybe it is closed already?
|
||||
return;
|
||||
|
@ -673,7 +786,7 @@ var View = {
|
|||
eltReloadTab.textContent = "Reload tab";
|
||||
eltSpan.appendChild(eltReloadTab);
|
||||
eltReloadTab.addEventListener("click", () => {
|
||||
let found = findTabFromWindow(windowId);
|
||||
let found = findTabFromWindow(windowIds);
|
||||
if (!found) {
|
||||
// Cannot find the tab. Maybe it is closed already?
|
||||
return;
|
||||
|
@ -709,7 +822,7 @@ var View = {
|
|||
},
|
||||
};
|
||||
|
||||
var Control = {
|
||||
let Control = {
|
||||
init: function() {
|
||||
this._initAutorefresh();
|
||||
this._initDisplayMode();
|
||||
|
@ -720,18 +833,21 @@ var Control = {
|
|||
// Update the state only if we are not on pause.
|
||||
yield State.update();
|
||||
}
|
||||
yield wait(0);
|
||||
let state = yield (mode == MODE_GLOBAL?
|
||||
State.promiseDeltaSinceStartOfTime():
|
||||
State.promiseDeltaSinceStartOfBuffer());
|
||||
|
||||
for (let category of ["webpages", "addons"]) {
|
||||
yield Promise.resolve();
|
||||
yield View.updateCategory(state[category], category, category, state.duration, mode);
|
||||
yield wait(0);
|
||||
yield View.updateCategory(state[category], category, category, mode);
|
||||
}
|
||||
yield Promise.resolve();
|
||||
yield wait(0);
|
||||
|
||||
// Make sure that we do not keep obsolete stuff around.
|
||||
View.DOMCache.trimTo(state.groups);
|
||||
View.DOMCache.trimTo(state.deltas);
|
||||
|
||||
yield wait(0);
|
||||
|
||||
// Inform watchers
|
||||
Services.obs.notifyObservers(null, UPDATE_COMPLETE_TOPIC, mode);
|
||||
|
@ -792,7 +908,7 @@ var Control = {
|
|||
_displayMode: MODE_GLOBAL,
|
||||
};
|
||||
|
||||
var go = Task.async(function*() {
|
||||
let go = Task.async(function*() {
|
||||
Control.init();
|
||||
|
||||
// Setup a hook to allow tests to configure and control this page
|
||||
|
|
Загрузка…
Ссылка в новой задаче