diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:14:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:14:29 +0000 |
commit | fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8 (patch) | |
tree | 4c1ccaf5486d4f2009f9a338a98a83e886e29c97 /devtools/client/debugger/src/actions/sources | |
parent | Releasing progress-linux version 124.0.1-1~progress7.99u1. (diff) | |
download | firefox-fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8.tar.xz firefox-fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8.zip |
Merging upstream version 125.0.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'devtools/client/debugger/src/actions/sources')
7 files changed, 128 insertions, 99 deletions
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); - }); - }); }); |