forked from firefox-devtools/profiler
-
Notifications
You must be signed in to change notification settings - Fork 0
/
zipped-profiles.js
106 lines (95 loc) · 3.61 KB
/
zipped-profiles.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// @flow
import { getZipFileTable, getZipFileState } from '../selectors/zipped-profiles';
import { unserializeProfileOfArbitraryFormat } from '../profile-logic/process-profile';
import { loadProfile } from './receive-profile';
import type { Action, ThunkAction } from '../types/store';
import type { IndexIntoZipFileTable } from '../profile-logic/zip-files';
export function changeSelectedZipFile(
selectedZipFileIndex: IndexIntoZipFileTable
): Action {
return {
type: 'CHANGE_SELECTED_ZIP_FILE',
selectedZipFileIndex,
};
}
export function changeExpandedZipFile(
expandedZipFileIndexes: Array<IndexIntoZipFileTable | null>
): Action {
return {
type: 'CHANGE_EXPANDED_ZIP_FILES',
expandedZipFileIndexes,
};
}
/**
* This ThunkAction has a bit of complexity to it, due to the asynchronous nature of
* reading in profiles from a zip file. The UrlState represents the current desired state
* of what file to view in a zip file, and the ZipFileState represents the actual steps
* being performed to reach the desired UrlState.
*
* This ThunkAction needs to properly handle when the UrlState changes faster than it
* can change the ZipFileState, and not have any race conditions.
*/
export function viewProfileFromZip(
zipFileIndex: IndexIntoZipFileTable,
initialLoad: boolean = false
): ThunkAction<Promise<void>> {
return async (dispatch, getState) => {
const zipFileTable = getZipFileTable(getState());
const pathInZipFile = zipFileTable.path[zipFileIndex];
const file = zipFileTable.file[zipFileIndex];
if (!file) {
throw new Error(
'Attempted to load a zip file that did not exist or was a directory.'
);
}
dispatch({ type: 'PROCESS_PROFILE_FROM_ZIP_FILE', pathInZipFile });
try {
// Attempt to unserialize the profile.
const profile = await unserializeProfileOfArbitraryFormat(
await file.async('string')
);
// Since this is an async function, there can be race conditions. Prevent this by
// comparing this request with the current state of the store. If this result
// is invalid, don't dispatch anything, and discard the profile.
const zipFileState = getZipFileState(getState());
if (
zipFileState.pathInZipFile === pathInZipFile &&
zipFileState.phase === 'PROCESS_PROFILE_FROM_ZIP_FILE'
) {
await dispatch(loadProfile(profile, { pathInZipFile }, initialLoad));
}
} catch (error) {
console.error(
'Failed to process the profile in the zip file with the following error:'
);
console.error(error);
dispatch({ type: 'FAILED_TO_PROCESS_PROFILE_FROM_ZIP_FILE', error });
}
};
}
/**
* This function can take a zip file path, but the path can come from the URL, so
* don't really trust it.
*/
export function viewProfileFromPathInZipFile(
pathInZipFile: string
): ThunkAction<Promise<void>> {
return (dispatch, getState) => {
const zipFileTable = getZipFileTable(getState());
const zipFileIndex = zipFileTable.path.indexOf(pathInZipFile);
if (zipFileIndex === -1) {
dispatch(showErrorForNoFileInZip(pathInZipFile));
return Promise.resolve();
}
return dispatch(viewProfileFromZip(zipFileIndex));
};
}
export function returnToZipFileList() {
return { type: 'RETURN_TO_ZIP_FILE_LIST' };
}
export function showErrorForNoFileInZip(pathInZipFile: string) {
return { type: 'FILE_NOT_FOUND_IN_ZIP_FILE', pathInZipFile };
}