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
134 changes: 132 additions & 2 deletions src/components/tooltip/Marker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ import {
import { Backtrace } from 'firefox-profiler/components/shared/Backtrace';

import {
formatMarkupFromMarkerSchema,
getSchemaFromMarker,
formatFromMarkerSchema,
} from 'firefox-profiler/profile-logic/marker-schema';
import { computeScreenshotSize } from 'firefox-profiler/profile-logic/marker-data';

Expand All @@ -52,13 +52,15 @@ import type {
PageList,
MarkerSchemaByName,
MarkerIndex,
MarkerFormatType,
InnerWindowID,
Page,
Pid,
Tid,
IndexIntoStackTable,
} from 'firefox-profiler/types';

import type { StringTable } from 'firefox-profiler/utils/string-table';
import type { ConnectedProps } from 'firefox-profiler/utils/connect';
import {
getGCMinorDetails,
Expand Down Expand Up @@ -281,7 +283,7 @@ class MarkerTooltipContents extends React.PureComponent<Props> {
const displayLabel = this.props.showKeys ? key : label || key;
details.push(
<TooltipDetail key={schema.name + '-' + key} label={displayLabel}>
{formatMarkupFromMarkerSchema(
{renderMarkerFieldValue(
schema.name,
format,
value,
Expand Down Expand Up @@ -558,6 +560,134 @@ class MarkerTooltipContents extends React.PureComponent<Props> {
}
}

// This regexp is used to test for URLs and remove their scheme for display.
const URL_SCHEME_REGEXP = /^http(s?):\/\//;

/**
* This function may return structured markup for some types suchs as table,
* list, or urls. For other types this falls back to formatFromMarkerSchema
* above.
*/
export function renderMarkerFieldValue(
markerType: string,
format: MarkerFormatType,
value: any,
stringTable: StringTable,
threadIdToNameMap?: Map<Tid, string>,
processIdToNameMap?: Map<Pid, string>
): React.ReactElement | string {
if (value === undefined || value === null) {
console.warn(`Formatting ${value} for ${JSON.stringify(markerType)}`);
return '(empty)';
}
if (format !== 'url' && typeof format !== 'object' && format !== 'list') {
return formatFromMarkerSchema(
markerType,
format,
value,
stringTable,
threadIdToNameMap,
processIdToNameMap
);
}
if (typeof format === 'object') {
switch (format.type) {
case 'table': {
const { columns } = format;
if (!(value instanceof Array)) {
throw new Error('Expected an array for table type');
}
const hasHeader = columns.some((column) => column.label);
return (
<table className="marker-table-value">
{hasHeader ? (
<thead>
<tr>
{columns.map((col, i) => (
<th key={i}>{col.label || ''}</th>
))}
</tr>
</thead>
) : null}
<tbody>
{value.map((row, i) => {
if (!(row instanceof Array)) {
throw new Error('Expected an array for table row');
}

if (row.length !== columns.length) {
throw new Error(
`Row ${i} length doesn't match column count (row: ${row.length}, cols: ${columns.length})`
);
}
return (
<tr key={i}>
{row.map((cell, i) => {
return (
<td key={i}>
{renderMarkerFieldValue(
markerType,
columns[i].type || 'string',
cell,
stringTable,
threadIdToNameMap,
processIdToNameMap
)}
</td>
);
})}
</tr>
);
})}
</tbody>
</table>
);
}
default:
throw new Error(
`Unknown format type ${JSON.stringify(format as never)}`
);
}
}
switch (format) {
case 'list':
if (!(value instanceof Array)) {
throw new Error('Expected an array for list format');
}
return (
<ul className="marker-list-value">
{value.map((_entry, i) => (
<li key={i}>
{renderMarkerFieldValue(
markerType,
'string',
value[i],
stringTable
)}
</li>
))}
</ul>
);
case 'url': {
if (!URL_SCHEME_REGEXP.test(value)) {
return value;
}
return (
<a
href={value}
target="_blank"
rel="noreferrer"
className="marker-link-value"
>
{value.replace(URL_SCHEME_REGEXP, '')}
</a>
);
}
default:
throw new Error(`Unknown format type ${JSON.stringify(format as never)}`);
}
}

const ConnectedMarkerTooltipContents = explicitConnect<
OwnProps,
StateProps,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
* 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/. */

import * as React from 'react';
import { oneLine } from 'common-tags';
import {
formatNumber,
Expand Down Expand Up @@ -474,8 +473,6 @@ function parseFirstField(

/**
* This function formats a string from a marker type and a value.
* If you wish to get markup instead, have a look at
* formatMarkupFromMarkerSchema below.
*/
export function formatFromMarkerSchema(
markerType: string,
Expand Down Expand Up @@ -592,134 +589,6 @@ export function formatFromMarkerSchema(
}
}

// This regexp is used to test for URLs and remove their scheme for display.
const URL_SCHEME_REGEXP = /^http(s?):\/\//;

/**
* This function may return structured markup for some types suchs as table,
* list, or urls. For other types this falls back to formatFromMarkerSchema
* above.
*/
export function formatMarkupFromMarkerSchema(
markerType: string,
format: MarkerFormatType,
value: any,
stringTable: StringTable,
threadIdToNameMap?: Map<Tid, string>,
processIdToNameMap?: Map<Pid, string>
): React.ReactElement | string {
if (value === undefined || value === null) {
console.warn(`Formatting ${value} for ${JSON.stringify(markerType)}`);
return '(empty)';
}
if (format !== 'url' && typeof format !== 'object' && format !== 'list') {
return formatFromMarkerSchema(
markerType,
format,
value,
stringTable,
threadIdToNameMap,
processIdToNameMap
);
}
if (typeof format === 'object') {
switch (format.type) {
case 'table': {
const { columns } = format;
if (!(value instanceof Array)) {
throw new Error('Expected an array for table type');
}
const hasHeader = columns.some((column) => column.label);
return (
<table className="marker-table-value">
{hasHeader ? (
<thead>
<tr>
{columns.map((col, i) => (
<th key={i}>{col.label || ''}</th>
))}
</tr>
</thead>
) : null}
<tbody>
{value.map((row, i) => {
if (!(row instanceof Array)) {
throw new Error('Expected an array for table row');
}

if (row.length !== columns.length) {
throw new Error(
`Row ${i} length doesn't match column count (row: ${row.length}, cols: ${columns.length})`
);
}
return (
<tr key={i}>
{row.map((cell, i) => {
return (
<td key={i}>
{formatMarkupFromMarkerSchema(
markerType,
columns[i].type || 'string',
cell,
stringTable,
threadIdToNameMap,
processIdToNameMap
)}
</td>
);
})}
</tr>
);
})}
</tbody>
</table>
);
}
default:
throw new Error(
`Unknown format type ${JSON.stringify(format as never)}`
);
}
}
switch (format) {
case 'list':
if (!(value instanceof Array)) {
throw new Error('Expected an array for list format');
}
return (
<ul className="marker-list-value">
{value.map((_entry, i) => (
<li key={i}>
{formatFromMarkerSchema(
markerType,
'string',
value[i],
stringTable
)}
</li>
))}
</ul>
);
case 'url': {
if (!URL_SCHEME_REGEXP.test(value)) {
return value;
}
return (
<a
href={value}
target="_blank"
rel="noreferrer"
className="marker-link-value"
>
{value.replace(URL_SCHEME_REGEXP, '')}
</a>
);
}
default:
throw new Error(`Unknown format type ${JSON.stringify(format as never)}`);
}
}

/**
* Takes a marker and a RegExp and checks if any of its marker
* payload fields match the search regular expression.
Expand Down
4 changes: 2 additions & 2 deletions src/test/unit/marker-schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

import {
formatFromMarkerSchema,
formatMarkupFromMarkerSchema,
parseLabel,
markerSchemaFrontEndOnly,
} from '../../profile-logic/marker-schema';
import { renderMarkerFieldValue } from 'firefox-profiler/components/tooltip/Marker';
import type {
MarkerSchema,
Marker,
Expand Down Expand Up @@ -432,7 +432,7 @@ describe('marker schema formatting', function () {
entries.map(([format, value]: [MarkerFormatType, any]): string[][] => [
format,
value,
formatMarkupFromMarkerSchema('none', format, value, stringTable),
renderMarkerFieldValue('none', format, value, stringTable),
formatFromMarkerSchema('none', format, value, stringTable),
])
).toMatchSnapshot();
Expand Down