index.js (11203B)
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 5 /** 6 * Redux actions for breakpoints 7 * 8 * @module actions/breakpoints 9 */ 10 11 const { 12 PROMISE, 13 } = require("resource://devtools/client/shared/redux/middleware/promise.js"); 14 import { asyncStore } from "../../utils/prefs"; 15 import { createLocation } from "../../utils/location"; 16 import { 17 getBreakpointsList, 18 getXHRBreakpoints, 19 getSelectedSource, 20 getBreakpointAtLocation, 21 getBreakpointsForSource, 22 getBreakpointsAtLine, 23 } from "../../selectors/index"; 24 import { createXHRBreakpoint } from "../../utils/breakpoint/index"; 25 import { 26 addBreakpoint, 27 removeBreakpoint, 28 enableBreakpoint, 29 disableBreakpoint, 30 } from "./modify"; 31 import { getOriginalLocation } from "../../utils/source-maps"; 32 import { setSkipPausing } from "../pause/skipPausing"; 33 34 export * from "./breakpointPositions"; 35 export * from "./modify"; 36 export * from "./syncBreakpoint"; 37 38 export function addHiddenBreakpoint(location) { 39 return ({ dispatch }) => { 40 return dispatch(addBreakpoint(location, { hidden: true })); 41 }; 42 } 43 44 /** 45 * Disable all breakpoints in a source 46 * 47 * @memberof actions/breakpoints 48 * @static 49 */ 50 export function disableBreakpointsInSource(source) { 51 return async ({ dispatch, getState }) => { 52 const breakpoints = getBreakpointsForSource(getState(), source); 53 for (const breakpoint of breakpoints) { 54 if (!breakpoint.disabled) { 55 dispatch(disableBreakpoint(breakpoint)); 56 } 57 } 58 }; 59 } 60 61 /** 62 * Enable all breakpoints in a source 63 * 64 * @memberof actions/breakpoints 65 * @static 66 */ 67 export function enableBreakpointsInSource(source) { 68 return async ({ dispatch, getState }) => { 69 const breakpoints = getBreakpointsForSource(getState(), source); 70 for (const breakpoint of breakpoints) { 71 if (breakpoint.disabled) { 72 dispatch(enableBreakpoint(breakpoint)); 73 } 74 } 75 }; 76 } 77 78 /** 79 * Toggle All Breakpoints 80 * 81 * @memberof actions/breakpoints 82 * @static 83 */ 84 export function toggleAllBreakpoints(shouldDisableBreakpoints) { 85 return async ({ dispatch, getState }) => { 86 const breakpoints = getBreakpointsList(getState()); 87 88 for (const breakpoint of breakpoints) { 89 if (shouldDisableBreakpoints) { 90 dispatch(disableBreakpoint(breakpoint)); 91 } else { 92 dispatch(enableBreakpoint(breakpoint)); 93 } 94 } 95 }; 96 } 97 98 /** 99 * Toggle Breakpoints 100 * 101 * @memberof actions/breakpoints 102 * @static 103 */ 104 export function toggleBreakpoints(shouldDisableBreakpoints, breakpoints) { 105 return async ({ dispatch }) => { 106 const promises = breakpoints.map(breakpoint => 107 shouldDisableBreakpoints 108 ? dispatch(disableBreakpoint(breakpoint)) 109 : dispatch(enableBreakpoint(breakpoint)) 110 ); 111 112 await Promise.all(promises); 113 }; 114 } 115 116 export function toggleBreakpointsAtLine(shouldDisableBreakpoints, line) { 117 return async ({ dispatch, getState }) => { 118 const breakpoints = getBreakpointsAtLine(getState(), line); 119 return dispatch(toggleBreakpoints(shouldDisableBreakpoints, breakpoints)); 120 }; 121 } 122 123 /** 124 * Removes all breakpoints 125 * 126 * @memberof actions/breakpoints 127 * @static 128 */ 129 export function removeAllBreakpoints() { 130 return async ({ dispatch, getState }) => { 131 const breakpointList = getBreakpointsList(getState()); 132 await Promise.all(breakpointList.map(bp => dispatch(removeBreakpoint(bp)))); 133 dispatch({ type: "CLEAR_BREAKPOINTS" }); 134 }; 135 } 136 137 /** 138 * Removes breakpoints 139 * 140 * @memberof actions/breakpoints 141 * @static 142 */ 143 export function removeBreakpoints(breakpoints) { 144 return async ({ dispatch }) => { 145 return Promise.all(breakpoints.map(bp => dispatch(removeBreakpoint(bp)))); 146 }; 147 } 148 149 /** 150 * Removes all breakpoints in a source 151 * 152 * @memberof actions/breakpoints 153 * @static 154 */ 155 export function removeBreakpointsInSource(source) { 156 return async ({ dispatch, getState }) => { 157 const breakpoints = getBreakpointsForSource(getState(), source); 158 for (const breakpoint of breakpoints) { 159 dispatch(removeBreakpoint(breakpoint)); 160 } 161 }; 162 } 163 164 /** 165 * Update the original location information of breakpoints. 166 167 /* 168 * Update breakpoints for a source that just got pretty printed. 169 * This method maps the breakpoints currently set only against the 170 * non-pretty-printed (generated) source to the related pretty-printed 171 * (original) source by querying the SourceMap service. 172 * 173 * @param {string} source - the generated source 174 */ 175 export function updateBreakpointsForNewPrettyPrintedSource(source) { 176 return async thunkArgs => { 177 const { dispatch, getState } = thunkArgs; 178 if (source.isOriginal) { 179 console.error("Can't update breakpoints on original sources"); 180 return; 181 } 182 const breakpoints = getBreakpointsForSource(getState(), source); 183 // Remap the breakpoints with the original location information from 184 // the pretty-printed source. 185 const newBreakpoints = await Promise.all( 186 breakpoints.map(async breakpoint => { 187 const location = await getOriginalLocation( 188 breakpoint.generatedLocation, 189 thunkArgs 190 ); 191 return { ...breakpoint, location }; 192 }) 193 ); 194 195 // Normally old breakpoints will be clobbered if we re-add them, but when 196 // remapping we have changed the source maps and the old breakpoints will 197 // have different locations than the new ones. Manually remove the 198 // old breakpoints before adding the new ones. 199 for (const bp of breakpoints) { 200 dispatch(removeBreakpoint(bp)); 201 } 202 203 for (const bp of newBreakpoints) { 204 await dispatch(addBreakpoint(bp.location, bp.options, bp.disabled)); 205 } 206 }; 207 } 208 209 export function toggleBreakpointAtLine(line) { 210 return async ({ dispatch, getState }) => { 211 const state = getState(); 212 const selectedSource = getSelectedSource(state); 213 214 if (!selectedSource) { 215 return null; 216 } 217 218 const bp = getBreakpointAtLocation(state, { line, column: undefined }); 219 if (bp) { 220 return dispatch(removeBreakpoint(bp)); 221 } 222 223 // Only if we re-enable a breakpoint, ensure re-enabling breakpoints globally 224 await dispatch(setSkipPausing(false)); 225 226 return dispatch( 227 addBreakpoint( 228 createLocation({ 229 source: selectedSource, 230 line, 231 }) 232 ) 233 ); 234 }; 235 } 236 237 export function addBreakpointAtLine(line, shouldLog = false, disabled = false) { 238 return async ({ dispatch, getState }) => { 239 const state = getState(); 240 const source = getSelectedSource(state); 241 242 if (!source) { 243 return null; 244 } 245 const breakpointLocation = createLocation({ 246 source, 247 column: undefined, 248 line, 249 }); 250 251 const options = {}; 252 if (shouldLog) { 253 options.logValue = "displayName"; 254 } 255 256 // Ensure re-enabling breakpoints globally 257 await dispatch(setSkipPausing(false)); 258 259 return dispatch(addBreakpoint(breakpointLocation, options, disabled)); 260 }; 261 } 262 263 export function removeBreakpointsAtLine(source, line) { 264 return ({ dispatch, getState }) => { 265 const breakpointsAtLine = getBreakpointsForSource(getState(), source, line); 266 return dispatch(removeBreakpoints(breakpointsAtLine)); 267 }; 268 } 269 270 export function disableBreakpointsAtLine(source, line) { 271 return ({ dispatch, getState }) => { 272 const breakpointsAtLine = getBreakpointsForSource(getState(), source, line); 273 return dispatch(toggleBreakpoints(true, breakpointsAtLine)); 274 }; 275 } 276 277 export function enableBreakpointsAtLine(source, line) { 278 return ({ dispatch, getState }) => { 279 const breakpointsAtLine = getBreakpointsForSource(getState(), source, line); 280 return dispatch(toggleBreakpoints(false, breakpointsAtLine)); 281 }; 282 } 283 284 export function toggleDisabledBreakpoint(breakpoint) { 285 return ({ dispatch }) => { 286 if (!breakpoint.disabled) { 287 return dispatch(disableBreakpoint(breakpoint)); 288 } 289 return dispatch(enableBreakpoint(breakpoint)); 290 }; 291 } 292 293 export function enableXHRBreakpoint(index, bp) { 294 return ({ dispatch, getState, client }) => { 295 const xhrBreakpoints = getXHRBreakpoints(getState()); 296 const breakpoint = bp || xhrBreakpoints[index]; 297 const enabledBreakpoint = { 298 ...breakpoint, 299 disabled: false, 300 }; 301 302 return dispatch({ 303 type: "ENABLE_XHR_BREAKPOINT", 304 breakpoint: enabledBreakpoint, 305 index, 306 [PROMISE]: client.setXHRBreakpoint(breakpoint.path, breakpoint.method), 307 }); 308 }; 309 } 310 311 export function disableXHRBreakpoint(index, bp) { 312 return ({ dispatch, getState, client }) => { 313 const xhrBreakpoints = getXHRBreakpoints(getState()); 314 const breakpoint = bp || xhrBreakpoints[index]; 315 const disabledBreakpoint = { 316 ...breakpoint, 317 disabled: true, 318 }; 319 320 return dispatch({ 321 type: "DISABLE_XHR_BREAKPOINT", 322 breakpoint: disabledBreakpoint, 323 index, 324 [PROMISE]: client.removeXHRBreakpoint(breakpoint.path, breakpoint.method), 325 }); 326 }; 327 } 328 329 export function updateXHRBreakpoint(index, path, method) { 330 return ({ dispatch, getState, client }) => { 331 const xhrBreakpoints = getXHRBreakpoints(getState()); 332 const breakpoint = xhrBreakpoints[index]; 333 334 const updatedBreakpoint = { 335 ...breakpoint, 336 path, 337 method, 338 text: L10N.getFormatStr("xhrBreakpoints.item.label", path), 339 }; 340 341 return dispatch({ 342 type: "UPDATE_XHR_BREAKPOINT", 343 breakpoint: updatedBreakpoint, 344 index, 345 [PROMISE]: Promise.all([ 346 client.removeXHRBreakpoint(breakpoint.path, breakpoint.method), 347 client.setXHRBreakpoint(path, method), 348 ]), 349 }); 350 }; 351 } 352 export function togglePauseOnAny() { 353 return ({ dispatch, getState }) => { 354 const xhrBreakpoints = getXHRBreakpoints(getState()); 355 const index = xhrBreakpoints.findIndex(({ path }) => path.length === 0); 356 if (index < 0) { 357 return dispatch(setXHRBreakpoint("", "ANY")); 358 } 359 360 const bp = xhrBreakpoints[index]; 361 if (bp.disabled) { 362 return dispatch(enableXHRBreakpoint(index, bp)); 363 } 364 365 return dispatch(disableXHRBreakpoint(index, bp)); 366 }; 367 } 368 369 export function setXHRBreakpoint(path, method) { 370 return ({ dispatch, client }) => { 371 const breakpoint = createXHRBreakpoint(path, method); 372 373 return dispatch({ 374 type: "SET_XHR_BREAKPOINT", 375 breakpoint, 376 [PROMISE]: client.setXHRBreakpoint(path, method), 377 }); 378 }; 379 } 380 381 export function removeAllXHRBreakpoints() { 382 return async ({ dispatch, getState, client }) => { 383 const xhrBreakpoints = getXHRBreakpoints(getState()); 384 const promises = xhrBreakpoints.map(breakpoint => 385 client.removeXHRBreakpoint(breakpoint.path, breakpoint.method) 386 ); 387 await dispatch({ 388 type: "CLEAR_XHR_BREAKPOINTS", 389 [PROMISE]: Promise.all(promises), 390 }); 391 asyncStore.xhrBreakpoints = []; 392 }; 393 } 394 395 export function removeXHRBreakpoint(index) { 396 return ({ dispatch, getState, client }) => { 397 const xhrBreakpoints = getXHRBreakpoints(getState()); 398 const breakpoint = xhrBreakpoints[index]; 399 return dispatch({ 400 type: "REMOVE_XHR_BREAKPOINT", 401 breakpoint, 402 index, 403 [PROMISE]: client.removeXHRBreakpoint(breakpoint.path, breakpoint.method), 404 }); 405 }; 406 }