actions.js (6114B)
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"; 6 7 const selectors = require("resource://devtools/client/performance-new/store/selectors.js"); 8 9 /** 10 * @typedef {import("../@types/perf").Action} Action 11 * @typedef {import("../@types/perf").Library} Library 12 * @typedef {import("../@types/perf").PerfFront} PerfFront 13 * @typedef {import("../@types/perf").SymbolTableAsTuple} SymbolTableAsTuple 14 * @typedef {import("../@types/perf").RecordingState} RecordingState 15 * @typedef {import("../@types/perf").InitializeStoreValues} InitializeStoreValues 16 * @typedef {import("../@types/perf").RecordingSettings} RecordingSettings 17 * @typedef {import("../@types/perf").Presets} Presets 18 * @typedef {import("../@types/perf").PanelWindow} PanelWindow 19 */ 20 21 /** 22 * @template T 23 * @typedef {import("../@types/perf").ThunkAction<T>} ThunkAction<T> 24 */ 25 26 /** 27 * This is the result of the initial questions about the state of the profiler. 28 * 29 * @param {boolean} isActive 30 * @return {Action} 31 */ 32 exports.reportProfilerReady = isActive => ({ 33 type: "REPORT_PROFILER_READY", 34 isActive, 35 }); 36 37 /** 38 * Dispatched when the profiler starting is observed. 39 * 40 * @return {Action} 41 */ 42 exports.reportProfilerStarted = () => ({ 43 type: "REPORT_PROFILER_STARTED", 44 }); 45 46 /** 47 * Dispatched when the profiler stopping is observed. 48 * 49 * @return {Action} 50 */ 51 exports.reportProfilerStopped = () => ({ 52 type: "REPORT_PROFILER_STOPPED", 53 }); 54 55 /** 56 * Updates the recording settings for the interval. 57 * 58 * @param {number} interval 59 * @return {Action} 60 */ 61 exports.changeInterval = interval => ({ 62 type: "CHANGE_INTERVAL", 63 interval, 64 }); 65 66 /** 67 * Updates the recording settings for the entries. 68 * 69 * @param {number} entries 70 * @return {Action} 71 */ 72 exports.changeEntries = entries => ({ 73 type: "CHANGE_ENTRIES", 74 entries, 75 }); 76 77 /** 78 * Updates the recording settings for the features. 79 * 80 * @param {string[]} features 81 * @return {ThunkAction<void>} 82 */ 83 exports.changeFeatures = features => { 84 return ({ dispatch, getState }) => { 85 let promptEnvRestart = null; 86 if (selectors.getPageContext(getState()) === "aboutprofiling") { 87 // TODO Bug 1615431 - The old popup supported restarting the browser, but 88 // this hasn't been updated yet for the about:profiling workflow, because 89 // jstracer is disabled for now. 90 if ( 91 !Services.env.get("JS_TRACE_LOGGING") && 92 features.includes("jstracer") 93 ) { 94 promptEnvRestart = "JS_TRACE_LOGGING"; 95 } 96 } 97 98 dispatch({ 99 type: "CHANGE_FEATURES", 100 features, 101 promptEnvRestart, 102 }); 103 }; 104 }; 105 106 /** 107 * Updates the recording settings for the threads. 108 * 109 * @param {string[]} threads 110 * @return {Action} 111 */ 112 exports.changeThreads = threads => ({ 113 type: "CHANGE_THREADS", 114 threads, 115 }); 116 117 /** 118 * Change the preset. 119 * 120 * @param {Presets} presets 121 * @param {string} presetName 122 * @return {Action} 123 */ 124 exports.changePreset = (presets, presetName) => ({ 125 type: "CHANGE_PRESET", 126 presetName, 127 // Also dispatch the preset so that the reducers can pre-fill the values 128 // from a preset. 129 preset: presets[presetName], 130 }); 131 132 /** 133 * Updates the recording settings for the objdirs. 134 * 135 * @param {string[]} objdirs 136 * @return {Action} 137 */ 138 exports.changeObjdirs = objdirs => ({ 139 type: "CHANGE_OBJDIRS", 140 objdirs, 141 }); 142 143 /** 144 * Receive the values to initialize the store. See the reducer for what values 145 * are expected. 146 * 147 * @param {InitializeStoreValues} values 148 * @return {Action} 149 */ 150 exports.initializeStore = values => { 151 return { 152 type: "INITIALIZE_STORE", 153 ...values, 154 }; 155 }; 156 157 /** 158 * Whenever the preferences are updated, this action is dispatched to update the 159 * redux store. 160 * 161 * @param {RecordingSettings} recordingSettingsFromPreferences 162 * @return {Action} 163 */ 164 exports.updateSettingsFromPreferences = recordingSettingsFromPreferences => { 165 return { 166 type: "UPDATE_SETTINGS_FROM_PREFERENCES", 167 recordingSettingsFromPreferences, 168 }; 169 }; 170 171 /** 172 * Start a new recording with the perfFront and update the internal recording state. 173 * 174 * @param {PerfFront} perfFront 175 * @return {ThunkAction<void>} 176 */ 177 exports.startRecording = perfFront => { 178 return ({ dispatch, getState }) => { 179 const recordingSettings = selectors.getRecordingSettings(getState()); 180 // In the case of the profiler popup, the startProfiler can be synchronous. 181 // In order to properly allow the React components to handle the state changes 182 // make sure and change the recording state first, then start the profiler. 183 dispatch({ type: "REQUESTING_TO_START_RECORDING" }); 184 perfFront.startProfiler(recordingSettings); 185 }; 186 }; 187 188 /** 189 * Stops the profiler, and opens the profile in a new window. 190 * 191 * @param {PerfFront} perfFront 192 * @return {ThunkAction<Promise<MockedExports.ProfileAndAdditionalInformation>>} 193 */ 194 exports.getProfileAndStopProfiler = perfFront => { 195 return async ({ dispatch }) => { 196 dispatch({ type: "REQUESTING_PROFILE" }); 197 const profileAndAdditionalInformation = 198 await perfFront.getProfileAndStopProfiler(); 199 dispatch({ type: "OBTAINED_PROFILE" }); 200 return profileAndAdditionalInformation; 201 }; 202 }; 203 204 /** 205 * Stops the profiler, but does not try to retrieve the profile. 206 * 207 * @param {PerfFront} perfFront 208 * @return {ThunkAction<void>} 209 */ 210 exports.stopProfilerAndDiscardProfile = perfFront => { 211 return async ({ dispatch }) => { 212 dispatch({ type: "REQUESTING_TO_STOP_RECORDING" }); 213 214 try { 215 await perfFront.stopProfilerAndDiscardProfile(); 216 } catch (error) { 217 /** @type {any} */ 218 const anyWindow = window; 219 /** @type {PanelWindow} - Coerce the window into the PanelWindow. */ 220 const { gIsPanelDestroyed } = anyWindow; 221 222 if (gIsPanelDestroyed) { 223 // This error is most likely "Connection closed, pending request" as the 224 // command can race with closing the panel. Do not report an error. It's 225 // most likely fine. 226 } else { 227 throw error; 228 } 229 } 230 }; 231 };