From 66e0c52cd59aa99c36d1edd77d32fce2e9f60447 Mon Sep 17 00:00:00 2001 From: Alexandre Poirot Date: Thu, 27 Feb 2025 18:00:13 +0000 Subject: [PATCH] Bug 1943130 - [devtools] Avoid fetching breakable lines more than once per source. r=devtools-reviewers,bomsy We can't use either `memoizableAction`, nor the `promise` middleware. * `memoizableAction` helps memoize on the long run, but doesn't handle mutliple calls made for the same ressource *before* the async computation of the value. * `promise` middleware explicitely prevents exposing the actual Promise to the reducer, which prevents exposing it to the selector. Ultimately, we have to expose that async promise done to compute the reducer data, that's the one the frontend should wait for on any subsequent run for the same ressource. These two existing APIs are weak and should probably be removed in favor of something similar to the current patch. Last, but not least, this approach doesn't leak all data indefinitely until DevTools closes (`memoizableAction` does) Also tweak the new-source action in order to force fetching breakable lines of HTML sources, only if that source is currently selected. We were computing breakable lines for all HTML sources, for example when having iframes. Differential Revision: https://phabricator.services.mozilla.com/D235208 --- .../src/actions/sources/breakableLines.js | 87 ++++++++++++++----- .../src/actions/sources/newSources.js | 25 ++++-- .../debugger/src/reducers/source-actors.js | 7 +- .../client/debugger/src/reducers/sources.js | 6 +- .../debugger/src/selectors/source-actors.js | 27 +++--- .../client/debugger/src/selectors/sources.js | 10 ++- 6 files changed, 116 insertions(+), 46 deletions(-) diff --git a/devtools/client/debugger/src/actions/sources/breakableLines.js b/devtools/client/debugger/src/actions/sources/breakableLines.js index 98d0b49a3717d1..cec8357221df89 100644 --- a/devtools/client/debugger/src/actions/sources/breakableLines.js +++ b/devtools/client/debugger/src/actions/sources/breakableLines.js @@ -5,6 +5,7 @@ import { getBreakableLines, getSourceActorBreakableLines, + getSourceActorsForSource, } from "../../selectors/index"; import { setBreakpointPositions } from "../breakpoints/breakpointPositions"; @@ -22,47 +23,93 @@ function calculateBreakableLines(positions) { /** * Ensure that breakable lines for a given source are fetched. * + * This method is memoized, so that if concurrent calls are dispatched, + * it will return the first async promise processing the breakable lines. + * * @param Object location */ export function setBreakableLines(location) { return async ({ getState, dispatch, client }) => { - let breakableLines; if (location.source.isOriginal) { - const positions = await dispatch(setBreakpointPositions(location)); - breakableLines = calculateBreakableLines(positions); - - const existingBreakableLines = getBreakableLines( - getState(), - location.source.id - ); - if (existingBreakableLines) { - breakableLines = [ - ...new Set([...existingBreakableLines, ...breakableLines]), - ]; + // Original sources have a dedicated codepath to fetch locations + // from the generated source actor and then map them to "positions" + // in the original source. + let promise = getBreakableLines(getState(), location.source.id); + if (promise) { + return promise; } - + promise = (async () => { + const positions = await dispatch(setBreakpointPositions(location)); + return calculateBreakableLines(positions); + })(); + dispatch({ + type: "SET_ORIGINAL_BREAKABLE_LINES", + source: location.source, + promise, + }); + const breakableLines = await promise; dispatch({ type: "SET_ORIGINAL_BREAKABLE_LINES", source: location.source, breakableLines, }); + } else if (location.source.isHTML) { + // For a given HTML page, we get a unique Source (for the html page), + // but many Source Actors (one per inline