[go: nahoru, domu]

blob: 3206f4105ba8161b53cf2f4a561a9e8e8a33f14b [file] [log] [blame]
// Copyright 2014 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.
import * as Common from '../../core/common/common.js';
import * as Host from '../../core/host/host.js';
import * as i18n from '../../core/i18n/i18n.js';
import * as SDK from '../../core/sdk/sdk.js';
import * as DataGrid from '../../ui/legacy/components/data_grid/data_grid.js';
import * as UI from '../../ui/legacy/legacy.js';
import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
import eventSourceMessagesViewStyles from './eventSourceMessagesView.css.js';
const UIStrings = {
/**
*@description Text in Event Source Messages View of the Network panel
*/
id: 'Id',
/**
*@description Text that refers to some types
*/
type: 'Type',
/**
*@description Text in Event Source Messages View of the Network panel
*/
data: 'Data',
/**
*@description Text that refers to the time
*/
time: 'Time',
/**
*@description Data grid name for Event Source data grids
*/
eventSource: 'Event Source',
/**
*@description A context menu item in the Resource Web Socket Frame View of the Network panel
*/
copyMessage: 'Copy message',
/**
*@description Text to clear everything
*/
clearAll: 'Clear all',
/**
*@description Example for placeholder text
*/
enterRegex: 'Enter regex, for example: https?',
};
const str_ = i18n.i18n.registerUIStrings('panels/network/EventSourceMessagesView.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
export class EventSourceMessagesView extends UI.Widget.VBox {
private readonly request: SDK.NetworkRequest.NetworkRequest;
private dataGrid: DataGrid.SortableDataGrid.SortableDataGrid<EventSourceMessageNode>;
private readonly mainToolbar: UI.Toolbar.Toolbar;
private readonly clearAllButton: UI.Toolbar.ToolbarButton;
private readonly filterTextInput: UI.Toolbar.ToolbarInput;
private filterRegex: RegExp|null;
private messageFilterSetting: Common.Settings.Setting<string> =
Common.Settings.Settings.instance().createSetting('networkEventSourceMessageFilter', '');
constructor(request: SDK.NetworkRequest.NetworkRequest) {
super();
this.element.classList.add('event-source-messages-view');
this.element.setAttribute('jslog', `${VisualLogging.pane('event-stream')}`);
this.request = request;
this.mainToolbar = new UI.Toolbar.Toolbar('');
this.clearAllButton = new UI.Toolbar.ToolbarButton(i18nString(UIStrings.clearAll), 'clear');
this.clearAllButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, this.clearMessages, this);
this.mainToolbar.appendToolbarItem(this.clearAllButton);
const placeholder = i18nString(UIStrings.enterRegex);
this.filterTextInput = new UI.Toolbar.ToolbarInput(placeholder, '', 0.4);
this.filterTextInput.addEventListener(UI.Toolbar.ToolbarInput.Event.TextChanged, this.updateFilterSetting, this);
const filter = this.messageFilterSetting.get();
this.filterRegex = null;
this.setFilter(filter);
if (filter) {
this.filterTextInput.setValue(filter);
}
this.mainToolbar.appendToolbarItem(this.filterTextInput);
this.element.appendChild(this.mainToolbar.element);
const columns = ([
{id: 'id', title: i18nString(UIStrings.id), sortable: true, weight: 8},
{id: 'type', title: i18nString(UIStrings.type), sortable: true, weight: 8},
{id: 'data', title: i18nString(UIStrings.data), sortable: false, weight: 88},
{id: 'time', title: i18nString(UIStrings.time), sortable: true, weight: 8},
] as DataGrid.DataGrid.ColumnDescriptor[]);
this.dataGrid = new DataGrid.SortableDataGrid.SortableDataGrid({
displayName: i18nString(UIStrings.eventSource),
columns,
editCallback: undefined,
deleteCallback: undefined,
refreshCallback: undefined,
});
this.dataGrid.setStriped(true);
this.dataGrid.setStickToBottom(true);
this.dataGrid.setRowContextMenuCallback(this.onRowContextMenu.bind(this));
this.dataGrid.markColumnAsSortedBy('time', DataGrid.DataGrid.Order.Ascending);
this.sortItems();
this.dataGrid.addEventListener(DataGrid.DataGrid.Events.SortingChanged, this.sortItems, this);
this.dataGrid.setName('event-source-messages-view');
this.dataGrid.asWidget().show(this.element);
}
override wasShown(): void {
this.refresh();
this.registerCSSFiles([eventSourceMessagesViewStyles]);
this.request.addEventListener(SDK.NetworkRequest.Events.EventSourceMessageAdded, this.messageAdded, this);
}
override willHide(): void {
this.request.removeEventListener(SDK.NetworkRequest.Events.EventSourceMessageAdded, this.messageAdded, this);
}
private messageAdded(event: Common.EventTarget.EventTargetEvent<SDK.NetworkRequest.EventSourceMessage>): void {
const message = event.data;
if (!this.messageFilter(message)) {
return;
}
this.dataGrid.insertChild(new EventSourceMessageNode(message));
}
private messageFilter(message: SDK.NetworkRequest.EventSourceMessage): boolean {
return !this.filterRegex || this.filterRegex.test(message.eventName) || this.filterRegex.test(message.eventId) ||
this.filterRegex.test(message.data);
}
private clearMessages(): void {
clearMessageOffsets.set(this.request, this.request.eventSourceMessages().length);
this.refresh();
}
private updateFilterSetting(): void {
const text = this.filterTextInput.value();
this.messageFilterSetting.set(text);
this.setFilter(text);
this.refresh();
}
private setFilter(text: string): void {
this.filterRegex = null;
if (text) {
try {
this.filterRegex = new RegExp(text, 'i');
} catch (e) {
// this regex will never match any input
this.filterRegex = new RegExp('(?!)', 'i');
}
}
}
private sortItems(): void {
const sortColumnId = this.dataGrid.sortColumnId();
if (!sortColumnId) {
return;
}
const comparator =
(Comparators[sortColumnId] as
((arg0: DataGrid.SortableDataGrid.SortableDataGridNode<EventSourceMessageNode>,
arg1: DataGrid.SortableDataGrid.SortableDataGridNode<EventSourceMessageNode>) => number) |
undefined);
if (!comparator) {
return;
}
this.dataGrid.sortNodes(comparator, !this.dataGrid.isSortOrderAscending());
}
private onRowContextMenu(
contextMenu: UI.ContextMenu.ContextMenu,
node: DataGrid.DataGrid.DataGridNode<DataGrid.ViewportDataGrid.ViewportDataGridNode<
DataGrid.SortableDataGrid.SortableDataGridNode<EventSourceMessageNode>>>): void {
contextMenu.clipboardSection().appendItem(
i18nString(UIStrings.copyMessage),
Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText.bind(
Host.InspectorFrontendHost.InspectorFrontendHostInstance, node.data.data));
}
refresh(): void {
this.dataGrid.rootNode().removeChildren();
let messages = this.request.eventSourceMessages();
const offset = clearMessageOffsets.get(this.request) || 0;
messages = messages.slice(offset);
messages = messages.filter(this.messageFilter.bind(this));
messages.forEach(message => this.dataGrid.insertChild(new EventSourceMessageNode(message)));
}
}
export class EventSourceMessageNode extends DataGrid.SortableDataGrid.SortableDataGridNode<EventSourceMessageNode> {
readonly message: SDK.NetworkRequest.EventSourceMessage;
constructor(message: SDK.NetworkRequest.EventSourceMessage) {
const time = new Date(message.time * 1000);
const timeText = ('0' + time.getHours()).substr(-2) + ':' + ('0' + time.getMinutes()).substr(-2) + ':' +
('0' + time.getSeconds()).substr(-2) + '.' + ('00' + time.getMilliseconds()).substr(-3);
const timeNode = document.createElement('div');
UI.UIUtils.createTextChild(timeNode, timeText);
UI.Tooltip.Tooltip.install(timeNode, time.toLocaleString());
super({id: message.eventId, type: message.eventName, data: message.data, time: timeNode});
this.message = message;
}
}
// TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration)
// eslint-disable-next-line @typescript-eslint/naming-convention
export function EventSourceMessageNodeComparator(
fieldGetter: (arg0: SDK.NetworkRequest.EventSourceMessage) => (number | string), a: EventSourceMessageNode,
b: EventSourceMessageNode): number {
const aValue = fieldGetter(a.message);
const bValue = fieldGetter(b.message);
return aValue < bValue ? -1 : aValue > bValue ? 1 : 0;
}
export const Comparators: {
[x: string]: (arg0: EventSourceMessageNode, arg1: EventSourceMessageNode) => number,
} = {
'id': EventSourceMessageNodeComparator.bind(null, message => message.eventId),
'type': EventSourceMessageNodeComparator.bind(null, message => message.eventName),
'time': EventSourceMessageNodeComparator.bind(null, message => message.time),
};
const clearMessageOffsets = new WeakMap<SDK.NetworkRequest.NetworkRequest, number>();