diff options
Diffstat (limited to 'devtools/client/debugger/src/actions')
32 files changed, 192 insertions, 158 deletions
diff --git a/devtools/client/debugger/src/actions/ast/setInScopeLines.js b/devtools/client/debugger/src/actions/ast/setInScopeLines.js index 72bd33b59f..5a76ab7f65 100644 --- a/devtools/client/debugger/src/actions/ast/setInScopeLines.js +++ b/devtools/client/debugger/src/actions/ast/setInScopeLines.js @@ -27,11 +27,7 @@ function getOutOfScopeLines(outOfScopeLocations) { return uniqueLines; } -async function getInScopeLines( - location, - sourceTextContent, - { dispatch, getState, parserWorker } -) { +async function getInScopeLines(location, sourceTextContent, { parserWorker }) { let locations = null; if (location.line && parserWorker.isLocationSupported(location)) { locations = await parserWorker.findOutOfScopeLocations(location); diff --git a/devtools/client/debugger/src/actions/breakpoints/index.js b/devtools/client/debugger/src/actions/breakpoints/index.js index 2125ec9ec7..94e9028a1b 100644 --- a/devtools/client/debugger/src/actions/breakpoints/index.js +++ b/devtools/client/debugger/src/actions/breakpoints/index.js @@ -44,7 +44,7 @@ export function addHiddenBreakpoint(location) { * @static */ export function disableBreakpointsInSource(source) { - return async ({ dispatch, getState, client }) => { + return async ({ dispatch, getState }) => { const breakpoints = getBreakpointsForSource(getState(), source); for (const breakpoint of breakpoints) { if (!breakpoint.disabled) { @@ -61,7 +61,7 @@ export function disableBreakpointsInSource(source) { * @static */ export function enableBreakpointsInSource(source) { - return async ({ dispatch, getState, client }) => { + return async ({ dispatch, getState }) => { const breakpoints = getBreakpointsForSource(getState(), source); for (const breakpoint of breakpoints) { if (breakpoint.disabled) { @@ -78,7 +78,7 @@ export function enableBreakpointsInSource(source) { * @static */ export function toggleAllBreakpoints(shouldDisableBreakpoints) { - return async ({ dispatch, getState, client }) => { + return async ({ dispatch, getState }) => { const breakpoints = getBreakpointsList(getState()); for (const breakpoint of breakpoints) { @@ -149,7 +149,7 @@ export function removeBreakpoints(breakpoints) { * @static */ export function removeBreakpointsInSource(source) { - return async ({ dispatch, getState, client }) => { + return async ({ dispatch, getState }) => { const breakpoints = getBreakpointsForSource(getState(), source); for (const breakpoint of breakpoints) { dispatch(removeBreakpoint(breakpoint)); @@ -271,7 +271,7 @@ export function enableBreakpointsAtLine(source, line) { } export function toggleDisabledBreakpoint(breakpoint) { - return ({ dispatch, getState }) => { + return ({ dispatch }) => { if (!breakpoint.disabled) { return dispatch(disableBreakpoint(breakpoint)); } @@ -356,7 +356,7 @@ export function togglePauseOnAny() { } export function setXHRBreakpoint(path, method) { - return ({ dispatch, getState, client }) => { + return ({ dispatch, client }) => { const breakpoint = createXHRBreakpoint(path, method); return dispatch({ diff --git a/devtools/client/debugger/src/actions/breakpoints/syncBreakpoint.js b/devtools/client/debugger/src/actions/breakpoints/syncBreakpoint.js index b24912de58..aec56664d0 100644 --- a/devtools/client/debugger/src/actions/breakpoints/syncBreakpoint.js +++ b/devtools/client/debugger/src/actions/breakpoints/syncBreakpoint.js @@ -14,7 +14,7 @@ import { originalToGeneratedId } from "devtools/client/shared/source-map-loader/ import { getSource } from "../../selectors/index"; import { addBreakpoint, removeBreakpointAtGeneratedLocation } from "./modify"; -async function findBreakpointPosition({ getState, dispatch }, location) { +async function findBreakpointPosition({ dispatch }, location) { const positions = await dispatch(setBreakpointPositions(location)); const position = findPosition(positions, location); diff --git a/devtools/client/debugger/src/actions/breakpoints/tests/__snapshots__/breakpoints.spec.js.snap b/devtools/client/debugger/src/actions/breakpoints/tests/__snapshots__/breakpoints.spec.js.snap index e6abad25d5..69bc4a08b4 100644 --- a/devtools/client/debugger/src/actions/breakpoints/tests/__snapshots__/breakpoints.spec.js.snap +++ b/devtools/client/debugger/src/actions/breakpoints/tests/__snapshots__/breakpoints.spec.js.snap @@ -24,6 +24,8 @@ Array [ "isOriginal": false, "isPrettyPrinted": false, "isWasm": false, + "longName": "a", + "shortName": "a", "url": "http://localhost:8000/examples/a", }, "sourceActor": null, @@ -48,6 +50,8 @@ Array [ "isOriginal": false, "isPrettyPrinted": false, "isWasm": false, + "longName": "a", + "shortName": "a", "url": "http://localhost:8000/examples/a", }, "sourceActor": null, @@ -59,7 +63,6 @@ Array [ "thread": undefined, }, ], - "filename": "a", "source": Object { "displayURL": Object { "fileExtension": "", @@ -75,6 +78,8 @@ Array [ "isOriginal": false, "isPrettyPrinted": false, "isWasm": false, + "longName": "a", + "shortName": "a", "url": "http://localhost:8000/examples/a", }, }, @@ -107,6 +112,8 @@ Array [ "isOriginal": false, "isPrettyPrinted": false, "isWasm": false, + "longName": "a", + "shortName": "a", "url": "http://localhost:8000/examples/a", }, "sourceActor": null, @@ -131,6 +138,8 @@ Array [ "isOriginal": false, "isPrettyPrinted": false, "isWasm": false, + "longName": "a", + "shortName": "a", "url": "http://localhost:8000/examples/a", }, "sourceActor": null, @@ -142,7 +151,6 @@ Array [ "thread": undefined, }, ], - "filename": "a", "source": Object { "displayURL": Object { "fileExtension": "", @@ -158,6 +166,8 @@ Array [ "isOriginal": false, "isPrettyPrinted": false, "isWasm": false, + "longName": "a", + "shortName": "a", "url": "http://localhost:8000/examples/a", }, }, diff --git a/devtools/client/debugger/src/actions/context-menus/editor-breakpoint.js b/devtools/client/debugger/src/actions/context-menus/editor-breakpoint.js index 39ec2f1589..999912966b 100644 --- a/devtools/client/debugger/src/actions/context-menus/editor-breakpoint.js +++ b/devtools/client/debugger/src/actions/context-menus/editor-breakpoint.js @@ -85,7 +85,7 @@ export function showEditorCreateBreakpointContextMenu( location, lineText ) { - return async ({ dispatch, getState }) => { + return async ({ dispatch }) => { const items = createBreakpointItems(location, lineText, dispatch); showMenu(event, items); diff --git a/devtools/client/debugger/src/actions/context-menus/editor.js b/devtools/client/debugger/src/actions/context-menus/editor.js index 1125790a9b..1cad1e2131 100644 --- a/devtools/client/debugger/src/actions/context-menus/editor.js +++ b/devtools/client/debugger/src/actions/context-menus/editor.js @@ -8,7 +8,6 @@ import { copyToTheClipboard } from "../../utils/clipboard"; import { isPretty, getRawSourceURL, - getFilename, shouldBlackbox, findBlackBoxRange, } from "../../utils/source"; @@ -255,7 +254,7 @@ const blackBoxLinesMenuItem = ( editor, blackboxedRanges, isSourceOnIgnoreList, - clickedLine = null, + clickedLine, dispatch ) => { const { codeMirror } = editor; @@ -321,7 +320,7 @@ const downloadFileItem = (selectedSource, selectedContent) => ({ id: "node-menu-download-file", label: L10N.getStr("downloadFile.label"), accesskey: L10N.getStr("downloadFile.accesskey"), - click: () => downloadFile(selectedContent, getFilename(selectedSource)), + click: () => downloadFile(selectedContent, selectedSource.shortName), }); const inlinePreviewItem = dispatch => ({ diff --git a/devtools/client/debugger/src/actions/context-menus/frame.js b/devtools/client/debugger/src/actions/context-menus/frame.js index 1d287b1028..a517b55e07 100644 --- a/devtools/client/debugger/src/actions/context-menus/frame.js +++ b/devtools/client/debugger/src/actions/context-menus/frame.js @@ -26,7 +26,7 @@ function formatMenuElement(labelString, click, disabled = false) { }; } -function isValidRestartFrame(frame, callbacks) { +function isValidRestartFrame(frame) { // Any frame state than 'on-stack' is either dismissed by the server // or can potentially cause unexpected errors. // Global frame has frame.callee equal to null and can't be restarted. @@ -34,7 +34,7 @@ function isValidRestartFrame(frame, callbacks) { } function copyStackTrace() { - return async ({ dispatch, getState }) => { + return async ({ getState }) => { const frames = getCurrentThreadFrames(getState()); const shouldDisplayOriginalLocation = getShouldSelectOriginalLocation( getState() diff --git a/devtools/client/debugger/src/actions/expressions.js b/devtools/client/debugger/src/actions/expressions.js index 8ccb6013ba..383f191506 100644 --- a/devtools/client/debugger/src/actions/expressions.js +++ b/devtools/client/debugger/src/actions/expressions.js @@ -23,7 +23,7 @@ import { features } from "../utils/prefs"; * @param {string} input */ export function addExpression(input) { - return async ({ dispatch, getState, parserWorker }) => { + return async ({ dispatch, getState }) => { if (!input) { return null; } @@ -64,7 +64,7 @@ export function clearAutocomplete() { } export function updateExpression(input, expression) { - return async ({ getState, dispatch, parserWorker }) => { + return async ({ dispatch }) => { if (!input) { return; } diff --git a/devtools/client/debugger/src/actions/file-search.js b/devtools/client/debugger/src/actions/file-search.js index cc5794d7ab..fb8cfe8475 100644 --- a/devtools/client/debugger/src/actions/file-search.js +++ b/devtools/client/debugger/src/actions/file-search.js @@ -30,7 +30,7 @@ export function querySearchWorker(query, text, modifiers) { } export function searchContentsForHighlight(query, editor, line, ch) { - return async ({ getState, dispatch }) => { + return async ({ getState }) => { const modifiers = getSearchOptions(getState(), "file-search"); const sourceTextContent = getSelectedSourceTextContent(getState()); diff --git a/devtools/client/debugger/src/actions/navigation.js b/devtools/client/debugger/src/actions/navigation.js index 1b437837c2..839d67a31a 100644 --- a/devtools/client/debugger/src/actions/navigation.js +++ b/devtools/client/debugger/src/actions/navigation.js @@ -18,13 +18,7 @@ import { evaluateExpressionsForCurrentContext } from "../actions/expressions"; * @static */ export function willNavigate(event) { - return async function ({ - dispatch, - getState, - client, - sourceMapLoader, - parserWorker, - }) { + return async function ({ dispatch, getState, sourceMapLoader }) { sourceQueue.clear(); sourceMapLoader.clearSourceMaps(); clearWasmStates(); @@ -42,7 +36,7 @@ export function willNavigate(event) { * @static */ export function navigated() { - return async function ({ getState, dispatch, panel }) { + return async function ({ dispatch, panel }) { try { // Update the watched expressions once the page is fully loaded await dispatch(evaluateExpressionsForCurrentContext()); diff --git a/devtools/client/debugger/src/actions/pause/commands.js b/devtools/client/debugger/src/actions/pause/commands.js index 0bb371bf6e..1623add274 100644 --- a/devtools/client/debugger/src/actions/pause/commands.js +++ b/devtools/client/debugger/src/actions/pause/commands.js @@ -17,7 +17,7 @@ import { recordEvent } from "../../utils/telemetry"; import { validateFrame } from "../../utils/context"; export function selectThread(thread) { - return async ({ dispatch, getState, client }) => { + return async ({ dispatch, getState }) => { if (getCurrentThread(getState()) === thread) { return; } diff --git a/devtools/client/debugger/src/actions/pause/expandScopes.js b/devtools/client/debugger/src/actions/pause/expandScopes.js index af95f16fea..1f7b7caac1 100644 --- a/devtools/client/debugger/src/actions/pause/expandScopes.js +++ b/devtools/client/debugger/src/actions/pause/expandScopes.js @@ -5,7 +5,7 @@ import { getScopeItemPath } from "../../utils/pause/scopes"; export function setExpandedScope(selectedFrame, item, expanded) { - return function ({ dispatch, getState }) { + return function ({ dispatch }) { return dispatch({ type: "SET_EXPANDED_SCOPE", selectedFrame, diff --git a/devtools/client/debugger/src/actions/pause/pauseOnDebuggerStatement.js b/devtools/client/debugger/src/actions/pause/pauseOnDebuggerStatement.js index 7b2b1d70cb..7d82fd44a8 100644 --- a/devtools/client/debugger/src/actions/pause/pauseOnDebuggerStatement.js +++ b/devtools/client/debugger/src/actions/pause/pauseOnDebuggerStatement.js @@ -5,7 +5,7 @@ import { PROMISE } from "../utils/middleware/promise"; export function pauseOnDebuggerStatement(shouldPauseOnDebuggerStatement) { - return ({ dispatch, getState, client }) => { + return ({ dispatch, client }) => { return dispatch({ type: "PAUSE_ON_DEBUGGER_STATEMENT", shouldPauseOnDebuggerStatement, diff --git a/devtools/client/debugger/src/actions/pause/pauseOnExceptions.js b/devtools/client/debugger/src/actions/pause/pauseOnExceptions.js index e7c04ded61..fe2f3e882a 100644 --- a/devtools/client/debugger/src/actions/pause/pauseOnExceptions.js +++ b/devtools/client/debugger/src/actions/pause/pauseOnExceptions.js @@ -14,7 +14,7 @@ export function pauseOnExceptions( shouldPauseOnExceptions, shouldPauseOnCaughtExceptions ) { - return ({ dispatch, getState, client }) => { + return ({ dispatch, client }) => { recordEvent("pause_on_exceptions", { exceptions: shouldPauseOnExceptions, // There's no "n" in the key below (#1463117) diff --git a/devtools/client/debugger/src/actions/pause/resumed.js b/devtools/client/debugger/src/actions/pause/resumed.js index 47d55f84ca..2095bacc3b 100644 --- a/devtools/client/debugger/src/actions/pause/resumed.js +++ b/devtools/client/debugger/src/actions/pause/resumed.js @@ -14,7 +14,7 @@ import { inDebuggerEval } from "../../utils/pause/index"; * Debugger has just resumed. */ export function resumed(thread) { - return async ({ dispatch, client, getState }) => { + return async ({ dispatch, getState }) => { const why = getPauseReason(getState(), thread); const wasPausedInEval = inDebuggerEval(why); const wasStepping = isStepping(getState(), thread); diff --git a/devtools/client/debugger/src/actions/pause/tests/pause.spec.js b/devtools/client/debugger/src/actions/pause/tests/pause.spec.js index f8bd87375a..6c69ac4661 100644 --- a/devtools/client/debugger/src/actions/pause/tests/pause.spec.js +++ b/devtools/client/debugger/src/actions/pause/tests/pause.spec.js @@ -25,7 +25,7 @@ const mockCommandClient = { getFrames: async () => [], setBreakpoint: () => new Promise(_resolve => {}), sourceContents: ({ source }) => { - return new Promise((resolve, reject) => { + return new Promise(resolve => { switch (source) { case "foo1": return resolve({ @@ -184,7 +184,7 @@ describe("pause", () => { it("maps frame to original frames", async () => { const sourceMapLoaderMock = { - getOriginalStackFrames: loc => Promise.resolve(originStackFrames), + getOriginalStackFrames: () => Promise.resolve(originStackFrames), getOriginalLocation: () => Promise.resolve(debuggerToSourceMapLocation(originalLocation)), getOriginalLocations: async items => diff --git a/devtools/client/debugger/src/actions/preview.js b/devtools/client/debugger/src/actions/preview.js index c3bc8dbffd..3b3637e2c0 100644 --- a/devtools/client/debugger/src/actions/preview.js +++ b/devtools/client/debugger/src/actions/preview.js @@ -128,7 +128,7 @@ export function getPreview(target, tokenPos, codeMirror) { } export function getExceptionPreview(target, tokenPos, codeMirror) { - return async ({ dispatch, getState, parserWorker }) => { + return async ({ getState, parserWorker }) => { const match = await findExpressionMatch( getState(), parserWorker, diff --git a/devtools/client/debugger/src/actions/sources/loadSourceText.js b/devtools/client/debugger/src/actions/sources/loadSourceText.js index d3bbd53871..b523b1984a 100644 --- a/devtools/client/debugger/src/actions/sources/loadSourceText.js +++ b/devtools/client/debugger/src/actions/sources/loadSourceText.js @@ -43,7 +43,7 @@ async function loadGeneratedSource(sourceActor, { client }) { async function loadOriginalSource( source, - { getState, client, sourceMapLoader, prettyPrintWorker } + { getState, sourceMapLoader, prettyPrintWorker } ) { if (isPretty(source)) { const generatedSource = getGeneratedSource(getState(), source); diff --git a/devtools/client/debugger/src/actions/sources/newSources.js b/devtools/client/debugger/src/actions/sources/newSources.js index 4d9c2cd5f7..44e8595c42 100644 --- a/devtools/client/debugger/src/actions/sources/newSources.js +++ b/devtools/client/debugger/src/actions/sources/newSources.js @@ -36,30 +36,22 @@ import { prefs } from "../../utils/prefs"; import sourceQueue from "../../utils/source-queue"; import { validateSourceActor, ContextError } from "../../utils/context"; -function loadSourceMaps(sources) { +function loadSourceMapsForSourceActors(sourceActors) { return async function ({ dispatch }) { try { - const sourceList = await Promise.all( - sources.map(async sourceActor => { - const originalSourcesInfo = await dispatch( - loadSourceMap(sourceActor) - ); - originalSourcesInfo.forEach( - sourcesInfo => (sourcesInfo.sourceActor = sourceActor) - ); - sourceQueue.queueOriginalSources(originalSourcesInfo); - return originalSourcesInfo; - }) + await Promise.all( + sourceActors.map(sourceActor => dispatch(loadSourceMap(sourceActor))) ); - - await sourceQueue.flush(); - return sourceList.flat(); } catch (error) { + // This may throw a context error if we navigated while processing the source maps if (!(error instanceof ContextError)) { throw error; } } - return []; + + // Once all the source maps, of all the bulk of new source actors are processed, + // flush the SourceQueue. This help aggregate all the original sources in one action. + await sourceQueue.flush(); }; } @@ -70,7 +62,7 @@ function loadSourceMaps(sources) { function loadSourceMap(sourceActor) { return async function ({ dispatch, getState, sourceMapLoader, panel }) { if (!prefs.clientSourceMapsEnabled || !sourceActor.sourceMapURL) { - return []; + return; } let sources, ignoreListUrls, resolvedSourceMapURL, exception; @@ -135,12 +127,20 @@ function loadSourceMap(sourceActor) { type: "CLEAR_SOURCE_ACTOR_MAP_URL", sourceActor, }); - return []; + return; } // Before dispatching this action, ensure that the related sourceActor is still registered validateSourceActor(getState(), sourceActor); - return sources; + + for (const originalSource of sources) { + // The Source Map worker doesn't set the `sourceActor` attribute, + // which is handy to know what is the related bundle. + originalSource.sourceActor = sourceActor; + } + + // Register all the new reported original sources in the queue to be flushed once all new bundles are processed. + sourceQueue.queueOriginalSources(sources); }; } @@ -294,7 +294,7 @@ export function newGeneratedSource(sourceInfo) { } export function newGeneratedSources(sourceResources) { - return async ({ dispatch, getState, client }) => { + return async ({ dispatch, getState }) => { if (!sourceResources.length) { return []; } @@ -341,7 +341,7 @@ export function newGeneratedSources(sourceResources) { await dispatch(checkNewSources(newSources)); (async () => { - await dispatch(loadSourceMaps(newSourceActors)); + await dispatch(loadSourceMapsForSourceActors(newSourceActors)); // We would like to sync breakpoints after we are done // loading source maps as sometimes generated and original @@ -370,7 +370,7 @@ export function newGeneratedSources(sourceResources) { } function checkNewSources(sources) { - return async ({ dispatch, getState }) => { + return async ({ dispatch }) => { for (const source of sources) { dispatch(checkSelectedSource(source.id)); } diff --git a/devtools/client/debugger/src/actions/sources/prettyPrint.js b/devtools/client/debugger/src/actions/sources/prettyPrint.js index 6a12a34240..d1e46ac949 100644 --- a/devtools/client/debugger/src/actions/sources/prettyPrint.js +++ b/devtools/client/debugger/src/actions/sources/prettyPrint.js @@ -224,7 +224,7 @@ async function prettyPrintHtmlFile({ } function createPrettySource(source, sourceActor) { - return async ({ dispatch, sourceMapLoader, getState }) => { + return async ({ dispatch }) => { const url = getPrettyOriginalSourceURL(source); const id = generatedToOriginalId(source.id, url); const prettySource = createPrettyPrintOriginalSource(id, url); @@ -336,7 +336,7 @@ const memoizedPrettyPrintSource = memoizeableAction("setSymbols", { }); export function prettyPrintAndSelectSource(source) { - return async ({ dispatch, sourceMapLoader, getState }) => { + return async ({ dispatch }) => { const prettySource = await dispatch(memoizedPrettyPrintSource(source)); // Select the pretty/original source based on the location we may diff --git a/devtools/client/debugger/src/actions/sources/select.js b/devtools/client/debugger/src/actions/sources/select.js index 63200a398a..f25267374b 100644 --- a/devtools/client/debugger/src/actions/sources/select.js +++ b/devtools/client/debugger/src/actions/sources/select.js @@ -63,6 +63,11 @@ export const clearSelectedLocation = () => ({ type: "CLEAR_SELECTED_LOCATION", }); +export const setDefaultSelectedLocation = shouldSelectOriginalLocation => ({ + type: "SET_DEFAULT_SELECTED_LOCATION", + shouldSelectOriginalLocation, +}); + /** * Deterministically select a source that has a given URL. This will * work regardless of the connection status or if the source exists @@ -105,6 +110,76 @@ export function selectSource(source, sourceActor) { } /** + * Helper for `selectLocation`. + * Based on `keepContext` argument passed to `selectLocation`, + * this will automatically select the related mapped source (original or generated). + * + * @param {Object} location + * The location to select. + * @param {Boolean} keepContext + * If true, will try to select a mapped source. + * @param {Object} thunkArgs + * @return {Object} + * Object with two attributes: + * - `shouldSelectOriginalLocation`, to know if we should keep trying to select the original location + * - `newLocation`, for the final location to select + */ +async function mayBeSelectMappedSource(location, keepContext, thunkArgs) { + const { getState, dispatch } = thunkArgs; + // Preserve the current source map context (original / generated) + // when navigating to a new location. + // i.e. if keepContext isn't manually overriden to false, + // we will convert the source we want to select to either + // original/generated in order to match the currently selected one. + // If the currently selected source is original, we will + // automatically map `location` to refer to the original source, + // even if that used to refer only to the generated source. + let shouldSelectOriginalLocation = getShouldSelectOriginalLocation( + getState() + ); + if (keepContext) { + // Pretty print source may not be registered yet and getRelatedMapLocation may not return it. + // Wait for the pretty print source to be fully processed. + if ( + !location.source.isOriginal && + shouldSelectOriginalLocation && + hasPrettyTab(getState(), location.source) + ) { + // Note that prettyPrintAndSelectSource has already been called a bit before when this generated source has been added + // but it is a slow operation and is most likely not resolved yet. + // prettyPrintAndSelectSource uses memoization to avoid doing the operation more than once, while waiting from both callsites. + await dispatch(prettyPrintAndSelectSource(location.source)); + } + if (shouldSelectOriginalLocation != location.source.isOriginal) { + // Only try to map if the source is mapped. i.e. is original source or a bundle with a valid source map comment + if ( + location.source.isOriginal || + isSourceActorWithSourceMap(getState(), location.sourceActor.id) + ) { + // getRelatedMapLocation will convert to the related generated/original location. + // i.e if the original location is passed, the related generated location will be returned and vice versa. + location = await getRelatedMapLocation(location, thunkArgs); + } + // Note that getRelatedMapLocation may return the exact same location. + // For example, if the source-map is half broken, it may return a generated location + // while we were selecting original locations. So we may be seeing bundles intermittently + // when stepping through broken source maps. And we will see original sources when stepping + // through functional original sources. + } + } else if ( + location.source.isOriginal || + isSourceActorWithSourceMap(getState(), location.sourceActor.id) + ) { + // Only update this setting if the source is mapped. i.e. don't update if we select a regular source. + // The source is mapped when it is either: + // - an original source, + // - a bundle with a source map comment referencing a source map URL. + shouldSelectOriginalLocation = location.source.isOriginal; + } + return { shouldSelectOriginalLocation, newLocation: location }; +} + +/** * Select a new location. * This will automatically select the source in the source tree (if visible) * and open the source (a new tab and the source editor) @@ -144,47 +219,28 @@ export function selectLocation( return; } - // Preserve the current source map context (original / generated) - // when navigating to a new location. - // i.e. if keepContext isn't manually overriden to false, - // we will convert the source we want to select to either - // original/generated in order to match the currently selected one. - // If the currently selected source is original, we will - // automatically map `location` to refer to the original source, - // even if that used to refer only to the generated source. - let shouldSelectOriginalLocation = getShouldSelectOriginalLocation( - getState() - ); - if (keepContext) { - // Pretty print source may not be registered yet and getRelatedMapLocation may not return it. - // Wait for the pretty print source to be fully processed. - if ( - !location.source.isOriginal && - shouldSelectOriginalLocation && - hasPrettyTab(getState(), location.source) - ) { - // Note that prettyPrintAndSelectSource has already been called a bit before when this generated source has been added - // but it is a slow operation and is most likely not resolved yet. - // prettyPrintAndSelectSource uses memoization to avoid doing the operation more than once, while waiting from both callsites. - await dispatch(prettyPrintAndSelectSource(location.source)); - } - if (shouldSelectOriginalLocation != location.source.isOriginal) { - // getRelatedMapLocation will convert to the related generated/original location. - // i.e if the original location is passed, the related generated location will be returned and vice versa. - location = await getRelatedMapLocation(location, thunkArgs); - // Note that getRelatedMapLocation may return the exact same location. - // For example, if the source-map is half broken, it may return a generated location - // while we were selecting original locations. So we may be seeing bundles intermittently - // when stepping through broken source maps. And we will see original sources when stepping - // through functional original sources. + let sourceActor = location.sourceActor; + if (!sourceActor) { + sourceActor = getFirstSourceActorForGeneratedSource( + getState(), + source.id + ); + location = createLocation({ ...location, sourceActor }); + } - source = location.source; - } - } else { - shouldSelectOriginalLocation = location.source.isOriginal; + const lastSelectedLocation = getSelectedLocation(getState()); + const { shouldSelectOriginalLocation, newLocation } = + await mayBeSelectMappedSource(location, keepContext, thunkArgs); + + // Ignore the request if another location was selected while we were waiting for mayBeSelectMappedSource async completion + if (getSelectedLocation(getState()) != lastSelectedLocation) { + return; } - let sourceActor = location.sourceActor; + // Update all local variables after mapping + location = newLocation; + source = location.source; + sourceActor = location.sourceActor; if (!sourceActor) { sourceActor = getFirstSourceActorForGeneratedSource( getState(), @@ -351,11 +407,16 @@ export function jumpToMappedLocation(location) { // Map to either an original or a generated source location const pairedLocation = await getRelatedMapLocation(location, thunkArgs); + // If we are on a non-mapped source, this will return the same location + // so ignore the request. + if (pairedLocation == location) { + return null; + } + return dispatch(selectSpecificLocation(pairedLocation)); }; } -// This is only used by tests export function jumpToMappedSelectedLocation() { return async function ({ dispatch, getState }) { const location = getSelectedLocation(getState()); diff --git a/devtools/client/debugger/src/actions/sources/symbols.js b/devtools/client/debugger/src/actions/sources/symbols.js index c7b9132c32..e33c3e1036 100644 --- a/devtools/client/debugger/src/actions/sources/symbols.js +++ b/devtools/client/debugger/src/actions/sources/symbols.js @@ -10,7 +10,7 @@ import { loadSourceText } from "./loadSourceText"; import { memoizeableAction } from "../../utils/memoizableAction"; import { fulfilled } from "../../utils/async-value"; -async function doSetSymbols(location, { dispatch, getState, parserWorker }) { +async function doSetSymbols(location, { dispatch, parserWorker }) { await dispatch(loadSourceText(location.source, location.sourceActor)); await dispatch({ diff --git a/devtools/client/debugger/src/actions/sources/tests/newSources.spec.js b/devtools/client/debugger/src/actions/sources/tests/newSources.spec.js index cef9eca31e..19457a4bf5 100644 --- a/devtools/client/debugger/src/actions/sources/tests/newSources.spec.js +++ b/devtools/client/debugger/src/actions/sources/tests/newSources.spec.js @@ -7,10 +7,9 @@ import { selectors, createStore, makeSource, - makeSourceURL, makeOriginalSource, } from "../../../utils/test-head"; -const { getSource, getSourceCount, getSelectedSource } = selectors; +const { getSource, getSourceCount } = selectors; import { mockCommandClient } from "../../tests/helpers/mockCommandClient"; @@ -49,20 +48,6 @@ describe("sources - new sources", () => { expect(getSourceCount(getState())).toEqual(1); }); - it("should automatically select a pending source", async () => { - const { dispatch, getState } = createStore(mockCommandClient); - const baseSourceURL = makeSourceURL("base.js"); - await dispatch(actions.selectSourceURL(baseSourceURL)); - - expect(getSelectedSource(getState())).toBe(undefined); - const baseSource = await dispatch( - actions.newGeneratedSource(makeSource("base.js")) - ); - - const selected = getSelectedSource(getState()); - expect(selected && selected.url).toBe(baseSource.url); - }); - // eslint-disable-next-line it("should not attempt to fetch original sources if it's missing a source map url", async () => { const loadSourceMap = jest.fn(); diff --git a/devtools/client/debugger/src/actions/sources/tests/select.spec.js b/devtools/client/debugger/src/actions/sources/tests/select.spec.js index 5f4feba6c0..4cf0c58c94 100644 --- a/devtools/client/debugger/src/actions/sources/tests/select.spec.js +++ b/devtools/client/debugger/src/actions/sources/tests/select.spec.js @@ -8,7 +8,6 @@ import { createStore, createSourceObject, makeSource, - makeSourceURL, waitForState, makeOriginalSource, } from "../../../utils/test-head"; @@ -23,7 +22,7 @@ import { createLocation } from "../../../utils/location"; import { mockCommandClient } from "../../tests/helpers/mockCommandClient"; -process.on("unhandledRejection", (reason, p) => {}); +process.on("unhandledRejection", () => {}); function initialLocation(sourceId) { return createLocation({ source: createSourceObject(sourceId), line: 1 }); @@ -176,20 +175,4 @@ describe("sources", () => { const selected = getSelectedLocation(getState()); expect(selected && selected.line).toBe(1); }); - - describe("selectSourceURL", () => { - it("should automatically select a pending source", async () => { - const { dispatch, getState } = createStore(mockCommandClient); - const baseSourceURL = makeSourceURL("base.js"); - await dispatch(actions.selectSourceURL(baseSourceURL)); - - expect(getSelectedSource(getState())).toBe(undefined); - const baseSource = await dispatch( - actions.newGeneratedSource(makeSource("base.js")) - ); - - const selected = getSelectedSource(getState()); - expect(selected && selected.url).toBe(baseSource.url); - }); - }); }); diff --git a/devtools/client/debugger/src/actions/tests/expressions.spec.js b/devtools/client/debugger/src/actions/tests/expressions.spec.js index c69276fa40..093f99e228 100644 --- a/devtools/client/debugger/src/actions/tests/expressions.spec.js +++ b/devtools/client/debugger/src/actions/tests/expressions.spec.js @@ -6,7 +6,7 @@ import { actions, selectors, createStore } from "../../utils/test-head"; const mockThreadFront = { evaluate: (script, { frameId }) => - new Promise((resolve, reject) => { + new Promise(resolve => { if (!frameId) { resolve("bla"); } else { @@ -16,8 +16,8 @@ const mockThreadFront = { evaluateExpressions: (inputs, { frameId }) => Promise.all( inputs.map( - input => - new Promise((resolve, reject) => { + () => + new Promise(resolve => { if (!frameId) { resolve("bla"); } else { diff --git a/devtools/client/debugger/src/actions/tests/helpers/mockCommandClient.js b/devtools/client/debugger/src/actions/tests/helpers/mockCommandClient.js index 38dd55c274..2e0333fc99 100644 --- a/devtools/client/debugger/src/actions/tests/helpers/mockCommandClient.js +++ b/devtools/client/debugger/src/actions/tests/helpers/mockCommandClient.js @@ -29,7 +29,7 @@ const sources = [ ]; export const mockCommandClient = { - sourceContents: function ({ source }) { + sourceContents({ source }) { return new Promise((resolve, reject) => { if (sources.includes(source)) { resolve(createSource(source)); diff --git a/devtools/client/debugger/src/actions/toolbox.js b/devtools/client/debugger/src/actions/toolbox.js index a343c92863..4c88ec6d24 100644 --- a/devtools/client/debugger/src/actions/toolbox.js +++ b/devtools/client/debugger/src/actions/toolbox.js @@ -12,6 +12,12 @@ export function openLink(url) { }; } +export function openSourceMap(url, line, column) { + return async function ({ panel }) { + return panel.toolbox.viewSource(url, line, column); + }; +} + export function evaluateInConsole(inputString) { return async ({ panel }) => { return panel.openConsoleAndEvaluate(inputString); @@ -24,7 +30,7 @@ export function openElementInInspectorCommand(grip) { }; } -export function openInspector(grip) { +export function openInspector() { return async ({ panel }) => { return panel.openInspector(); }; diff --git a/devtools/client/debugger/src/actions/ui.js b/devtools/client/debugger/src/actions/ui.js index 2424c658b8..8d4d62307a 100644 --- a/devtools/client/debugger/src/actions/ui.js +++ b/devtools/client/debugger/src/actions/ui.js @@ -16,7 +16,7 @@ import { selectSource } from "../actions/sources/select"; import { getEditor, getLocationsInViewport, - updateDocuments, + updateEditorLineWrapping, } from "../utils/editor/index"; import { blackboxSourceActorsForSource } from "./sources/blackbox"; import { toggleBreakpoints } from "./breakpoints/index"; @@ -66,7 +66,7 @@ export function setActiveSearch(activeSearch) { } export function toggleFrameworkGrouping(toggleValue) { - return ({ dispatch, getState }) => { + return ({ dispatch }) => { dispatch({ type: "TOGGLE_FRAMEWORK_GROUPING", value: toggleValue, @@ -75,7 +75,7 @@ export function toggleFrameworkGrouping(toggleValue) { } export function toggleInlinePreview(toggleValue) { - return ({ dispatch, getState }) => { + return ({ dispatch }) => { dispatch({ type: "TOGGLE_INLINE_PREVIEW", value: toggleValue, @@ -84,8 +84,8 @@ export function toggleInlinePreview(toggleValue) { } export function toggleEditorWrapping(toggleValue) { - return ({ dispatch, getState }) => { - updateDocuments(doc => doc.cm.setOption("lineWrapping", toggleValue)); + return ({ dispatch }) => { + updateEditorLineWrapping(toggleValue); dispatch({ type: "TOGGLE_EDITOR_WRAPPING", @@ -95,7 +95,7 @@ export function toggleEditorWrapping(toggleValue) { } export function toggleSourceMapsEnabled(toggleValue) { - return ({ dispatch, getState }) => { + return ({ dispatch }) => { dispatch({ type: "TOGGLE_SOURCE_MAPS_ENABLED", value: toggleValue, @@ -217,7 +217,7 @@ export function setSearchOptions(searchKey, searchOptions) { } export function copyToClipboard(location) { - return ({ dispatch, getState }) => { + return ({ getState }) => { const content = getSourceTextContent(getState(), location); if (content && isFulfilled(content) && content.value.type === "text") { copyToTheClipboard(content.value.value); @@ -257,7 +257,7 @@ export function toggleJavascriptTracingOnNextLoad() { } export function setHideOrShowIgnoredSources(shouldHide) { - return ({ dispatch, getState }) => { + return ({ dispatch }) => { dispatch({ type: "HIDE_IGNORED_SOURCES", shouldHide }); }; } diff --git a/devtools/client/debugger/src/actions/utils/middleware/context.js b/devtools/client/debugger/src/actions/utils/middleware/context.js index 00711a8c3f..350a90d66e 100644 --- a/devtools/client/debugger/src/actions/utils/middleware/context.js +++ b/devtools/client/debugger/src/actions/utils/middleware/context.js @@ -27,7 +27,7 @@ function validateActionContext(getState, action) { // Middleware which looks for actions that have a cx property and ignores // them if the context is no longer valid. -function context({ dispatch, getState }) { +function context({ getState }) { return next => action => { if ("cx" in action) { validateActionContext(getState, action); diff --git a/devtools/client/debugger/src/actions/utils/middleware/log.js b/devtools/client/debugger/src/actions/utils/middleware/log.js index b9592ce22c..d228669dd4 100644 --- a/devtools/client/debugger/src/actions/utils/middleware/log.js +++ b/devtools/client/debugger/src/actions/utils/middleware/log.js @@ -92,7 +92,7 @@ function serializeAction(action) { * A middleware that logs all actions coming through the system * to the console. */ -export function log({ dispatch, getState }) { +export function log() { return next => action => { const asyncMsg = !action.status ? "" : `[${action.status}]`; diff --git a/devtools/client/debugger/src/actions/utils/middleware/promise.js b/devtools/client/debugger/src/actions/utils/middleware/promise.js index 52054a1fcc..2b88bb8ca9 100644 --- a/devtools/client/debugger/src/actions/utils/middleware/promise.js +++ b/devtools/client/debugger/src/actions/utils/middleware/promise.js @@ -21,7 +21,7 @@ function seqIdGen() { return seqIdVal++; } -function promiseMiddleware({ dispatch, getState }) { +function promiseMiddleware({ dispatch }) { return next => action => { if (!(PROMISE in action)) { return next(action); @@ -42,7 +42,7 @@ function promiseMiddleware({ dispatch, getState }) { .finally(() => new Promise(resolve => executeSoon(resolve))) .then( value => { - dispatch({ ...action, status: "done", value: value }); + dispatch({ ...action, status: "done", value }); return value; }, error => { diff --git a/devtools/client/debugger/src/actions/utils/middleware/timing.js b/devtools/client/debugger/src/actions/utils/middleware/timing.js index d0bfa05977..8b6b7baf5e 100644 --- a/devtools/client/debugger/src/actions/utils/middleware/timing.js +++ b/devtools/client/debugger/src/actions/utils/middleware/timing.js @@ -9,13 +9,13 @@ const mark = window.performance?.mark ? window.performance.mark.bind(window.performance) - : a => {}; + : () => {}; const measure = window.performance?.measure ? window.performance.measure.bind(window.performance) - : (a, b, c) => {}; + : () => {}; -export function timing(store) { +export function timing() { return next => action => { mark(`${action.type}_start`); const result = next(action); |