mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
233 lines
7.9 KiB
JavaScript
233 lines
7.9 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/. */
|
||
|
"use strict";
|
||
|
|
||
|
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||
|
|
||
|
Cu.import("resource://gre/modules/Task.jsm");
|
||
|
Cu.import("resource://gre/modules/devtools/Loader.jsm");
|
||
|
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
|
||
|
|
||
|
devtools.lazyRequireGetter(this, "Services");
|
||
|
devtools.lazyRequireGetter(this, "promise");
|
||
|
devtools.lazyRequireGetter(this, "EventEmitter",
|
||
|
"devtools/toolkit/event-emitter");
|
||
|
devtools.lazyRequireGetter(this, "DevToolsUtils",
|
||
|
"devtools/toolkit/DevToolsUtils");
|
||
|
devtools.lazyRequireGetter(this, "FramerateFront",
|
||
|
"devtools/server/actors/framerate", true);
|
||
|
|
||
|
devtools.lazyRequireGetter(this, "L10N",
|
||
|
"devtools/profiler/global", true);
|
||
|
devtools.lazyRequireGetter(this, "CATEGORIES",
|
||
|
"devtools/profiler/global", true);
|
||
|
devtools.lazyRequireGetter(this, "CATEGORY_MAPPINGS",
|
||
|
"devtools/profiler/global", true);
|
||
|
devtools.lazyRequireGetter(this, "ThreadNode",
|
||
|
"devtools/profiler/tree-model", true);
|
||
|
devtools.lazyRequireGetter(this, "CallView",
|
||
|
"devtools/profiler/tree-view", true);
|
||
|
|
||
|
devtools.lazyImporter(this, "FileUtils",
|
||
|
"resource://gre/modules/FileUtils.jsm");
|
||
|
devtools.lazyImporter(this, "NetUtil",
|
||
|
"resource://gre/modules/NetUtil.jsm");
|
||
|
devtools.lazyImporter(this, "LineGraphWidget",
|
||
|
"resource:///modules/devtools/Graphs.jsm");
|
||
|
devtools.lazyImporter(this, "BarGraphWidget",
|
||
|
"resource:///modules/devtools/Graphs.jsm");
|
||
|
devtools.lazyImporter(this, "CanvasGraphUtils",
|
||
|
"resource:///modules/devtools/Graphs.jsm");
|
||
|
devtools.lazyImporter(this, "SideMenuWidget",
|
||
|
"resource:///modules/devtools/SideMenuWidget.jsm");
|
||
|
|
||
|
const RECORDING_DATA_DISPLAY_DELAY = 10; // ms
|
||
|
const FRAMERATE_CALC_INTERVAL = 16; // ms
|
||
|
const FRAMERATE_GRAPH_HEIGHT = 60; // px
|
||
|
const CATEGORIES_GRAPH_HEIGHT = 60; // px
|
||
|
const CATEGORIES_GRAPH_MIN_BARS_WIDTH = 3; // px
|
||
|
const CALL_VIEW_FOCUS_EVENTS_DRAIN = 10; // ms
|
||
|
const GRAPH_SCROLL_EVENTS_DRAIN = 50; // ms
|
||
|
const GRAPH_ZOOM_MIN_TIMESPAN = 20; // ms
|
||
|
|
||
|
// This identifier string is used to tentatively ascertain whether or not
|
||
|
// a JSON loaded from disk is actually something generated by this tool.
|
||
|
// It isn't, of course, a definitive verification, but a Good Enoughâ„¢
|
||
|
// approximation before continuing the import. Don't localize this.
|
||
|
const PROFILE_SERIALIZER_IDENTIFIER = "Recorded Performance Data";
|
||
|
const PROFILE_SERIALIZER_VERSION = 1;
|
||
|
|
||
|
// The panel's window global is an EventEmitter firing the following events:
|
||
|
const EVENTS = {
|
||
|
// When a recording is started or stopped, via the `stopwatch` button, or
|
||
|
// when `console.profile` and `console.profileEnd` is invoked.
|
||
|
RECORDING_STARTED: "Profiler:RecordingStarted",
|
||
|
RECORDING_ENDED: "Profiler:RecordingEnded",
|
||
|
|
||
|
// When a recording is abruptly ended, either because the built-in profiler
|
||
|
// module is stopped by a third party, or because the recordings list is
|
||
|
// cleared while there's one in progress.
|
||
|
RECORDING_LOST: "Profiler:RecordingCancelled",
|
||
|
|
||
|
// When a recording is displayed in the ProfileView.
|
||
|
RECORDING_DISPLAYED: "Profiler:RecordingDisplayed",
|
||
|
|
||
|
// When a new tab is spawned in the ProfileView from a graphs selection.
|
||
|
TAB_SPAWNED_FROM_SELECTION: "Profiler:TabSpawnedFromSelection",
|
||
|
|
||
|
// When a new tab is spawned in the ProfileView from a node in the tree.
|
||
|
TAB_SPAWNED_FROM_FRAME_NODE: "Profiler:TabSpawnedFromFrameNode",
|
||
|
|
||
|
// When different panels in the ProfileView are shown.
|
||
|
EMPTY_NOTICE_SHOWN: "Profiler:EmptyNoticeShown",
|
||
|
RECORDING_NOTICE_SHOWN: "Profiler:RecordingNoticeShown",
|
||
|
LOADING_NOTICE_SHOWN: "Profiler:LoadingNoticeShown",
|
||
|
TABBED_BROWSER_SHOWN: "Profiler:TabbedBrowserShown",
|
||
|
|
||
|
// When a source is shown in the JavaScript Debugger at a specific location.
|
||
|
SOURCE_SHOWN_IN_JS_DEBUGGER: "Profiler:SourceShownInJsDebugger",
|
||
|
SOURCE_NOT_FOUND_IN_JS_DEBUGGER: "Profiler:SourceNotFoundInJsDebugger"
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* The current target and the profiler connection, set by this tool's host.
|
||
|
*/
|
||
|
let gToolbox, gTarget, gFront;
|
||
|
|
||
|
/**
|
||
|
* Initializes the profiler controller and views.
|
||
|
*/
|
||
|
let startupProfiler = Task.async(function*() {
|
||
|
yield promise.all([
|
||
|
PrefObserver.register(),
|
||
|
EventsHandler.initialize(),
|
||
|
RecordingsListView.initialize(),
|
||
|
ProfileView.initialize()
|
||
|
]);
|
||
|
|
||
|
// Profiles may have been created before this tool was opened, e.g. via
|
||
|
// `console.profile` and `console.profileEnd(). Populate the UI with them.
|
||
|
for (let recordingData of gFront.finishedConsoleRecordings) {
|
||
|
let profileLabel = recordingData.profilerData.profileLabel;
|
||
|
let recordingItem = RecordingsListView.addEmptyRecording(profileLabel);
|
||
|
RecordingsListView.customizeRecording(recordingItem, recordingData);
|
||
|
}
|
||
|
for (let { profileLabel } of gFront.pendingConsoleRecordings) {
|
||
|
RecordingsListView.handleRecordingStarted(profileLabel);
|
||
|
}
|
||
|
|
||
|
// Select the first recording, if available.
|
||
|
RecordingsListView.selectedIndex = 0;
|
||
|
});
|
||
|
|
||
|
/**
|
||
|
* Destroys the profiler controller and views.
|
||
|
*/
|
||
|
let shutdownProfiler = Task.async(function*() {
|
||
|
yield promise.all([
|
||
|
PrefObserver.unregister(),
|
||
|
EventsHandler.destroy(),
|
||
|
RecordingsListView.destroy(),
|
||
|
ProfileView.destroy()
|
||
|
]);
|
||
|
});
|
||
|
|
||
|
/**
|
||
|
* Observes pref changes on the devtools.profiler branch and triggers the
|
||
|
* required frontend modifications.
|
||
|
*/
|
||
|
let PrefObserver = {
|
||
|
register: function() {
|
||
|
this.branch = Services.prefs.getBranch("devtools.profiler.");
|
||
|
this.branch.addObserver("", this, false);
|
||
|
},
|
||
|
unregister: function() {
|
||
|
this.branch.removeObserver("", this);
|
||
|
},
|
||
|
observe: function(subject, topic, pref) {
|
||
|
Prefs.refresh();
|
||
|
|
||
|
if (pref == "ui.show-platform-data") {
|
||
|
RecordingsListView.forceSelect(RecordingsListView.selectedItem);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Functions handling target-related lifetime events.
|
||
|
*/
|
||
|
let EventsHandler = {
|
||
|
/**
|
||
|
* Listen for events emitted by the current tab target.
|
||
|
*/
|
||
|
initialize: function() {
|
||
|
this._onConsoleProfileStart = this._onConsoleProfileStart.bind(this);
|
||
|
this._onConsoleProfileEnd = this._onConsoleProfileEnd.bind(this);
|
||
|
|
||
|
gFront.on("profile", this._onConsoleProfileStart);
|
||
|
gFront.on("profileEnd", this._onConsoleProfileEnd);
|
||
|
gFront.on("profiler-unexpectedly-stopped", this._onProfilerDeactivated);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Remove events emitted by the current tab target.
|
||
|
*/
|
||
|
destroy: function() {
|
||
|
gFront.off("profile", this._onConsoleProfileStart);
|
||
|
gFront.off("profileEnd", this._onConsoleProfileEnd);
|
||
|
gFront.off("profiler-unexpectedly-stopped", this._onProfilerDeactivated);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Invoked whenever `console.profile` is called.
|
||
|
*
|
||
|
* @param string profileLabel
|
||
|
* The provided string argument if available, undefined otherwise.
|
||
|
*/
|
||
|
_onConsoleProfileStart: function(event, profileLabel) {
|
||
|
RecordingsListView.handleRecordingStarted(profileLabel);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Invoked whenever `console.profileEnd` is called.
|
||
|
*
|
||
|
* @param object recordingData
|
||
|
* The profiler and refresh driver ticks data received from the front.
|
||
|
*/
|
||
|
_onConsoleProfileEnd: function(event, recordingData) {
|
||
|
RecordingsListView.handleRecordingEnded(recordingData);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Invoked whenever the built-in profiler module is deactivated.
|
||
|
* @see ProfilerConnection.prototype._onProfilerUnexpectedlyStopped
|
||
|
*/
|
||
|
_onProfilerDeactivated: function() {
|
||
|
RecordingsListView.removeForPredicate(e => e.isRecording);
|
||
|
RecordingsListView.handleRecordingCancelled();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Shortcuts for accessing various profiler preferences.
|
||
|
*/
|
||
|
const Prefs = new ViewHelpers.Prefs("devtools.profiler", {
|
||
|
showPlatformData: ["Bool", "ui.show-platform-data"]
|
||
|
});
|
||
|
|
||
|
/**
|
||
|
* Convenient way of emitting events from the panel window.
|
||
|
*/
|
||
|
EventEmitter.decorate(this);
|
||
|
|
||
|
/**
|
||
|
* DOM query helpers.
|
||
|
*/
|
||
|
function $(selector, target = document) {
|
||
|
return target.querySelector(selector);
|
||
|
}
|
||
|
function $$(selector, target = document) {
|
||
|
return target.querySelectorAll(selector);
|
||
|
}
|