UNPKG

@dcloudio/uni-debugger

Version:

uni-app debugger

286 lines (257 loc) 9.8 kB
// Copyright 2018 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. ProtocolMonitor.ProtocolMonitor = class extends UI.VBox { constructor() { super(true); this._nodes = []; this._recordingListeners = null; this._started = false; this._startTime = 0; this._nodeForId = {}; this._filter = node => true; this._columns = [ {id: 'method', title: ls`Method`, visible: true, sortable: true, weight: 60}, {id: 'direction', title: ls`Direction`, visible: false, sortable: true, hideable: true, weight: 30}, {id: 'request', title: ls`Request`, visible: true, hideable: true, weight: 60}, {id: 'response', title: ls`Response`, visible: true, hideable: true, weight: 60}, {id: 'timestamp', title: ls`Timestamp`, visible: false, sortable: true, hideable: true, weight: 30} ]; this.registerRequiredCSS('protocol_monitor/protocolMonitor.css'); const topToolbar = new UI.Toolbar('protocol-monitor-toolbar', this.contentElement); const recordButton = new UI.ToolbarToggle(ls`Record`, 'largeicon-start-recording', 'largeicon-stop-recording'); recordButton.addEventListener(UI.ToolbarButton.Events.Click, () => { recordButton.setToggled(!recordButton.toggled()); this._setRecording(recordButton.toggled()); }); recordButton.setToggleWithRedColor(true); topToolbar.appendToolbarItem(recordButton); recordButton.setToggled(true); const clearButton = new UI.ToolbarButton(ls`Clear all`, 'largeicon-clear'); clearButton.addEventListener(UI.ToolbarButton.Events.Click, () => { this._dataGrid.rootNode().removeChildren(); this._nodes = []; this._nodeForId = {}; }); topToolbar.appendToolbarItem(clearButton); const split = new UI.SplitWidget(true, true, 'protocol-monitor-panel-split', 250); split.show(this.contentElement); this._dataGrid = new DataGrid.SortableDataGrid(this._columns); this._dataGrid.element.style.flex = '1'; this._infoWidget = new ProtocolMonitor.ProtocolMonitor.InfoWidget(); split.setMainWidget(this._dataGrid.asWidget()); split.setSidebarWidget(this._infoWidget); this._dataGrid.addEventListener( DataGrid.DataGrid.Events.SelectedNode, event => this._infoWidget.render(event.data.data)); this._dataGrid.addEventListener(DataGrid.DataGrid.Events.DeselectedNode, event => this._infoWidget.render(null)); this._dataGrid.setHeaderContextMenuCallback(this._innerHeaderContextMenu.bind(this)); this._dataGrid.addEventListener(DataGrid.DataGrid.Events.SortingChanged, this._sortDataGrid.bind(this)); this._dataGrid.setStickToBottom(true); this._dataGrid.sortNodes(DataGrid.SortableDataGrid.NumericComparator.bind(null, 'timestamp'), false); this._updateColumnVisibility(); const keys = ['method', 'request', 'response', 'direction']; this._filterParser = new TextUtils.FilterParser(keys); this._suggestionBuilder = new UI.FilterSuggestionBuilder(keys); this._textFilterUI = new UI.ToolbarInput(ls`Filter`, 1, .2, '', this._suggestionBuilder.completions.bind(this._suggestionBuilder)); this._textFilterUI.addEventListener(UI.ToolbarInput.Event.TextChanged, event => { const query = /** @type {string} */ (event.data); const filters = this._filterParser.parse(query); this._filter = node => { for (const {key, text, negative} of filters) { if (!(key in node.data) || !text) continue; const found = JSON.stringify(node.data[key]).indexOf(text) !== -1; if (found === negative) return false; } return true; }; this._filterNodes(); }); topToolbar.appendToolbarItem(this._textFilterUI); } _filterNodes() { for (const node of this._nodes) { if (this._filter(node)) { if (!node.parent) this._dataGrid.insertChild(node); } else { node.remove(); } } } /** * @param {!UI.ContextMenu} contextMenu */ _innerHeaderContextMenu(contextMenu) { const columnConfigs = this._columns.filter(columnConfig => columnConfig.hideable); for (const columnConfig of columnConfigs) { contextMenu.headerSection().appendCheckboxItem( columnConfig.title, this._toggleColumnVisibility.bind(this, columnConfig), columnConfig.visible); } contextMenu.show(); } /** * @param {!Object} columnConfig */ _toggleColumnVisibility(columnConfig) { columnConfig.visible = !columnConfig.visible; this._updateColumnVisibility(); } _updateColumnVisibility() { const visibleColumns = /** @type {!Object.<string, boolean>} */ ({}); for (const columnConfig of this._columns) visibleColumns[columnConfig.id] = columnConfig.visible; this._dataGrid.setColumnsVisiblity(visibleColumns); } _sortDataGrid() { const sortColumnId = this._dataGrid.sortColumnId(); if (!sortColumnId) return; let columnIsNumeric = true; switch (sortColumnId) { case 'method': case 'direction': columnIsNumeric = false; break; } const comparator = columnIsNumeric ? DataGrid.SortableDataGrid.NumericComparator : DataGrid.SortableDataGrid.StringComparator; this._dataGrid.sortNodes(comparator.bind(null, sortColumnId), !this._dataGrid.isSortOrderAscending()); } /** * @override */ wasShown() { if (this._started) return; this._started = true; this._startTime = Date.now(); this._setRecording(true); } /** * @param {boolean} recording */ _setRecording(recording) { if (this._recordingListeners) { Common.EventTarget.removeEventListeners(this._recordingListeners); this._recordingListeners = null; } if (recording) { this._recordingListeners = [ SDK.targetManager.mainTarget().addEventListener( Protocol.TargetBase.Events.MessageReceived, this._messageRecieved, this), SDK.targetManager.mainTarget().addEventListener(Protocol.TargetBase.Events.MessageSent, this._messageSent, this) ]; } } _messageRecieved(event) { const message = event.data.message; if ('id' in message) { const node = this._nodeForId[message.id]; if (!node) return; node.data.response = message.result; node.refresh(); if (this._dataGrid.selectedNode === node) this._infoWidget.render(node.data); return; } const node = new ProtocolMonitor.ProtocolMonitor.ProtocolNode({ method: message.method, direction: 'recieved', response: message.params, timestamp: Date.now() - this._startTime, request: '' }); this._nodes.push(node); if (this._filter(node)) this._dataGrid.insertChild(node); } _messageSent(event) { const message = event.data; const node = new ProtocolMonitor.ProtocolMonitor.ProtocolNode({ method: message.method, direction: 'sent', request: message.params, timestamp: Date.now() - this._startTime, response: '(pending)', id: message.id }); this._nodeForId[message.id] = node; this._nodes.push(node); if (this._filter(node)) this._dataGrid.insertChild(node); } }; ProtocolMonitor.ProtocolMonitor.ProtocolNode = class extends DataGrid.SortableDataGridNode { constructor(data) { super(data); } /** * @override * @param {string} columnId * @return {!Element} */ createCell(columnId) { switch (columnId) { case 'response': if (!this.data[columnId] && this.data.direction === 'send') { const cell = this.createTD(columnId); cell.textContent = '(pending)'; return cell; } // fall through case 'request': { const cell = this.createTD(columnId); const obj = SDK.RemoteObject.fromLocalObject(this.data[columnId]); cell.textContent = obj.description.trimEnd(50); cell.classList.add('source-code'); return cell; } case 'timestamp': { const cell = this.createTD(columnId); cell.textContent = ls`${this.data[columnId]} ms`; return cell; } } return super.createCell(columnId); } /** * @override */ element() { const element = super.element(); element.classList.toggle('protocol-message-sent', this.data.direction === 'sent'); element.classList.toggle('protocol-message-recieved', this.data.direction !== 'sent'); return element; } }; ProtocolMonitor.ProtocolMonitor.InfoWidget = class extends UI.VBox { constructor() { super(); this._tabbedPane = new UI.TabbedPane(); this._tabbedPane.appendTab('request', 'Request', new UI.Widget()); this._tabbedPane.appendTab('response', 'Response', new UI.Widget()); this._tabbedPane.show(this.contentElement); this._tabbedPane.selectTab('response'); this.render(null); } /** * @param {?{method: string, direction: string, request: ?Object, response: ?Object, timestamp: number}} data */ render(data) { const requestEnabled = data && data.direction === 'sent'; this._tabbedPane.setTabEnabled('request', !!requestEnabled); if (!data) { this._tabbedPane.changeTabView('request', new UI.EmptyWidget(ls`No message selected`)); this._tabbedPane.changeTabView('response', new UI.EmptyWidget(ls`No message selected`)); return; } if (!requestEnabled) this._tabbedPane.selectTab('response'); this._tabbedPane.changeTabView('request', SourceFrame.JSONView.createViewSync(data.request)); this._tabbedPane.changeTabView('response', SourceFrame.JSONView.createViewSync(data.response)); } };