chrome-devtools-frontend
Version:
Chrome DevTools UI
170 lines (149 loc) • 5.77 kB
text/typescript
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/* eslint-disable rulesdir/no-imperative-dom-api */
import * as i18n from '../../core/i18n/i18n.js';
import * as DataGrid from '../../ui/legacy/components/data_grid/data_grid.js';
import * as SourceFrame from '../../ui/legacy/components/source_frame/source_frame.js';
import * as UI from '../../ui/legacy/legacy.js';
import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
import eventDisplayTableStyles from './eventDisplayTable.css.js';
import type {PlayerEvent} from './MediaModel.js';
const UIStrings = {
/**
*@description Text for timestamps of items
*/
timestamp: 'Timestamp',
/**
*@description The column header for event names.
*/
eventName: 'Event name',
/**
*@description Text for the value of something
*/
value: 'Value',
/**
*@description The accessible name of a table that displays information about events that occurred
* while a video/media player was present on the page.
*/
eventDisplay: 'Event display',
} as const;
const str_ = i18n.i18n.registerUIStrings('panels/media/EventDisplayTable.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
export interface EventDisplayColumnConfig {
id: string;
title: string;
sortable: boolean;
weight?: number;
}
export const enum MediaEventColumnKeys {
TIMESTAMP = 'display-timestamp',
EVENT = 'event',
VALUE = 'value',
}
export class EventNode extends DataGrid.DataGrid.DataGridNode<EventNode> {
private expandableElement: SourceFrame.JSONView.JSONView|null;
constructor(event: PlayerEvent) {
super(event, false);
this.expandableElement = null;
}
override createCell(columnId: string): HTMLElement {
const cell = this.createTD(columnId);
const cellData = this.data[columnId] as string;
if (columnId === MediaEventColumnKeys.VALUE) {
const enclosed = cell.createChild('div', 'event-display-table-contents-json-wrapper');
this.expandableElement =
new SourceFrame.JSONView.JSONView(new SourceFrame.JSONView.ParsedJSON(cellData, '', ''), true);
this.expandableElement.markAsRoot();
this.expandableElement.show(enclosed);
} else {
cell.classList.add('event-display-table-basic-text-table-entry');
UI.UIUtils.createTextChild(cell, cellData);
}
return cell;
}
}
export class PlayerEventsView extends UI.Widget.VBox {
private readonly dataGrid: DataGrid.DataGrid.DataGridImpl<EventNode>;
private firstEventTime: number;
constructor() {
super();
this.registerRequiredCSS(eventDisplayTableStyles);
this.element.setAttribute('jslog', `${VisualLogging.pane('events')}`);
// Set up element styles.
this.contentElement.classList.add('event-display-table-contents-table-container');
this.dataGrid = this.createDataGrid([
{
id: MediaEventColumnKeys.TIMESTAMP,
title: i18nString(UIStrings.timestamp),
weight: 1,
sortable: false,
},
{id: MediaEventColumnKeys.EVENT, title: i18nString(UIStrings.eventName), weight: 2, sortable: false},
{
id: MediaEventColumnKeys.VALUE,
title: i18nString(UIStrings.value),
weight: 7,
sortable: false,
},
]);
this.firstEventTime = 0;
this.dataGrid.setStriped(true);
this.dataGrid.asWidget().show(this.contentElement);
}
private createDataGrid(headers: EventDisplayColumnConfig[]): DataGrid.DataGrid.DataGridImpl<EventNode> {
const gridColumnDescs = [];
for (const headerDesc of headers) {
gridColumnDescs.push(PlayerEventsView.convertToGridDescriptor(headerDesc));
}
// TODO(tmathmeyer) SortableDataGrid doesn't play nice with nested JSON
// renderers, since they can change size, and this breaks the visible
// element computation in ViewportDataGrid.
const datagrid = new DataGrid.DataGrid.DataGridImpl({
displayName: i18nString(UIStrings.eventDisplay),
columns: gridColumnDescs,
deleteCallback: undefined,
refreshCallback: undefined,
});
datagrid.asWidget().contentElement.classList.add('no-border-top-datagrid');
return datagrid;
}
onEvent(event: PlayerEvent): void {
if (this.firstEventTime === 0 && typeof event.timestamp === 'number') {
this.firstEventTime = event.timestamp;
}
event = this.subtractFirstEventTime(event);
const stringified = event.value;
try {
const json = JSON.parse(stringified);
event.event = json.event;
delete json['event'];
event.value = json;
const node = new EventNode(event);
const scroll = this.dataGrid.scrollContainer;
const isAtBottom = scroll.scrollTop === (scroll.scrollHeight - scroll.offsetHeight);
this.dataGrid.rootNode().appendChild(node as DataGrid.DataGrid.DataGridNode<EventNode>);
if (isAtBottom) {
scroll.scrollTop = scroll.scrollHeight;
}
} catch {
// If this is a legacy message event, ignore it for now until they
// are handled.
}
}
private subtractFirstEventTime(event: PlayerEvent): PlayerEvent {
if (typeof event.timestamp === 'number') {
event.displayTimestamp = (event.timestamp - this.firstEventTime).toFixed(3);
}
return event;
}
private static convertToGridDescriptor(columnConfig: EventDisplayColumnConfig): DataGrid.DataGrid.ColumnDescriptor {
return {
id: columnConfig.id,
title: columnConfig.title,
sortable: columnConfig.sortable,
weight: columnConfig.weight || 0,
sort: DataGrid.DataGrid.Order.Ascending,
} as DataGrid.DataGrid.ColumnDescriptor;
}
}