Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
325 changes: 286 additions & 39 deletions src/actions/receive-profile.js

Large diffs are not rendered by default.

186 changes: 136 additions & 50 deletions src/app-logic/url-handling.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
} from '../utils/flow';
import { toValidCallTreeSummaryStrategy } from '../profile-logic/profile-data';
import { oneLine } from 'common-tags';
import type { UrlState } from '../types/state';
import type { UrlState, TimelineTrackOrganization } from '../types/state';
import type { DataSource } from '../types/actions';
import type {
Pid,
Expand Down Expand Up @@ -97,6 +97,9 @@ type FullProfileSpecificBaseQuery = {|
// Base query that only applies to active tab profile view.
type ActiveTabProfileSpecificBaseQuery = {||};

// Base query that only applies to origins profile view.
type OriginsProfileSpecificBaseQuery = {||};

// "null | void" in the query objects are flags which map to true for null, and false
// for void. False flags do not show up the URL.
type BaseQuery = {|
Expand All @@ -107,9 +110,11 @@ type BaseQuery = {|
transforms: string,
profiles: string[],
profileName: string,
showTabOnly1: BrowsingContextID,
ctxId: BrowsingContextID,
view: string,
...FullProfileSpecificBaseQuery,
...ActiveTabProfileSpecificBaseQuery,
...OriginsProfileSpecificBaseQuery,
|};

type CallTreeQuery = {|
Expand Down Expand Up @@ -162,6 +167,10 @@ type FullProfileSpecificBaseQueryShape = $Shape<
type ActiveTabProfileSpecificBaseQueryShape = $Shape<
$ObjMap<ActiveTabProfileSpecificBaseQuery, $MakeOptional>
>;
type OriginsProfileSpecificBaseQueryShape = $Shape<
$ObjMap<OriginsProfileSpecificBaseQuery, $MakeOptional>
>;

// Query shapes for individual query paths. These are needed for QueryShape union type.
type CallTreeQueryShape = $Shape<$ObjMap<CallTreeQuery, $MakeOptional>>;
type MarkersQueryShape = $Shape<$ObjMap<MarkersQuery, $MakeOptional>>;
Expand Down Expand Up @@ -209,49 +218,83 @@ export function urlStateToUrlObject(urlState: UrlState): UrlObject {

// Start with the query parameters that are shown regardless of the active panel.
let baseQuery;
if (urlState.showTabOnly === null) {
// Add the full profile specific state query here.
baseQuery = ({}: FullProfileSpecificBaseQueryShape);
baseQuery.globalTrackOrder =
urlState.profileSpecific.full.globalTrackOrder.join('-') || undefined;

// Add the parameter hiddenGlobalTracks only when needed.
if (urlState.profileSpecific.full.hiddenGlobalTracks.size > 0) {
baseQuery.hiddenGlobalTracks = [
...urlState.profileSpecific.full.hiddenGlobalTracks,
].join('-');
}
const { timelineTrackOrganization } = urlState;
switch (timelineTrackOrganization.type) {
case 'full': {
// Add the full profile specific state query here.
baseQuery = ({}: FullProfileSpecificBaseQueryShape);
baseQuery.globalTrackOrder =
urlState.profileSpecific.full.globalTrackOrder.join('-') || undefined;

// Add the parameter hiddenGlobalTracks only when needed.
if (urlState.profileSpecific.full.hiddenGlobalTracks.size > 0) {
baseQuery.hiddenGlobalTracks = [
...urlState.profileSpecific.full.hiddenGlobalTracks,
].join('-');
}

let hiddenLocalTracksByPid = '';
for (const [pid, tracks] of urlState.profileSpecific.full
.hiddenLocalTracksByPid) {
if (tracks.size > 0) {
hiddenLocalTracksByPid += [pid, ...tracks].join('-') + '~';
let hiddenLocalTracksByPid = '';
for (const [pid, tracks] of urlState.profileSpecific.full
.hiddenLocalTracksByPid) {
if (tracks.size > 0) {
hiddenLocalTracksByPid += [pid, ...tracks].join('-') + '~';
}
}
if (hiddenLocalTracksByPid.length > 0) {
// Only add to the query string if something was actually hidden.
// Also, slice off the last '~'.
baseQuery.hiddenLocalTracksByPid = hiddenLocalTracksByPid.slice(0, -1);
}
}
if (hiddenLocalTracksByPid.length > 0) {
// Only add to the query string if something was actually hidden.
// Also, slice off the last '~'.
baseQuery.hiddenLocalTracksByPid = hiddenLocalTracksByPid.slice(0, -1);
}

if (urlState.profileSpecific.full.timelineType === 'stack') {
// The default is the category view, so only add it to the URL if it's the
// stack view.
baseQuery.timelineType = 'stack';
}
if (urlState.profileSpecific.full.timelineType === 'stack') {
// The default is the category view, so only add it to the URL if it's the
// stack view.
baseQuery.timelineType = 'stack';
}

let localTrackOrderByPid = '';
for (const [pid, trackOrder] of urlState.profileSpecific.full
.localTrackOrderByPid) {
if (trackOrder.length > 0) {
localTrackOrderByPid += `${String(pid)}-` + trackOrder.join('-') + '~';
let localTrackOrderByPid = '';
for (const [pid, trackOrder] of urlState.profileSpecific.full
.localTrackOrderByPid) {
if (trackOrder.length > 0) {
localTrackOrderByPid +=
`${String(pid)}-` + trackOrder.join('-') + '~';
}
}
baseQuery.localTrackOrderByPid = localTrackOrderByPid || undefined;
break;
}
case 'active-tab': {
baseQuery = ({}: ActiveTabProfileSpecificBaseQueryShape);
break;
}
baseQuery.localTrackOrderByPid = localTrackOrderByPid || undefined;
} else {
// Add the active tab profile specific state query here.
baseQuery = ({}: ActiveTabProfileSpecificBaseQueryShape);
case 'origins':
baseQuery = ({}: OriginsProfileSpecificBaseQueryShape);
break;
default:
throw assertExhaustiveCheck(
timelineTrackOrganization,
`Unhandled GlobalTrack type.`
);
}

let ctxId;
let view;
switch (timelineTrackOrganization.type) {
case 'full':
// Dont URL-encode anything.
break;
case 'active-tab':
view = timelineTrackOrganization.type;
ctxId = timelineTrackOrganization.browsingContextID;
break;
case 'origins':
view = timelineTrackOrganization.type;
break;
default:
throw assertExhaustiveCheck(
timelineTrackOrganization,
'Unhandled TimelineTrackOrganization case'
);
}

baseQuery = ({
Expand All @@ -262,9 +305,10 @@ export function urlStateToUrlObject(urlState: UrlState): UrlObject {
thread: selectedThread === null ? undefined : selectedThread.toString(),
file: urlState.pathInZipFile || undefined,
profiles: urlState.profilesToCompare || undefined,
ctxId,
view,
v: CURRENT_URL_VERSION,
profileName: urlState.profileName || undefined,
showTabOnly1: urlState.showTabOnly || undefined,
}: BaseQueryShape);

// Depending on which panel is active, also show tab-specific query parameters.
Expand Down Expand Up @@ -317,15 +361,28 @@ export function urlStateToUrlObject(urlState: UrlState): UrlObject {
query.networkSearch =
urlState.profileSpecific.networkSearchString || undefined;
break;
case 'js-tracer':
case 'js-tracer': {
query = (baseQuery: JsTracerQueryShape);
if (urlState.showTabOnly === null) {
// `null` adds the parameter to the query, while `undefined` doesn't.
query.summary = urlState.profileSpecific.full.showJsTracerSummary
? null
: undefined;
const { timelineTrackOrganization } = urlState;
switch (timelineTrackOrganization.type) {
case 'full':
case 'origins':
// `null` adds the parameter to the query, while `undefined` doesn't.
query.summary = urlState.profileSpecific.full.showJsTracerSummary
? null
: undefined;
break;
case 'active-tab':
// JS Tracer isn't helpful for web developers.
break;
default:
throw assertExhaustiveCheck(
timelineTrackOrganization,
'Unhandled timelineTrackOrganization case'
);
}
break;
}
default:
throw assertExhaustiveCheck(selectedTab);
}
Expand Down Expand Up @@ -417,9 +474,9 @@ export function stateFromLocation(
: [];
}

let showTabOnly = null;
if (query.showTabOnly1 && Number.isInteger(Number(query.showTabOnly1))) {
showTabOnly = Number(query.showTabOnly1);
let browsingContextId = null;
if (query.ctxId && Number.isInteger(Number(query.ctxId))) {
browsingContextId = Number(query.ctxId);
}

return {
Expand All @@ -430,7 +487,10 @@ export function stateFromLocation(
selectedTab: toValidTabSlug(pathParts[selectedTabPathPart]) || 'calltree',
pathInZipFile: query.file || null,
profileName: query.profileName,
showTabOnly: showTabOnly,
timelineTrackOrganization: validateTimelineTrackOrganization(
query.view,
browsingContextId
),
profileSpecific: {
implementation,
lastSelectedCallTreeSummaryStrategy: toValidCallTreeSummaryStrategy(
Expand Down Expand Up @@ -771,3 +831,29 @@ function getVersion4JSCallNodePathFromStackIndex(
}
return callNodePath;
}

function validateTimelineTrackOrganization(
type: ?string,
browsingContextID: number | null
): TimelineTrackOrganization {
// Pretend this is a TimelineTrackOrganization so that we can exhaustively
// go through each option.
const timelineTrackOrganization: TimelineTrackOrganization = ({ type }: any);
switch (timelineTrackOrganization.type) {
case 'full':
return { type: 'full' };
case 'active-tab':
if (browsingContextID) {
return { type: 'active-tab', browsingContextID };
}
return { type: 'full' };

case 'origins':
return { type: 'origins' };
default:
// Type assert we've checked everythign:
(timelineTrackOrganization: empty);

return { type: 'full' };
}
}
Loading