Backed out 2 changesets (bug 1943998) for causing wd failures @ phases.py CLOSED...
[gecko.git] / devtools / client / performance-new / shared / browser.js
blob79157c64e29d56ab202fd0c0622ca5f1ebb96bc5
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 // @ts-check
5 "use strict";
7 /**
8 * @typedef {import("../@types/perf").Action} Action
9 * @typedef {import("../@types/perf").Library} Library
10 * @typedef {import("../@types/perf").PerfFront} PerfFront
11 * @typedef {import("../@types/perf").SymbolTableAsTuple} SymbolTableAsTuple
12 * @typedef {import("../@types/perf").RecordingState} RecordingState
13 * @typedef {import("../@types/perf").SymbolicationService} SymbolicationService
14 * @typedef {import("../@types/perf").PreferenceFront} PreferenceFront
15 * @typedef {import("../@types/perf").PerformancePref} PerformancePref
16 * @typedef {import("../@types/perf").RecordingSettings} RecordingSettings
17 * @typedef {import("../@types/perf").RestartBrowserWithEnvironmentVariable} RestartBrowserWithEnvironmentVariable
18 * @typedef {import("../@types/perf").GetActiveBrowserID} GetActiveBrowserID
19 * @typedef {import("../@types/perf").MinimallyTypedGeckoProfile} MinimallyTypedGeckoProfile
20 * @typedef {import("../@types/perf").ProfilerViewMode} ProfilerViewMode
21 * @typedef {import("../@types/perf").ProfilerPanel} ProfilerPanel
24 const {
25 gDevTools,
26 } = require("resource://devtools/client/framework/devtools.js");
28 /** @type {PerformancePref["UIBaseUrl"]} */
29 const UI_BASE_URL_PREF = "devtools.performance.recording.ui-base-url";
30 /** @type {PerformancePref["UIBaseUrlPathPref"]} */
31 const UI_BASE_URL_PATH_PREF = "devtools.performance.recording.ui-base-url-path";
33 /** @type {PerformancePref["UIEnableActiveTabView"]} */
34 const UI_ENABLE_ACTIVE_TAB_PREF =
35 "devtools.performance.recording.active-tab-view.enabled";
37 const UI_BASE_URL_DEFAULT = "https://profiler.firefox.com";
38 const UI_BASE_URL_PATH_DEFAULT = "/from-browser";
40 /**
41 * This file contains all of the privileged browser-specific functionality. This helps
42 * keep a clear separation between the privileged and non-privileged client code. It
43 * is also helpful in being able to mock out browser behavior for tests, without
44 * worrying about polluting the browser environment.
47 /**
48 * Once a profile is received from the actor, it needs to be opened up in
49 * profiler.firefox.com to be analyzed. This function opens up profiler.firefox.com
50 * into a new browser tab.
52 * @typedef {Object} OpenProfilerOptions
53 * @property {ProfilerViewMode | undefined} [profilerViewMode] - View mode for the Firefox Profiler
54 * front-end timeline. While opening the url, we should append a query string
55 * if a view other than "full" needs to be displayed.
56 * @property {ProfilerPanel} [defaultPanel] Allows to change the default opened panel.
58 * @param {OpenProfilerOptions} options
59 * @returns {Promise<MockedExports.Browser>} The browser for the opened tab.
61 async function openProfilerTab({ profilerViewMode, defaultPanel }) {
62 // Allow the user to point to something other than profiler.firefox.com.
63 const baseUrl = Services.prefs.getStringPref(
64 UI_BASE_URL_PREF,
65 UI_BASE_URL_DEFAULT
67 // Allow tests to override the path.
68 const baseUrlPath = Services.prefs.getStringPref(
69 UI_BASE_URL_PATH_PREF,
70 UI_BASE_URL_PATH_DEFAULT
72 const additionalPath = defaultPanel ? `/${defaultPanel}/` : "";
73 // This controls whether we enable the active tab view when capturing in web
74 // developer preset.
75 const enableActiveTab = Services.prefs.getBoolPref(
76 UI_ENABLE_ACTIVE_TAB_PREF,
77 false
80 // We automatically open up the "full" mode if no query string is present.
81 // `undefined` also means nothing is specified, and it should open the "full"
82 // timeline view in that case.
83 let viewModeQueryString = "";
84 if (profilerViewMode === "active-tab") {
85 // We're not enabling the active-tab view in all environments until we
86 // iron out all its issues.
87 if (enableActiveTab) {
88 viewModeQueryString = "?view=active-tab&implementation=js";
89 } else {
90 viewModeQueryString = "?implementation=js";
92 } else if (profilerViewMode !== undefined && profilerViewMode !== "full") {
93 viewModeQueryString = `?view=${profilerViewMode}`;
96 const urlToLoad = `${baseUrl}${baseUrlPath}${additionalPath}${viewModeQueryString}`;
98 // Find the most recently used window, as the DevTools client could be in a variety
99 // of hosts.
100 // Note that when running from the browser toolbox, there won't be the browser window,
101 // but only the browser toolbox document.
102 const win =
103 Services.wm.getMostRecentWindow("navigator:browser") ||
104 Services.wm.getMostRecentWindow("devtools:toolbox");
105 if (!win) {
106 throw new Error("No browser window");
108 win.focus();
110 // The profiler frontend currently doesn't support being loaded in a private
111 // window, because it does some storage writes in IndexedDB. That's why we
112 // force the opening of the tab in a non-private window. This might open a new
113 // non-private window if the only currently opened window is a private window.
114 const contentBrowser = await new Promise(resolveOnContentBrowserCreated =>
115 win.openWebLinkIn(urlToLoad, "tab", {
116 forceNonPrivate: true,
117 resolveOnContentBrowserCreated,
118 userContextId: win.gBrowser?.contentPrincipal.userContextId,
119 relatedToCurrent: true,
122 return contentBrowser;
126 * Flatten all the sharedLibraries of the different processes in the profile
127 * into one list of libraries.
128 * @param {MinimallyTypedGeckoProfile} profile - The profile JSON object
129 * @returns {Library[]}
131 function sharedLibrariesFromProfile(profile) {
133 * @param {MinimallyTypedGeckoProfile} processProfile
134 * @returns {Library[]}
136 function getLibsRecursive(processProfile) {
137 return processProfile.libs.concat(
138 ...processProfile.processes.map(getLibsRecursive)
142 return getLibsRecursive(profile);
146 * Restarts the browser with a given environment variable set to a value.
148 * @type {RestartBrowserWithEnvironmentVariable}
150 function restartBrowserWithEnvironmentVariable(envName, value) {
151 Services.env.set(envName, value);
153 Services.startup.quit(
154 Services.startup.eForceQuit | Services.startup.eRestart
159 * @param {Window} window
160 * @param {string[]} objdirs
161 * @param {(objdirs: string[]) => unknown} changeObjdirs
163 function openFilePickerForObjdir(window, objdirs, changeObjdirs) {
164 const FilePicker = Cc["@mozilla.org/filepicker;1"].createInstance(
165 Ci.nsIFilePicker
167 FilePicker.init(
168 window.browsingContext,
169 "Pick build directory",
170 FilePicker.modeGetFolder
172 FilePicker.open(rv => {
173 if (rv == FilePicker.returnOK) {
174 const path = FilePicker.file.path;
175 if (path && !objdirs.includes(path)) {
176 const newObjdirs = [...objdirs, path];
177 changeObjdirs(newObjdirs);
184 * Try to open the given script with line and column in the tab.
186 * If the profiled tab is not alive anymore, returns without doing anything.
188 * @param {number} tabId
189 * @param {string} scriptUrl
190 * @param {number} line
191 * @param {number} columnOneBased
193 async function openScriptInDebugger(tabId, scriptUrl, line, columnOneBased) {
194 const win = Services.wm.getMostRecentWindow("navigator:browser");
196 // Iterate through all tabs in the current window and find the tab that we want.
197 const foundTab = win.gBrowser.tabs.find(
198 tab => tab.linkedBrowser.browserId === tabId
201 if (!foundTab) {
202 console.log(`No tab found with the tab id: ${tabId}`);
203 return;
206 // If a matching tab was found, switch to it.
207 win.gBrowser.selectedTab = foundTab;
209 // And open the devtools debugger with script.
210 const toolbox = await gDevTools.showToolboxForTab(foundTab, {
211 toolId: "jsdebugger",
214 toolbox.win.focus();
216 // In case profiler backend can't retrieve the column number, it can return zero.
217 const columnZeroBased = columnOneBased > 0 ? columnOneBased - 1 : 0;
218 await toolbox.viewSourceInDebugger(
219 scriptUrl,
220 line,
221 columnZeroBased,
222 /* sourceId = */ null,
223 "ProfilerOpenScript"
227 module.exports = {
228 openProfilerTab,
229 sharedLibrariesFromProfile,
230 restartBrowserWithEnvironmentVariable,
231 openFilePickerForObjdir,
232 openScriptInDebugger,