mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 23:31:56 +00:00
Bug 1667061 - Add ProfilerViewMode for preset and append view
querystring while opening the front-end. r=gregtatum
Depends on D91266 Differential Revision: https://phabricator.services.mozilla.com/D91267
This commit is contained in:
parent
349951062c
commit
f4d36497aa
@ -117,6 +117,7 @@ export interface State {
|
||||
threads: string[];
|
||||
objdirs: string[];
|
||||
presetName: string;
|
||||
profilerViewMode: ProfilerViewMode | undefined;
|
||||
initializedValues: InitializedValues | null;
|
||||
promptEnvRestart: null | string;
|
||||
}
|
||||
@ -168,6 +169,7 @@ export type GetSymbolTableCallback = (
|
||||
|
||||
export type ReceiveProfile = (
|
||||
geckoProfile: MinimallyTypedGeckoProfile,
|
||||
profilerViewMode: ProfilerViewMode | undefined,
|
||||
getSymbolTableCallback: GetSymbolTableCallback
|
||||
) => void;
|
||||
|
||||
@ -426,6 +428,12 @@ export interface ScaleFunctions {
|
||||
fromFractionToSingleDigitValue: NumberScaler;
|
||||
}
|
||||
|
||||
/**
|
||||
* View mode for the Firefox Profiler front-end timeline.
|
||||
* `undefined` is defaulted to full automatically.
|
||||
*/
|
||||
export type ProfilerViewMode = "full" | "active-tab" | "origins";
|
||||
|
||||
export interface PresetDefinition {
|
||||
label: string;
|
||||
description: string;
|
||||
@ -434,6 +442,7 @@ export interface PresetDefinition {
|
||||
features: string[];
|
||||
threads: string[];
|
||||
duration: number;
|
||||
profilerViewMode?: ProfilerViewMode;
|
||||
}
|
||||
|
||||
export interface Presets {
|
||||
|
@ -18,6 +18,7 @@
|
||||
* @typedef {import("./@types/perf").GetEnvironmentVariable} GetEnvironmentVariable
|
||||
* @typedef {import("./@types/perf").GetActiveBrowsingContextID} GetActiveBrowsingContextID
|
||||
* @typedef {import("./@types/perf").MinimallyTypedGeckoProfile} MinimallyTypedGeckoProfile
|
||||
* * @typedef {import("./@types/perf").ProfilerViewMode} ProfilerViewMode
|
||||
*/
|
||||
|
||||
const ChromeUtils = require("ChromeUtils");
|
||||
@ -62,13 +63,16 @@ const UI_BASE_URL_PATH_DEFAULT = "/from-addon";
|
||||
* into a new browser tab, and injects the profile via a frame script.
|
||||
*
|
||||
* @param {MinimallyTypedGeckoProfile} profile - The Gecko profile.
|
||||
* @param {ProfilerViewMode | undefined} profilerViewMode - View mode for the Firefox Profiler
|
||||
* front-end timeline. While opening the url, we should append a query string
|
||||
* if a view other than "full" needs to be displayed.
|
||||
* @param {GetSymbolTableCallback} getSymbolTableCallback - A callback function with the signature
|
||||
* (debugName, breakpadId) => Promise<SymbolTableAsTuple>, which will be invoked
|
||||
* when profiler.firefox.com sends SYMBOL_TABLE_REQUEST_EVENT messages to us. This
|
||||
* function should obtain a symbol table for the requested binary and resolve the
|
||||
* returned promise with it.
|
||||
*/
|
||||
function receiveProfile(profile, getSymbolTableCallback) {
|
||||
function receiveProfile(profile, profilerViewMode, getSymbolTableCallback) {
|
||||
const Services = lazy.Services();
|
||||
// Find the most recently used window, as the DevTools client could be in a variety
|
||||
// of hosts.
|
||||
@ -90,11 +94,22 @@ function receiveProfile(profile, getSymbolTableCallback) {
|
||||
UI_BASE_URL_PATH_DEFAULT
|
||||
);
|
||||
|
||||
const tab = browser.addWebTab(`${baseUrl}${baseUrlPath}`, {
|
||||
triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({
|
||||
userContextId: browser.contentPrincipal.userContextId,
|
||||
}),
|
||||
});
|
||||
// We automatically open up the "full" mode if no query string is present.
|
||||
// `undefined` also means nothing is specified, and it should open the "full"
|
||||
// timeline view in that case.
|
||||
const viewModeQueryString =
|
||||
profilerViewMode !== undefined && profilerViewMode !== "full"
|
||||
? `?view=${profilerViewMode}`
|
||||
: "";
|
||||
|
||||
const tab = browser.addWebTab(
|
||||
`${baseUrl}${baseUrlPath}${viewModeQueryString}`,
|
||||
{
|
||||
triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({
|
||||
userContextId: browser.contentPrincipal.userContextId,
|
||||
}),
|
||||
}
|
||||
);
|
||||
browser.selectedTab = tab;
|
||||
const mm = tab.linkedBrowser.messageManager;
|
||||
mm.loadFrameScript(
|
||||
|
@ -33,6 +33,7 @@ const AppConstants = ChromeUtils.import(
|
||||
* @typedef {import("../@types/perf").MessageFromFrontend} MessageFromFrontend
|
||||
* @typedef {import("../@types/perf").PageContext} PageContext
|
||||
* @typedef {import("../@types/perf").Presets} Presets
|
||||
* @typedef {import("../@types/perf").ProfilerViewMode} ProfilerViewMode
|
||||
*/
|
||||
|
||||
/** @type {PerformancePref["Entries"]} */
|
||||
@ -93,6 +94,7 @@ const presets = {
|
||||
features: ["screenshots", "js"],
|
||||
threads: ["GeckoMain", "Compositor", "Renderer", "DOM Worker"],
|
||||
duration: 0,
|
||||
profilerViewMode: "active-tab",
|
||||
},
|
||||
"firefox-platform": {
|
||||
label: "Firefox Platform",
|
||||
@ -192,6 +194,29 @@ async function getSymbolsFromThisBrowser(pageContext, debugName, breakpadId) {
|
||||
return getSymbolTableMultiModal(lib, objdirs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the proper view mode for the Firefox Profiler front-end timeline by
|
||||
* looking at the proper preset that is selected.
|
||||
* Return value can be undefined when the preset is unknown or custom.
|
||||
* @param {PageContext} pageContext
|
||||
* @return {ProfilerViewMode | undefined}
|
||||
*/
|
||||
function getProfilerViewModeForCurrentPreset(pageContext) {
|
||||
const postfix = getPrefPostfix(pageContext);
|
||||
const presetName = Services.prefs.getCharPref(PRESET_PREF + postfix);
|
||||
|
||||
if (presetName === "custom") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const preset = presets[presetName];
|
||||
if (!preset) {
|
||||
console.error(`Unknown profiler preset was encountered: "${presetName}"`);
|
||||
return undefined;
|
||||
}
|
||||
return preset.profilerViewMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called directly by devtools/startup/DevToolsStartup.jsm when
|
||||
* using the shortcut keys to capture a profile.
|
||||
@ -221,8 +246,9 @@ async function captureProfile(pageContext) {
|
||||
}
|
||||
);
|
||||
|
||||
const profilerViewMode = getProfilerViewModeForCurrentPreset(pageContext);
|
||||
const receiveProfile = lazy.BrowserModule().receiveProfile;
|
||||
receiveProfile(profile, (debugName, breakpadId) => {
|
||||
receiveProfile(profile, profilerViewMode, (debugName, breakpadId) => {
|
||||
return getSymbolsFromThisBrowser(pageContext, debugName, breakpadId);
|
||||
});
|
||||
|
||||
|
@ -212,7 +212,8 @@ exports.getProfileAndStopProfiler = () => {
|
||||
|
||||
const getSymbolTable = selectors.getSymbolTableGetter(getState())(profile);
|
||||
const receiveProfile = selectors.getReceiveProfileFn(getState());
|
||||
receiveProfile(profile, getSymbolTable);
|
||||
const profilerViewMode = selectors.getProfilerViewMode(getState());
|
||||
receiveProfile(profile, profilerViewMode, getSymbolTable);
|
||||
dispatch(changeRecordingState("available-to-record"));
|
||||
};
|
||||
};
|
||||
|
@ -16,6 +16,7 @@
|
||||
* @typedef {import("../@types/perf").GetEnvironmentVariable} GetEnvironmentVariable
|
||||
* @typedef {import("../@types/perf").PageContext} PageContext
|
||||
* @typedef {import("../@types/perf").Presets} Presets
|
||||
* @typedef {import("../@types/perf").ProfilerViewMode} ProfilerViewMode
|
||||
*/
|
||||
/**
|
||||
* @template S
|
||||
@ -56,6 +57,9 @@ const getPresets = state => getInitializedValues(state).presets;
|
||||
/** @type {Selector<string>} */
|
||||
const getPresetName = state => state.presetName;
|
||||
|
||||
/** @type {Selector<ProfilerViewMode | undefined>} */
|
||||
const getProfilerViewMode = state => state.profilerViewMode;
|
||||
|
||||
/**
|
||||
* When remote profiling, there will be a back button to the settings.
|
||||
*
|
||||
@ -160,6 +164,7 @@ module.exports = {
|
||||
getObjdirs,
|
||||
getPresets,
|
||||
getPresetName,
|
||||
getProfilerViewMode,
|
||||
getOpenRemoteDevTools,
|
||||
getOpenAboutProfiling,
|
||||
getRecordingSettings,
|
||||
|
@ -29,6 +29,7 @@ skip-if = tsan # Frequently times out on TSan
|
||||
[browser_webchannel-enable-menu-button.js]
|
||||
[browser_popup-profiler-states.js]
|
||||
[browser_popup-record-capture.js]
|
||||
[browser_popup-record-capture-view.js]
|
||||
[browser_popup-record-discard.js]
|
||||
|
||||
# The popupshown and popuphidden events are not firing correctly on linux, as of
|
||||
|
@ -0,0 +1,67 @@
|
||||
/* 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 FRONTEND_BASE_URL =
|
||||
"http://example.com/browser/devtools/client/performance-new/test/browser/fake-frontend.html";
|
||||
|
||||
add_task(async function test() {
|
||||
info(
|
||||
"Test that the profiler pop-up correctly opens the captured profile on the " +
|
||||
"correct frontend view by adding proper view query string"
|
||||
);
|
||||
await setProfilerFrontendUrl(FRONTEND_BASE_URL);
|
||||
await makeSureProfilerPopupIsEnabled();
|
||||
|
||||
// First check for "firefox-platform" preset which will have no "view" query
|
||||
// string because this is where our traditional "full" view opens up.
|
||||
await openPopupAndAssertUrlForPreset({
|
||||
preset: "firefox-platform",
|
||||
expectedUrl: FRONTEND_BASE_URL,
|
||||
});
|
||||
|
||||
// Now, let's check for "web-developer" preset. This will open up the frontend
|
||||
// with "active-tab" view query string. Frontend will understand and open the active tab view for it.
|
||||
await openPopupAndAssertUrlForPreset({
|
||||
preset: "web-developer",
|
||||
expectedUrl: FRONTEND_BASE_URL + "?view=active-tab",
|
||||
});
|
||||
});
|
||||
|
||||
async function openPopupAndAssertUrlForPreset({ preset, expectedUrl }) {
|
||||
// First, switch to the preset we want to test.
|
||||
BackgroundJSM.changePreset(
|
||||
"aboutprofiling",
|
||||
preset,
|
||||
[] // We don't need any features for this test.
|
||||
);
|
||||
|
||||
// Let's capture a profile and assert newly created tab's url.
|
||||
await openPopupAndEnsureCloses(window, async () => {
|
||||
{
|
||||
const button = await getElementByLabel(document, "Start Recording");
|
||||
info("Click the button to start recording.");
|
||||
button.click();
|
||||
}
|
||||
|
||||
{
|
||||
const button = await getElementByLabel(document, "Capture");
|
||||
info("Click the button to capture the recording.");
|
||||
button.click();
|
||||
}
|
||||
|
||||
info(
|
||||
"If the profiler successfully captures a profile, it will create a new " +
|
||||
"tab with the proper view query string depending on the preset."
|
||||
);
|
||||
|
||||
await waitForTabUrl({
|
||||
initialTitle: "Waiting on the profile",
|
||||
successTitle: "Profile received",
|
||||
errorTitle: "Error",
|
||||
expectedUrl,
|
||||
});
|
||||
});
|
||||
}
|
@ -365,6 +365,52 @@ async function checkTabLoadedProfile({
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This function checks the url of a tab so we can assert the frontend's url
|
||||
* with our expected url. This function runs in a loop every
|
||||
* requestAnimationFrame, and checks for a initialTitle. Asserts as soon as it
|
||||
* finds that title. We don't have to look for success title or error title
|
||||
* since we only care about the url.
|
||||
* @param {{
|
||||
* initialTitle: string,
|
||||
* successTitle: string,
|
||||
* errorTitle: string,
|
||||
* expectedUrl: string
|
||||
* }}
|
||||
*/
|
||||
async function waitForTabUrl({
|
||||
initialTitle,
|
||||
successTitle,
|
||||
errorTitle,
|
||||
expectedUrl,
|
||||
}) {
|
||||
const logPeriodically = createPeriodicLogger();
|
||||
|
||||
info(`Waiting for the selected tab to have the url "${expectedUrl}".`);
|
||||
|
||||
return waitUntil(() => {
|
||||
switch (gBrowser.selectedTab.textContent) {
|
||||
case initialTitle:
|
||||
case successTitle:
|
||||
if (gBrowser.currentURI.spec === expectedUrl) {
|
||||
ok(true, `The selected tab has the url ${expectedUrl}`);
|
||||
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
return true;
|
||||
}
|
||||
throw new Error(
|
||||
`Found a different url on the fake frontend: ${gBrowser.currentURI.spec}`
|
||||
);
|
||||
case errorTitle:
|
||||
throw new Error(
|
||||
"The fake frontend indicated that there was an error injecting the profile."
|
||||
);
|
||||
default:
|
||||
logPeriodically(`> Waiting for the fake frontend tab to be loaded.`);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This function checks the document title of a tab as an easy way to pass
|
||||
* messages from a content page to the mochitest.
|
||||
|
Loading…
Reference in New Issue
Block a user