mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
270 lines
7.3 KiB
JavaScript
270 lines
7.3 KiB
JavaScript
/* 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/. */
|
|
|
|
const { Cu } = require("chrome");
|
|
Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm");
|
|
const STRINGS_URI = "chrome://browser/locale/devtools/memory.properties"
|
|
const L10N = exports.L10N = new ViewHelpers.L10N(STRINGS_URI);
|
|
const { assert } = require("devtools/shared/DevToolsUtils");
|
|
const { Preferences } = require("resource://gre/modules/Preferences.jsm");
|
|
const CUSTOM_BREAKDOWN_PREF = "devtools.memory.custom-breakdowns";
|
|
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
|
const { snapshotState: states, breakdowns } = require("./constants");
|
|
|
|
/**
|
|
* Takes a snapshot object and returns the
|
|
* localized form of its timestamp to be used as a title.
|
|
*
|
|
* @param {Snapshot} snapshot
|
|
* @return {String}
|
|
*/
|
|
exports.getSnapshotTitle = function (snapshot) {
|
|
if (!snapshot.creationTime) {
|
|
return L10N.getStr("snapshot-title.loading");
|
|
}
|
|
|
|
let date = new Date(snapshot.creationTime / 1000);
|
|
return date.toLocaleTimeString(void 0, {
|
|
year: "2-digit",
|
|
month: "2-digit",
|
|
day: "2-digit",
|
|
hour12: false
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Returns an array of objects with the unique key `name`
|
|
* and `displayName` for each breakdown.
|
|
*
|
|
* @return {Object{name, displayName}}
|
|
*/
|
|
exports.getBreakdownDisplayData = function () {
|
|
return exports.getBreakdownNames().map(name => {
|
|
// If it's a preset use the display name value
|
|
let preset = breakdowns[name];
|
|
let displayName = name;
|
|
if (preset && preset.displayName) {
|
|
displayName = preset.displayName;
|
|
}
|
|
return { name, displayName };
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Returns an array of the unique names for each breakdown in
|
|
* presets and custom pref.
|
|
*
|
|
* @return {Array<Breakdown>}
|
|
*/
|
|
exports.getBreakdownNames = function () {
|
|
let custom = exports.getCustomBreakdowns();
|
|
return Object.keys(Object.assign({}, breakdowns, custom));
|
|
};
|
|
|
|
/**
|
|
* Returns custom breakdowns defined in `devtools.memory.custom-breakdowns` pref.
|
|
*
|
|
* @return {Object}
|
|
*/
|
|
exports.getCustomBreakdowns = function () {
|
|
let customBreakdowns = Object.create(null);
|
|
try {
|
|
customBreakdowns = JSON.parse(Preferences.get(CUSTOM_BREAKDOWN_PREF)) || Object.create(null);
|
|
} catch (e) {
|
|
DevToolsUtils.reportException(
|
|
`String stored in "${CUSTOM_BREAKDOWN_PREF}" pref cannot be parsed by \`JSON.parse()\`.`);
|
|
}
|
|
return customBreakdowns;
|
|
}
|
|
|
|
/**
|
|
* Converts a breakdown preset name, like "allocationStack", and returns the
|
|
* spec for the breakdown. Also checks properties of keys in the `devtools.memory.custom-breakdowns`
|
|
* pref. If not found, returns an empty object.
|
|
*
|
|
* @param {String} name
|
|
* @return {Object}
|
|
*/
|
|
|
|
exports.breakdownNameToSpec = function (name) {
|
|
let customBreakdowns = exports.getCustomBreakdowns();
|
|
|
|
// If breakdown is already a breakdown, use it
|
|
if (typeof name === "object") {
|
|
return name;
|
|
}
|
|
// If it's in our custom breakdowns, use it
|
|
else if (name in customBreakdowns) {
|
|
return customBreakdowns[name];
|
|
}
|
|
// If breakdown name is in our presets, use that
|
|
else if (name in breakdowns) {
|
|
return breakdowns[name].breakdown;
|
|
}
|
|
return Object.create(null);
|
|
};
|
|
|
|
/**
|
|
* Returns a string representing a readable form of the snapshot's state.
|
|
* More brief than `getSnapshotStatusText`.
|
|
*
|
|
* @param {Snapshot} snapshot
|
|
* @return {String}
|
|
*/
|
|
exports.getSnapshotStatusText = function (snapshot) {
|
|
assert((snapshot || {}).state,
|
|
`Snapshot must have expected state, found ${(snapshot || {}).state}.`);
|
|
|
|
switch (snapshot.state) {
|
|
case states.ERROR:
|
|
return L10N.getStr("snapshot.state.error");
|
|
case states.SAVING:
|
|
return L10N.getStr("snapshot.state.saving");
|
|
case states.SAVED:
|
|
case states.READING:
|
|
return L10N.getStr("snapshot.state.reading");
|
|
case states.SAVING_CENSUS:
|
|
return L10N.getStr("snapshot.state.saving-census");
|
|
// Both READ and SAVED_CENSUS state do not have any message
|
|
// to show as other content will be displayed.
|
|
case states.READ:
|
|
case states.SAVED_CENSUS:
|
|
return "";
|
|
}
|
|
|
|
assert(false, `Snapshot in unexpected state: ${snapshot.state}`);
|
|
return "";
|
|
}
|
|
|
|
/**
|
|
* Returns a string representing a readable form of the snapshot's state;
|
|
* more verbose than `getSnapshotStatusText`.
|
|
*
|
|
* @param {Snapshot} snapshot
|
|
* @return {String}
|
|
*/
|
|
exports.getSnapshotStatusTextFull = function (snapshot) {
|
|
assert((snapshot || {}).state,
|
|
`Snapshot must have expected state, found ${(snapshot || {}).state}.`);
|
|
switch (snapshot.state) {
|
|
case states.ERROR:
|
|
return L10N.getStr("snapshot.state.error.full");
|
|
case states.SAVING:
|
|
return L10N.getStr("snapshot.state.saving.full");
|
|
case states.SAVED:
|
|
case states.READING:
|
|
return L10N.getStr("snapshot.state.reading.full");
|
|
case states.SAVING_CENSUS:
|
|
return L10N.getStr("snapshot.state.saving-census.full");
|
|
// Both READ and SAVED_CENSUS state do not have any full message
|
|
// to show as other content will be displayed.
|
|
case states.READ:
|
|
case states.SAVED_CENSUS:
|
|
return "";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Takes an array of snapshots and a snapshot and returns
|
|
* the snapshot instance in `snapshots` that matches
|
|
* the snapshot passed in.
|
|
*
|
|
* @param {Array<Snapshot>} snapshots
|
|
* @param {Snapshot}
|
|
* @return ?Snapshot
|
|
*/
|
|
exports.getSnapshot = function getSnapshot (snapshots, snapshot) {
|
|
let found = snapshots.find(s => s.id === snapshot.id);
|
|
if (!found) {
|
|
DevToolsUtils.reportException(`No matching snapshot found for ${snapshot.id}`);
|
|
}
|
|
return found || null;
|
|
};
|
|
|
|
/**
|
|
* Creates a new snapshot object.
|
|
*
|
|
* @return {Snapshot}
|
|
*/
|
|
let INC_ID = 0;
|
|
exports.createSnapshot = function createSnapshot () {
|
|
let id = ++INC_ID;
|
|
return {
|
|
id,
|
|
state: states.SAVING,
|
|
census: null,
|
|
path: null,
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Takes two objects and compares them deeply, returning
|
|
* a boolean indicating if they're equal or not. Used for breakdown
|
|
* comparison.
|
|
*
|
|
* @param {Any} obj1
|
|
* @param {Any} obj2
|
|
* @return {Boolean}
|
|
*/
|
|
exports.breakdownEquals = function (obj1, obj2) {
|
|
let type1 = typeof obj1;
|
|
let type2 = typeof obj2;
|
|
|
|
// Quick checks
|
|
if (type1 !== type2 || (Array.isArray(obj1) !== Array.isArray(obj2))) {
|
|
return false;
|
|
}
|
|
|
|
if (obj1 === obj2) {
|
|
return true;
|
|
}
|
|
|
|
if (Array.isArray(obj1)) {
|
|
if (obj1.length !== obj2.length) { return false; }
|
|
return obj1.every((_, i) => exports.breakdownEquals(obj[1], obj2[i]));
|
|
}
|
|
else if (type1 === "object") {
|
|
let k1 = Object.keys(obj1);
|
|
let k2 = Object.keys(obj2);
|
|
|
|
if (k1.length !== k2.length) {
|
|
return false;
|
|
}
|
|
|
|
return k1.every(k => exports.breakdownEquals(obj1[k], obj2[k]));
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* Takes a snapshot and returns the total bytes and
|
|
* total count that this snapshot represents.
|
|
*
|
|
* @param {Snapshot} snapshot
|
|
* @return {Object}
|
|
*/
|
|
exports.getSnapshotTotals = function (snapshot) {
|
|
let bytes, count;
|
|
|
|
let census = snapshot.census;
|
|
|
|
if (snapshot.inverted) {
|
|
while (census) {
|
|
bytes = census.totalBytes;
|
|
count = census.totalCount;
|
|
census = census.children && census.children[0];
|
|
}
|
|
} else {
|
|
bytes = census.totalBytes;
|
|
count = census.totalCount;
|
|
}
|
|
|
|
return {
|
|
bytes: bytes || 0,
|
|
count: count || 0,
|
|
};
|
|
};
|
|
|