"use strict"; if (typeof ChromeUtils !== "undefined") { // Use a var here instead of let outside to avoid creating a locally scoped // variable that hides the global, which we modify for testing. // eslint-disable-next-line no-var, vars-on-top var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); } let usablePerfObj; /* istanbul ignore else */ // eslint-disable-next-line block-scoped-var if (typeof Services !== "undefined") { // Borrow the high-resolution timer from the hidden window.... // eslint-disable-next-line block-scoped-var usablePerfObj = Services.appShell.hiddenDOMWindow.performance; } else if (typeof performance !== "undefined") { // we must be running in content space // eslint-disable-next-line no-undef usablePerfObj = performance; } else { // This is a dummy object so this file doesn't crash in the node prerendering // task. usablePerfObj = { now() {}, mark() {}, }; } function _PerfService(options) { // For testing, so that we can use a fake Window.performance object with // known state. if (options && options.performanceObj) { this._perf = options.performanceObj; } else { this._perf = usablePerfObj; } } _PerfService.prototype = { /** * Calls the underlying mark() method on the appropriate Window.performance * object to add a mark with the given name to the appropriate performance * timeline. * * @param {String} name the name to give the current mark * @return {void} */ mark: function mark(str) { this._perf.mark(str); }, /** * Calls the underlying getEntriesByName on the appropriate Window.performance * object. * * @param {String} name * @param {String} type eg "mark" * @return {Array} Performance* objects */ getEntriesByName: function getEntriesByName(name, type) { return this._perf.getEntriesByName(name, type); }, /** * The timeOrigin property from the appropriate performance object. * Used to ensure that timestamps from the add-on code and the content code * are comparable. * * @note If this is called from a context without a window * (eg a JSM in chrome), it will return the timeOrigin of the XUL hidden * window, which appears to be the first created window (and thus * timeOrigin) in the browser. Note also, however, there is also a private * hidden window, presumably for private browsing, which appears to be * created dynamically later. Exactly how/when that shows up needs to be * investigated. * * @return {Number} A double of milliseconds with a precision of 0.5us. */ get timeOrigin() { return this._perf.timeOrigin; }, /** * Returns the "absolute" version of performance.now(), i.e. one that * should ([bug 1401406](https://bugzilla.mozilla.org/show_bug.cgi?id=1401406) * be comparable across both chrome and content. * * @return {Number} */ absNow: function absNow() { return this.timeOrigin + this._perf.now(); }, /** * This returns the absolute startTime from the most recent performance.mark() * with the given name. * * @param {String} name the name to lookup the start time for * * @return {Number} the returned start time, as a DOMHighResTimeStamp * * @throws {Error} "No Marks with the name ..." if none are available * * @note Always surround calls to this by try/catch. Otherwise your code * may fail when the `privacy.resistFingerprinting` pref is true. When * this pref is set, all attempts to get marks will likely fail, which will * cause this method to throw. * * See [bug 1369303](https://bugzilla.mozilla.org/show_bug.cgi?id=1369303) * for more info. */ getMostRecentAbsMarkStartByName(name) { let entries = this.getEntriesByName(name, "mark"); if (!entries.length) { throw new Error(`No marks with the name ${name}`); } let mostRecentEntry = entries[entries.length - 1]; return this._perf.timeOrigin + mostRecentEntry.startTime; }, }; this.perfService = new _PerfService(); const EXPORTED_SYMBOLS = ["_PerfService", "perfService"];