Skip to content

Commit fff55d5

Browse files
committed
remove selectedMarker from view state, instead keep it in url state only
1 parent 206393b commit fff55d5

File tree

9 files changed

+47
-91
lines changed

9 files changed

+47
-91
lines changed

src/actions/receive-profile.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ import {
4141
import {
4242
getSelectedTab,
4343
getTabFilter,
44-
getSelectedMarkers,
4544
} from 'firefox-profiler/selectors/url-state';
4645
import {
4746
getTabToThreadIndexesMap,
@@ -359,8 +358,6 @@ export function finalizeFullProfileView(
359358
}
360359
}
361360

362-
const selectedMarkers = hasUrlInfo ? getSelectedMarkers(getState()) : {};
363-
364361
withHistoryReplaceStateSync(() => {
365362
dispatch({
366363
type: 'VIEW_FULL_PROFILE',
@@ -371,7 +368,6 @@ export function finalizeFullProfileView(
371368
localTracksByPid,
372369
localTrackOrderByPid,
373370
timelineType,
374-
selectedMarkers,
375371
...hiddenTracks,
376372
});
377373
});

src/reducers/profile-view.ts

Lines changed: 11 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,6 @@ export const defaultThreadViewOptions: ThreadViewOptions = {
137137
selectedInvertedCallNodePath: [],
138138
expandedNonInvertedCallNodePaths: new PathSet(),
139139
expandedInvertedCallNodePaths: new PathSet(),
140-
selectedMarker: null,
141140
selectedNetworkMarker: null,
142141
lastSeenTransformCount: 0,
143142
};
@@ -174,20 +173,6 @@ const viewOptionsPerThread: Reducer<ThreadViewOptionsPerThreads> = (
174173
case 'PROFILE_LOADED':
175174
// The view options are lazily initialized. Reset to the default values.
176175
return {};
177-
case 'VIEW_FULL_PROFILE': {
178-
// Initialize selectedMarker for each thread from the URL state.
179-
const { selectedMarkers } = action;
180-
const newState: ThreadViewOptionsPerThreads = {};
181-
for (const [threadsKey, markerIndex] of Object.entries(selectedMarkers)) {
182-
if (markerIndex !== null) {
183-
newState[threadsKey] = {
184-
..._getThreadViewOptions(state, threadsKey),
185-
selectedMarker: markerIndex,
186-
};
187-
}
188-
}
189-
return Object.keys(newState).length > 0 ? newState : state;
190-
}
191176
case 'BULK_SYMBOLICATION': {
192177
const { oldFuncToNewFuncsMaps } = action;
193178
// For each thread, apply oldFuncToNewFuncsMap to that thread's
@@ -342,10 +327,6 @@ const viewOptionsPerThread: Reducer<ThreadViewOptionsPerThreads> = (
342327
: { expandedNonInvertedCallNodePaths: expandedCallNodePaths }
343328
);
344329
}
345-
case 'CHANGE_SELECTED_MARKER': {
346-
const { threadsKey, selectedMarker } = action;
347-
return _updateThreadViewOptions(state, threadsKey, { selectedMarker });
348-
}
349330
case 'CHANGE_SELECTED_NETWORK_MARKER': {
350331
const { threadsKey, selectedNetworkMarker } = action;
351332
return _updateThreadViewOptions(state, threadsKey, {
@@ -417,44 +398,24 @@ const viewOptionsPerThread: Reducer<ThreadViewOptionsPerThreads> = (
417398
});
418399
}
419400
case 'UPDATE_URL_STATE': {
420-
// When the URL state changes (e.g., via browser back button):
421-
// 1. Check if the transform stack has been popped for each thread.
422-
// If so, reset the stored paths, because they may reference call nodes
423-
// that only exist in a transformed tree.
424-
// See: https://github.com/firefox-devtools/profiler/issues/5689.
425-
// 2. Sync selected marker with URL state.
426-
401+
// When the URL state changes (e.g., via browser back button), check if the
402+
// transform stack has been popped for each thread. If so, reset the stored paths
403+
// because they may reference call nodes that only exist in a transformed tree.
404+
// See: https://github.com/firefox-devtools/profiler/issues/5689
427405
if (!action.newUrlState) {
428406
return state;
429407
}
430408

431-
const { transforms, selectedMarkers } =
432-
action.newUrlState.profileSpecific;
409+
const { transforms } = action.newUrlState.profileSpecific;
433410
return objectMap(state, (viewOptions, threadsKey) => {
434411
const transformStack = transforms[threadsKey] || [];
435412
const newTransformCount = transformStack.length;
436413
const oldTransformCount = viewOptions.lastSeenTransformCount;
437414

438-
// Get the selected marker from URL state for this thread
439-
const urlSelectedMarker = selectedMarkers[threadsKey] ?? null;
440-
const currentSelectedMarker = viewOptions.selectedMarker;
441-
442-
// Check if we need to update anything
443-
const transformCountChanged = newTransformCount < oldTransformCount;
444-
const markerChanged = urlSelectedMarker !== currentSelectedMarker;
445-
446-
if (!transformCountChanged && !markerChanged) {
447-
// No change needed
448-
return viewOptions;
449-
}
450-
451-
// Build the updated view options
452-
let updatedOptions = { ...viewOptions };
453-
454-
// If transform count changed, reset the paths
455-
if (transformCountChanged) {
456-
updatedOptions = {
457-
...updatedOptions,
415+
// If transform count changed, reset the paths.
416+
if (newTransformCount < oldTransformCount) {
417+
return {
418+
...viewOptions,
458419
selectedNonInvertedCallNodePath: [],
459420
selectedInvertedCallNodePath: [],
460421
expandedNonInvertedCallNodePaths: new PathSet(),
@@ -463,15 +424,8 @@ const viewOptionsPerThread: Reducer<ThreadViewOptionsPerThreads> = (
463424
};
464425
}
465426

466-
// If marker changed, sync it from URL state
467-
if (markerChanged) {
468-
updatedOptions = {
469-
...updatedOptions,
470-
selectedMarker: urlSelectedMarker,
471-
};
472-
}
473-
474-
return updatedOptions;
427+
// No change needed.
428+
return viewOptions;
475429
});
476430
}
477431
case 'CHANGE_IMPLEMENTATION_FILTER': {

src/selectors/per-thread/markers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ export function getMarkerSelectorsPerThread(
552552
* This returns the marker index for the currently selected marker.
553553
*/
554554
const getSelectedMarkerIndex: Selector<MarkerIndex | null> = (state) =>
555-
threadSelectors.getViewOptions(state).selectedMarker;
555+
UrlState.getSelectedMarker(state, threadsKey);
556556

557557
/**
558558
* From the previous value, this returns the full marker object for the

src/selectors/url-state.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import type {
3131
NativeSymbolInfo,
3232
TabID,
3333
IndexIntoSourceTable,
34-
SelectedMarkersPerThread,
34+
MarkerIndex,
3535
} from 'firefox-profiler/types';
3636

3737
import type { TabSlug } from '../app-logic/tabs-handling';
@@ -168,8 +168,6 @@ export const getTabFilter: Selector<TabID | null> = (state) =>
168168
getProfileSpecificState(state).tabFilter;
169169
export const hasTabFilter: Selector<boolean> = (state) =>
170170
getTabFilter(state) !== null;
171-
export const getSelectedMarkers: Selector<SelectedMarkersPerThread> = (state) =>
172-
getProfileSpecificState(state).selectedMarkers;
173171

174172
/**
175173
* This selector does a simple lookup in the set of hidden tracks for a PID, and ensures
@@ -239,6 +237,12 @@ export const getTransformStack: DangerousSelectorWithArguments<
239237
);
240238
};
241239

240+
export const getSelectedMarker: DangerousSelectorWithArguments<
241+
MarkerIndex | null,
242+
ThreadsKey
243+
> = (state, threadsKey) =>
244+
getProfileSpecificState(state).selectedMarkers[threadsKey] ?? null;
245+
242246
export const getIsBottomBoxOpen: Selector<boolean> = (state) => {
243247
const tab = getSelectedTab(state);
244248
return getProfileSpecificState(state).isBottomBoxOpenPerPanel[tab];

src/test/store/__snapshots__/profile-view.test.ts.snap

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4428,7 +4428,6 @@ Object {
44284428
},
44294429
"lastSeenTransformCount": 1,
44304430
"selectedInvertedCallNodePath": Array [],
4431-
"selectedMarker": 1,
44324431
"selectedNetworkMarker": null,
44334432
"selectedNonInvertedCallNodePath": Array [
44344433
0,

src/test/store/profile-view.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,11 +1046,11 @@ describe('actions/ProfileView', function () {
10461046
const { dispatch, getState } = storeWithProfile(profile);
10471047

10481048
expect(
1049-
selectedThreadSelectors.getViewOptions(getState()).selectedMarker
1049+
selectedThreadSelectors.getSelectedMarkerIndex(getState())
10501050
).toEqual(null);
10511051
dispatch(ProfileView.changeSelectedMarker(0, 0));
10521052
expect(
1053-
selectedThreadSelectors.getViewOptions(getState()).selectedMarker
1053+
selectedThreadSelectors.getSelectedMarkerIndex(getState())
10541054
).toEqual(0);
10551055
});
10561056
});

src/test/url-handling.test.ts

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2036,9 +2036,9 @@ describe('selectedMarker', function () {
20362036
profile
20372037
);
20382038

2039-
expect(
2040-
selectedThreadSelectors.getViewOptions(getState()).selectedMarker
2041-
).toEqual(markerIndex);
2039+
expect(selectedThreadSelectors.getSelectedMarkerIndex(getState())).toEqual(
2040+
markerIndex
2041+
);
20422042
});
20432043

20442044
it('handles marker index 0 correctly on marker-chart tab', () => {
@@ -2121,6 +2121,11 @@ describe('selectedMarker', function () {
21212121
});
21222122

21232123
it('survives a URL round-trip on marker-chart tab', () => {
2124+
const profile = getProfileWithMarkers([
2125+
['Marker A', 0, null],
2126+
['Marker B', 1, null],
2127+
['Marker C', 2, null],
2128+
]);
21242129
const { dispatch, getState } = setup(
21252130
'?thread=0',
21262131
'/public/1ecd7a421948995171a4bb483b7bcc8e1868cc57/marker-chart/'
@@ -2137,13 +2142,16 @@ describe('selectedMarker', function () {
21372142
// URL should contain the marker
21382143
expect(url).toContain(`marker=${markerIndex}`);
21392144

2140-
// Parse the URL back
2141-
const newUrlState = stateFromLocation(
2142-
new URL(url, 'https://profiler.firefox.com')
2145+
// Load a fresh store from the URL and verify via selector
2146+
const { getState: getState2 } = _getStoreWithURL(
2147+
{
2148+
pathname:
2149+
'/public/1ecd7a421948995171a4bb483b7bcc8e1868cc57/marker-chart/',
2150+
search: new URL(url, 'https://profiler.firefox.com').search,
2151+
},
2152+
profile
21432153
);
2144-
2145-
// The marker should be in the parsed URL state
2146-
expect(newUrlState.profileSpecific.selectedMarkers[threadsKey]).toEqual(
2154+
expect(selectedThreadSelectors.getSelectedMarkerIndex(getState2())).toEqual(
21472155
markerIndex
21482156
);
21492157
});
@@ -2256,9 +2264,9 @@ describe('selectedMarker', function () {
22562264

22572265
// Select marker 1
22582266
dispatch(changeSelectedMarker(threadsKey, 1));
2259-
expect(
2260-
selectedThreadSelectors.getViewOptions(getState()).selectedMarker
2261-
).toEqual(1);
2267+
expect(selectedThreadSelectors.getSelectedMarkerIndex(getState())).toEqual(
2268+
1
2269+
);
22622270

22632271
// Simulate browser navigation by dispatching UPDATE_URL_STATE
22642272
// with a different selected marker (as if we navigated back)
@@ -2274,10 +2282,9 @@ describe('selectedMarker', function () {
22742282
};
22752283
dispatch({ type: 'UPDATE_URL_STATE', newUrlState });
22762284

2277-
// View state should now sync with the URL state
2278-
expect(
2279-
selectedThreadSelectors.getViewOptions(getState()).selectedMarker
2280-
).toEqual(2);
2285+
expect(selectedThreadSelectors.getSelectedMarkerIndex(getState())).toEqual(
2286+
2
2287+
);
22812288

22822289
// Now simulate navigating to a state with no selected marker
22832290
const urlStateWithNoMarker = {
@@ -2291,9 +2298,8 @@ describe('selectedMarker', function () {
22912298
};
22922299
dispatch({ type: 'UPDATE_URL_STATE', newUrlState: urlStateWithNoMarker });
22932300

2294-
// View state should clear the selected marker
2295-
expect(
2296-
selectedThreadSelectors.getViewOptions(getState()).selectedMarker
2297-
).toEqual(null);
2301+
expect(selectedThreadSelectors.getSelectedMarkerIndex(getState())).toEqual(
2302+
null
2303+
);
22982304
});
22992305
});

src/types/actions.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ import type {
3939
ApiQueryError,
4040
TableViewOptions,
4141
DecodedInstruction,
42-
SelectedMarkersPerThread,
4342
} from './state';
4443
import type { CssPixels, StartEndRange, Milliseconds } from './units';
4544
import type { BrowserConnectionStatus } from '../app-logic/browser-connection';
@@ -399,7 +398,6 @@ type ReceiveProfileAction =
399398
readonly localTrackOrderByPid: Map<Pid, TrackIndex[]>;
400399
readonly timelineType: TimelineType | null;
401400
readonly selectedTab: TabSlug;
402-
readonly selectedMarkers: SelectedMarkersPerThread;
403401
}
404402
| {
405403
readonly type: 'DATA_RELOAD';

src/types/state.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ export type ThreadViewOptions = {
5656
readonly selectedInvertedCallNodePath: CallNodePath;
5757
readonly expandedNonInvertedCallNodePaths: PathSet;
5858
readonly expandedInvertedCallNodePaths: PathSet;
59-
readonly selectedMarker: MarkerIndex | null;
6059
readonly selectedNetworkMarker: MarkerIndex | null;
6160
// Track the number of transforms to detect when they change via browser
6261
// navigation. This helps us know when to reset paths that may be invalid

0 commit comments

Comments
 (0)