Bug 1534847 - Part 2: Invalidate the loadSourceText cache on navigation. r=jlast

Summary:

If users navigate while source text is loading, we need to ignore existing
cached promises because they may resolve and then not actually set the
resulting source, because the source was deleted from the source list.
We want to explicitly use a new cache entry if we have navigated.

Reviewers: jlast

Bug #: 1534847

Differential Revision: https://phabricator.services.mozilla.com/D23452
This commit is contained in:
Logan Smyth 2019-03-14 14:35:30 -04:00 коммит произвёл Jason Laster
Родитель cefa9c5b9c
Коммит d1e54f1409
3 изменённых файлов: 30 добавлений и 6 удалений

Просмотреть файл

@ -5,7 +5,7 @@
// @flow
import { PROMISE } from "../utils/middleware/promise";
import { getSource } from "../../selectors";
import { getSource, getSourcesEpoch } from "../../selectors";
import { setBreakpointPositions } from "../breakpoints";
import * as parser from "../../workers/parser";
@ -44,6 +44,7 @@ async function loadSource(state, source: Source, { sourceMaps, client }) {
async function loadSourceTextPromise(
source: Source,
epoch: number,
{ dispatch, getState, client, sourceMaps }: ThunkArgs
): Promise<?Source> {
if (isLoaded(source)) {
@ -53,6 +54,7 @@ async function loadSourceTextPromise(
await dispatch({
type: "LOAD_SOURCE_TEXT",
sourceId: source.id,
epoch,
[PROMISE]: loadSource(getState(), source, { sourceMaps, client })
});
@ -82,12 +84,14 @@ export function loadSourceText(inputSource: ?Source) {
// below in a way that Flow is happy with.
const source = inputSource;
const id = source.id;
const epoch = getSourcesEpoch(thunkArgs.getState());
const id = `${epoch}:${source.id}`;
let promise = requests.get(id);
if (!promise) {
promise = (async () => {
try {
return await loadSourceTextPromise(source, thunkArgs);
return await loadSourceTextPromise(source, epoch, thunkArgs);
} catch (e) {
// TODO: This swallows errors for now. Ideally we would get rid of
// this once we have a better handle on our async state management.

Просмотреть файл

@ -10,7 +10,8 @@ import type { PromiseAction } from "../utils/middleware/promise";
export type LoadSourceAction = PromiseAction<
{|
+type: "LOAD_SOURCE_TEXT",
+sourceId: string
+sourceId: string,
+epoch: number
|},
Source
>;

Просмотреть файл

@ -36,6 +36,8 @@ type DisplayedSources = { [ThreadId]: { [SourceId]: boolean } };
type GetDisplayedSourcesSelector = OuterState => { [ThreadId]: SourcesMap };
export type SourcesState = {
epoch: number,
// All known sources.
sources: SourcesMap,
@ -63,6 +65,7 @@ const emptySources = {
export function initialSourcesState(): SourcesState {
return {
...emptySources,
epoch: 1,
selectedLocation: undefined,
pendingSelectedLocation: prefs.pendingSelectedLocation,
projectDirectoryRoot: prefs.projectDirectoryRoot,
@ -164,7 +167,10 @@ function update(
return updateProjectDirectoryRoot(state, action.url);
case "NAVIGATE":
return initialSourcesState();
return {
...initialSourcesState(),
epoch: state.epoch + 1
};
case "SET_FOCUSED_SOURCE_ITEM":
return { ...state, focusedItem: action.item };
@ -296,10 +302,19 @@ function updateProjectDirectoryRoot(state: SourcesState, root: string) {
* Update a source's loaded state fields
* i.e. loadedState, text, error
*/
function updateLoadedState(state, action: LoadSourceAction): SourcesState {
function updateLoadedState(
state: SourcesState,
action: LoadSourceAction
): SourcesState {
const { sourceId } = action;
let source;
// If there was a nativation between the time the action was started and
// completed, we don't want to update the store.
if (action.epoch !== state.epoch) {
return state;
}
if (action.status === "start") {
source = { id: sourceId, loadedState: "loading" };
} else if (action.status === "error") {
@ -522,6 +537,10 @@ export function getSources(state: OuterState) {
return state.sources.sources;
}
export function getSourcesEpoch(state: OuterState) {
return state.sources.epoch;
}
export function getUrls(state: OuterState) {
return state.sources.urls;
}