| // Copyright 2016 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. | |
| /** | |
| * @unrestricted | |
| */ | |
| export default class NetworkLogViewColumns { | |
| /** | |
| * @param {!Network.NetworkLogView} networkLogView | |
| * @param {!Network.NetworkTransferTimeCalculator} timeCalculator | |
| * @param {!Network.NetworkTransferDurationCalculator} durationCalculator | |
| * @param {!Common.Setting} networkLogLargeRowsSetting | |
| */ | |
| constructor(networkLogView, timeCalculator, durationCalculator, networkLogLargeRowsSetting) { | |
| this._networkLogView = networkLogView; | |
| /** @type {!Common.Setting} */ | |
| this._persistantSettings = Common.settings.createSetting('networkLogColumns', {}); | |
| this._networkLogLargeRowsSetting = networkLogLargeRowsSetting; | |
| this._networkLogLargeRowsSetting.addChangeListener(this._updateRowsSize, this); | |
| /** @type {!Map<string, !Array<number>>} */ | |
| this._eventDividers = new Map(); | |
| this._eventDividersShown = false; | |
| this._gridMode = true; | |
| /** @type {!Array.<!Network.NetworkLogViewColumns.Descriptor>} */ | |
| this._columns = []; | |
| this._waterfallRequestsAreStale = false; | |
| this._waterfallScrollerWidthIsStale = true; | |
| /** @type {!Components.Linkifier} */ | |
| this._popupLinkifier = new Components.Linkifier(); | |
| /** @type {!Map<string, !Network.NetworkTimeCalculator>} */ | |
| this._calculatorsMap = new Map(); | |
| this._calculatorsMap.set(_calculatorTypes.Time, timeCalculator); | |
| this._calculatorsMap.set(_calculatorTypes.Duration, durationCalculator); | |
| this._lastWheelTime = 0; | |
| this._setupDataGrid(); | |
| this._setupWaterfall(); | |
| } | |
| /** | |
| * @param {!Network.NetworkLogViewColumns.Descriptor} columnConfig | |
| * @return {!DataGrid.DataGrid.ColumnDescriptor} | |
| */ | |
| static _convertToDataGridDescriptor(columnConfig) { | |
| return /** @type {!DataGrid.DataGrid.ColumnDescriptor} */ ({ | |
| id: columnConfig.id, | |
| title: columnConfig.title, | |
| sortable: columnConfig.sortable, | |
| align: columnConfig.align, | |
| nonSelectable: columnConfig.nonSelectable, | |
| weight: columnConfig.weight, | |
| allowInSortByEvenWhenHidden: columnConfig.allowInSortByEvenWhenHidden, | |
| }); | |
| } | |
| wasShown() { | |
| this._updateRowsSize(); | |
| } | |
| willHide() { | |
| this._popoverHelper.hidePopover(); | |
| } | |
| reset() { | |
| if (this._popoverHelper) { | |
| this._popoverHelper.hidePopover(); | |
| } | |
| this._eventDividers.clear(); | |
| } | |
| _setupDataGrid() { | |
| const defaultColumns = _defaultColumns; | |
| const defaultColumnConfig = _defaultColumnConfig; | |
| this._columns = /** @type {!Array<!Network.NetworkLogViewColumns.Descriptor>} */ ([]); | |
| for (const currentConfigColumn of defaultColumns) { | |
| const columnConfig = /** @type {!Network.NetworkLogViewColumns.Descriptor} */ ( | |
| Object.assign({}, defaultColumnConfig, currentConfigColumn)); | |
| columnConfig.id = columnConfig.id; | |
| if (columnConfig.subtitle) { | |
| columnConfig.titleDOMFragment = this._makeHeaderFragment(columnConfig.title, columnConfig.subtitle); | |
| } | |
| this._columns.push(columnConfig); | |
| } | |
| this._loadCustomColumnsAndSettings(); | |
| this._popoverHelper = new UI.PopoverHelper(this._networkLogView.element, this._getPopoverRequest.bind(this)); | |
| this._popoverHelper.setHasPadding(true); | |
| this._popoverHelper.setTimeout(300, 300); | |
| /** @type {!DataGrid.SortableDataGrid<!Network.NetworkNode>} */ | |
| this._dataGrid = | |
| new DataGrid.SortableDataGrid(this._columns.map(NetworkLogViewColumns._convertToDataGridDescriptor)); | |
| this._dataGrid.element.addEventListener('mousedown', event => { | |
| if (!this._dataGrid.selectedNode && event.button) { | |
| event.consume(); | |
| } | |
| }, true); | |
| this._dataGridScroller = this._dataGrid.scrollContainer; | |
| this._updateColumns(); | |
| this._dataGrid.addEventListener(DataGrid.DataGrid.Events.SortingChanged, this._sortHandler, this); | |
| this._dataGrid.setHeaderContextMenuCallback(this._innerHeaderContextMenu.bind(this)); | |
| this._activeWaterfallSortId = WaterfallSortIds.StartTime; | |
| this._dataGrid.markColumnAsSortedBy(_initialSortColumn, DataGrid.DataGrid.Order.Ascending); | |
| this._splitWidget = new UI.SplitWidget(true, true, 'networkPanelSplitViewWaterfall', 200); | |
| const widget = this._dataGrid.asWidget(); | |
| widget.setMinimumSize(150, 0); | |
| this._splitWidget.setMainWidget(widget); | |
| } | |
| _setupWaterfall() { | |
| this._waterfallColumn = new Network.NetworkWaterfallColumn(this._networkLogView.calculator()); | |
| this._waterfallColumn.element.addEventListener('contextmenu', handleContextMenu.bind(this)); | |
| this._waterfallColumn.element.addEventListener('mousewheel', this._onMouseWheel.bind(this, false), {passive: true}); | |
| this._dataGridScroller.addEventListener('mousewheel', this._onMouseWheel.bind(this, true), true); | |
| this._waterfallScroller = this._waterfallColumn.contentElement.createChild('div', 'network-waterfall-v-scroll'); | |
| this._waterfallScrollerContent = this._waterfallScroller.createChild('div', 'network-waterfall-v-scroll-content'); | |
| this._dataGrid.addEventListener(DataGrid.DataGrid.Events.PaddingChanged, () => { | |
| this._waterfallScrollerWidthIsStale = true; | |
| this._syncScrollers(); | |
| }); | |
| this._dataGrid.addEventListener( | |
| DataGrid.ViewportDataGrid.Events.ViewportCalculated, this._redrawWaterfallColumn.bind(this)); | |
| this._createWaterfallHeader(); | |
| this._waterfallColumn.contentElement.classList.add('network-waterfall-view'); | |
| this._waterfallColumn.setMinimumSize(100, 0); | |
| this._splitWidget.setSidebarWidget(this._waterfallColumn); | |
| this.switchViewMode(false); | |
| /** | |
| * @param {!Event} event | |
| * @this {NetworkLogViewColumns} | |
| */ | |
| function handleContextMenu(event) { | |
| const node = this._waterfallColumn.getNodeFromPoint(event.offsetX, event.offsetY); | |
| if (!node) { | |
| return; | |
| } | |
| const request = node.request(); | |
| if (!request) { | |
| return; | |
| } | |
| const contextMenu = new UI.ContextMenu(event); | |
| this._networkLogView.handleContextMenuForRequest(contextMenu, request); | |
| contextMenu.show(); | |
| } | |
| } | |
| /** | |
| * @param {boolean} shouldConsume | |
| * @param {!Event} event | |
| */ | |
| _onMouseWheel(shouldConsume, event) { | |
| if (shouldConsume) { | |
| event.consume(true); | |
| } | |
| const hasRecentWheel = Date.now() - this._lastWheelTime < 80; | |
| this._activeScroller.scrollBy({top: -event.wheelDeltaY, behavior: hasRecentWheel ? 'instant' : 'smooth'}); | |
| this._syncScrollers(); | |
| this._lastWheelTime = Date.now(); | |
| } | |
| _syncScrollers() { | |
| if (!this._waterfallColumn.isShowing()) { | |
| return; | |
| } | |
| this._waterfallScrollerContent.style.height = this._dataGridScroller.scrollHeight + 'px'; | |
| this._updateScrollerWidthIfNeeded(); | |
| this._dataGridScroller.scrollTop = this._waterfallScroller.scrollTop; | |
| } | |
| _updateScrollerWidthIfNeeded() { | |
| if (this._waterfallScrollerWidthIsStale) { | |
| this._waterfallScrollerWidthIsStale = false; | |
| this._waterfallColumn.setRightPadding( | |
| this._waterfallScroller.offsetWidth - this._waterfallScrollerContent.offsetWidth); | |
| } | |
| } | |
| _redrawWaterfallColumn() { | |
| if (!this._waterfallRequestsAreStale) { | |
| this._updateScrollerWidthIfNeeded(); | |
| this._waterfallColumn.update( | |
| this._activeScroller.scrollTop, this._eventDividersShown ? this._eventDividers : undefined); | |
| return; | |
| } | |
| this._syncScrollers(); | |
| const nodes = this._networkLogView.flatNodesList(); | |
| this._waterfallColumn.update(this._activeScroller.scrollTop, this._eventDividers, nodes); | |
| } | |
| _createWaterfallHeader() { | |
| this._waterfallHeaderElement = this._waterfallColumn.contentElement.createChild('div', 'network-waterfall-header'); | |
| this._waterfallHeaderElement.addEventListener('click', waterfallHeaderClicked.bind(this)); | |
| this._waterfallHeaderElement.addEventListener( | |
| 'contextmenu', event => this._innerHeaderContextMenu(new UI.ContextMenu(event))); | |
| const innerElement = this._waterfallHeaderElement.createChild('div'); | |
| innerElement.textContent = Common.UIString('Waterfall'); | |
| this._waterfallColumnSortIcon = UI.Icon.create('', 'sort-order-icon'); | |
| this._waterfallHeaderElement.createChild('div', 'sort-order-icon-container') | |
| .appendChild(this._waterfallColumnSortIcon); | |
| /** | |
| * @this {NetworkLogViewColumns} | |
| */ | |
| function waterfallHeaderClicked() { | |
| const sortOrders = DataGrid.DataGrid.Order; | |
| const wasSortedByWaterfall = this._dataGrid.sortColumnId() === 'waterfall'; | |
| const wasSortedAscending = this._dataGrid.isSortOrderAscending(); | |
| const sortOrder = wasSortedByWaterfall && wasSortedAscending ? sortOrders.Descending : sortOrders.Ascending; | |
| this._dataGrid.markColumnAsSortedBy('waterfall', sortOrder); | |
| this._sortHandler(); | |
| } | |
| } | |
| /** | |
| * @param {!Network.NetworkTimeCalculator} x | |
| */ | |
| setCalculator(x) { | |
| this._waterfallColumn.setCalculator(x); | |
| } | |
| scheduleRefresh() { | |
| this._waterfallColumn.scheduleDraw(); | |
| } | |
| _updateRowsSize() { | |
| const largeRows = !!this._networkLogLargeRowsSetting.get(); | |
| this._dataGrid.element.classList.toggle('small', !largeRows); | |
| this._dataGrid.scheduleUpdate(); | |
| this._waterfallScrollerWidthIsStale = true; | |
| this._waterfallColumn.setRowHeight(largeRows ? 41 : 21); | |
| this._waterfallScroller.classList.toggle('small', !largeRows); | |
| this._waterfallHeaderElement.classList.toggle('small', !largeRows); | |
| this._waterfallColumn.setHeaderHeight(this._waterfallScroller.offsetTop); | |
| } | |
| /** | |
| * @param {!Element} element | |
| */ | |
| show(element) { | |
| this._splitWidget.show(element); | |
| } | |
| /** | |
| * @param {boolean} value | |
| */ | |
| setHidden(value) { | |
| UI.ARIAUtils.setHidden(this._splitWidget.element, value); | |
| } | |
| /** | |
| * @return {!DataGrid.SortableDataGrid<!Network.NetworkNode>} dataGrid | |
| */ | |
| dataGrid() { | |
| return this._dataGrid; | |
| } | |
| sortByCurrentColumn() { | |
| this._sortHandler(); | |
| } | |
| _sortHandler() { | |
| const columnId = this._dataGrid.sortColumnId(); | |
| this._networkLogView.removeAllNodeHighlights(); | |
| this._waterfallRequestsAreStale = true; | |
| if (columnId === 'waterfall') { | |
| if (this._dataGrid.sortOrder() === DataGrid.DataGrid.Order.Ascending) { | |
| this._waterfallColumnSortIcon.setIconType('smallicon-triangle-up'); | |
| } else { | |
| this._waterfallColumnSortIcon.setIconType('smallicon-triangle-down'); | |
| } | |
| const sortFunction = Network.NetworkRequestNode.RequestPropertyComparator.bind(null, this._activeWaterfallSortId); | |
| this._dataGrid.sortNodes(sortFunction, !this._dataGrid.isSortOrderAscending()); | |
| this._dataGridSortedForTest(); | |
| return; | |
| } | |
| this._waterfallColumnSortIcon.setIconType(''); | |
| const columnConfig = this._columns.find(columnConfig => columnConfig.id === columnId); | |
| if (!columnConfig || !columnConfig.sortingFunction) { | |
| return; | |
| } | |
| this._dataGrid.sortNodes(columnConfig.sortingFunction, !this._dataGrid.isSortOrderAscending()); | |
| this._dataGridSortedForTest(); | |
| } | |
| _dataGridSortedForTest() { | |
| } | |
| _updateColumns() { | |
| if (!this._dataGrid) { | |
| return; | |
| } | |
| const visibleColumns = /** @type {!Object.<string, boolean>} */ ({}); | |
| if (this._gridMode) { | |
| for (const columnConfig of this._columns) { | |
| visibleColumns[columnConfig.id] = columnConfig.visible; | |
| } | |
| } else { | |
| // Find the first visible column from the path group | |
| const visibleColumn = this._columns.find(c => c.hideableGroup === 'path' && c.visible); | |
| if (visibleColumn) { | |
| visibleColumns[visibleColumn.id] = true; | |
| } else { | |
| // This should not happen because inside a hideableGroup | |
| // there should always be at least one column visible | |
| // This is just in case. | |
| visibleColumns.name = true; | |
| } | |
| } | |
| this._dataGrid.setColumnsVisiblity(visibleColumns); | |
| } | |
| /** | |
| * @param {boolean} gridMode | |
| */ | |
| switchViewMode(gridMode) { | |
| if (this._gridMode === gridMode) { | |
| return; | |
| } | |
| this._gridMode = gridMode; | |
| if (gridMode) { | |
| this._splitWidget.showBoth(); | |
| this._activeScroller = this._waterfallScroller; | |
| this._waterfallScroller.scrollTop = this._dataGridScroller.scrollTop; | |
| this._dataGrid.setScrollContainer(this._waterfallScroller); | |
| } else { | |
| this._networkLogView.removeAllNodeHighlights(); | |
| this._splitWidget.hideSidebar(); | |
| this._activeScroller = this._dataGridScroller; | |
| this._dataGrid.setScrollContainer(this._dataGridScroller); | |
| } | |
| this._networkLogView.element.classList.toggle('brief-mode', !gridMode); | |
| this._updateColumns(); | |
| this._updateRowsSize(); | |
| } | |
| /** | |
| * @param {!Network.NetworkLogViewColumns.Descriptor} columnConfig | |
| */ | |
| _toggleColumnVisibility(columnConfig) { | |
| this._loadCustomColumnsAndSettings(); | |
| columnConfig.visible = !columnConfig.visible; | |
| this._saveColumnsSettings(); | |
| this._updateColumns(); | |
| } | |
| _saveColumnsSettings() { | |
| const saveableSettings = {}; | |
| for (const columnConfig of this._columns) { | |
| saveableSettings[columnConfig.id] = {visible: columnConfig.visible, title: columnConfig.title}; | |
| } | |
| this._persistantSettings.set(saveableSettings); | |
| } | |
| _loadCustomColumnsAndSettings() { | |
| const savedSettings = this._persistantSettings.get(); | |
| const columnIds = Object.keys(savedSettings); | |
| for (const columnId of columnIds) { | |
| const setting = savedSettings[columnId]; | |
| let columnConfig = this._columns.find(columnConfig => columnConfig.id === columnId); | |
| if (!columnConfig) { | |
| columnConfig = this._addCustomHeader(setting.title, columnId); | |
| } | |
| if (columnConfig.hideable && typeof setting.visible === 'boolean') { | |
| columnConfig.visible = !!setting.visible; | |
| } | |
| if (typeof setting.title === 'string') { | |
| columnConfig.title = setting.title; | |
| } | |
| } | |
| } | |
| /** | |
| * @param {string} title | |
| * @param {string} subtitle | |
| * @return {!DocumentFragment} | |
| */ | |
| _makeHeaderFragment(title, subtitle) { | |
| const fragment = createDocumentFragment(); | |
| fragment.createTextChild(title); | |
| const subtitleDiv = fragment.createChild('div', 'network-header-subtitle'); | |
| subtitleDiv.createTextChild(subtitle); | |
| return fragment; | |
| } | |
| /** | |
| * @param {!UI.ContextSubMenu} contextMenu | |
| */ | |
| _innerHeaderContextMenu(contextMenu) { | |
| const columnConfigs = this._columns.filter(columnConfig => columnConfig.hideable); | |
| const nonResponseHeaders = columnConfigs.filter(columnConfig => !columnConfig.isResponseHeader); | |
| /** @type {!Map<string, !Array<!Network.NetworkLogViewColumns.Descriptor>>} */ | |
| const hideableGroups = new Map(); | |
| /** @type {!Array.<!Network.NetworkLogViewColumns.Descriptor>} */ | |
| const nonResponseHeadersWithoutGroup = []; | |
| // Sort columns into their groups | |
| for (const columnConfig of nonResponseHeaders) { | |
| if (!columnConfig.hideableGroup) { | |
| nonResponseHeadersWithoutGroup.push(columnConfig); | |
| } else { | |
| const name = columnConfig.hideableGroup; | |
| if (!hideableGroups.has(name)) { | |
| hideableGroups.set(name, []); | |
| } | |
| hideableGroups.get(name).push(columnConfig); | |
| } | |
| } | |
| // Add all the groups first | |
| for (const group of hideableGroups.values()) { | |
| const visibleColumns = group.filter(columnConfig => columnConfig.visible); | |
| for (const columnConfig of group) { | |
| // Make sure that at least one item in every group is enabled | |
| const isDisabled = visibleColumns.length === 1 && visibleColumns[0] === columnConfig; | |
| contextMenu.headerSection().appendCheckboxItem( | |
| columnConfig.title, this._toggleColumnVisibility.bind(this, columnConfig), columnConfig.visible, | |
| isDisabled); | |
| } | |
| contextMenu.headerSection().appendSeparator(); | |
| } | |
| // Add normal columns not belonging to any group | |
| for (const columnConfig of nonResponseHeadersWithoutGroup) { | |
| contextMenu.headerSection().appendCheckboxItem( | |
| columnConfig.title, this._toggleColumnVisibility.bind(this, columnConfig), columnConfig.visible); | |
| } | |
| const responseSubMenu = contextMenu.footerSection().appendSubMenuItem(Common.UIString('Response Headers')); | |
| const responseHeaders = columnConfigs.filter(columnConfig => columnConfig.isResponseHeader); | |
| for (const columnConfig of responseHeaders) { | |
| responseSubMenu.defaultSection().appendCheckboxItem( | |
| columnConfig.title, this._toggleColumnVisibility.bind(this, columnConfig), columnConfig.visible); | |
| } | |
| responseSubMenu.footerSection().appendItem( | |
| Common.UIString('Manage Header Columns\u2026'), this._manageCustomHeaderDialog.bind(this)); | |
| const waterfallSortIds = WaterfallSortIds; | |
| const waterfallSubMenu = contextMenu.footerSection().appendSubMenuItem(Common.UIString('Waterfall')); | |
| waterfallSubMenu.defaultSection().appendCheckboxItem( | |
| Common.UIString('Start Time'), setWaterfallMode.bind(this, waterfallSortIds.StartTime), | |
| this._activeWaterfallSortId === waterfallSortIds.StartTime); | |
| waterfallSubMenu.defaultSection().appendCheckboxItem( | |
| Common.UIString('Response Time'), setWaterfallMode.bind(this, waterfallSortIds.ResponseTime), | |
| this._activeWaterfallSortId === waterfallSortIds.ResponseTime); | |
| waterfallSubMenu.defaultSection().appendCheckboxItem( | |
| Common.UIString('End Time'), setWaterfallMode.bind(this, waterfallSortIds.EndTime), | |
| this._activeWaterfallSortId === waterfallSortIds.EndTime); | |
| waterfallSubMenu.defaultSection().appendCheckboxItem( | |
| Common.UIString('Total Duration'), setWaterfallMode.bind(this, waterfallSortIds.Duration), | |
| this._activeWaterfallSortId === waterfallSortIds.Duration); | |
| waterfallSubMenu.defaultSection().appendCheckboxItem( | |
| Common.UIString('Latency'), setWaterfallMode.bind(this, waterfallSortIds.Latency), | |
| this._activeWaterfallSortId === waterfallSortIds.Latency); | |
| /** | |
| * @param {!WaterfallSortIds} sortId | |
| * @this {NetworkLogViewColumns} | |
| */ | |
| function setWaterfallMode(sortId) { | |
| let calculator = this._calculatorsMap.get(_calculatorTypes.Time); | |
| const waterfallSortIds = WaterfallSortIds; | |
| if (sortId === waterfallSortIds.Duration || sortId === waterfallSortIds.Latency) { | |
| calculator = this._calculatorsMap.get(_calculatorTypes.Duration); | |
| } | |
| this._networkLogView.setCalculator(calculator); | |
| this._activeWaterfallSortId = sortId; | |
| this._dataGrid.markColumnAsSortedBy('waterfall', DataGrid.DataGrid.Order.Ascending); | |
| this._sortHandler(); | |
| } | |
| } | |
| _manageCustomHeaderDialog() { | |
| const customHeaders = []; | |
| for (const columnConfig of this._columns) { | |
| if (columnConfig.isResponseHeader) { | |
| customHeaders.push({title: columnConfig.title, editable: columnConfig.isCustomHeader}); | |
| } | |
| } | |
| const manageCustomHeaders = new Network.NetworkManageCustomHeadersView( | |
| customHeaders, headerTitle => !!this._addCustomHeader(headerTitle), this._changeCustomHeader.bind(this), | |
| this._removeCustomHeader.bind(this)); | |
| const dialog = new UI.Dialog(); | |
| manageCustomHeaders.show(dialog.contentElement); | |
| dialog.setSizeBehavior(UI.GlassPane.SizeBehavior.MeasureContent); | |
| dialog.show(this._networkLogView.element); | |
| } | |
| /** | |
| * @param {string} headerId | |
| * @return {boolean} | |
| */ | |
| _removeCustomHeader(headerId) { | |
| headerId = headerId.toLowerCase(); | |
| const index = this._columns.findIndex(columnConfig => columnConfig.id === headerId); | |
| if (index === -1) { | |
| return false; | |
| } | |
| this._columns.splice(index, 1); | |
| this._dataGrid.removeColumn(headerId); | |
| this._saveColumnsSettings(); | |
| this._updateColumns(); | |
| return true; | |
| } | |
| /** | |
| * @param {string} headerTitle | |
| * @param {string=} headerId | |
| * @param {number=} index | |
| * @return {?Network.NetworkLogViewColumns.Descriptor} | |
| */ | |
| _addCustomHeader(headerTitle, headerId, index) { | |
| if (!headerId) { | |
| headerId = headerTitle.toLowerCase(); | |
| } | |
| if (index === undefined) { | |
| index = this._columns.length - 1; | |
| } | |
| const currentColumnConfig = this._columns.find(columnConfig => columnConfig.id === headerId); | |
| if (currentColumnConfig) { | |
| return null; | |
| } | |
| const columnConfigBase = Object.assign({}, _defaultColumnConfig, { | |
| id: headerId, | |
| title: headerTitle, | |
| isResponseHeader: true, | |
| isCustomHeader: true, | |
| visible: true, | |
| sortingFunction: Network.NetworkRequestNode.ResponseHeaderStringComparator.bind(null, headerId) | |
| }); | |
| // Split out the column config from the typed version, as doing it in a single assignment causes | |
| // issues with Closure compiler. | |
| const columnConfig = /** @type {!Network.NetworkLogViewColumns.Descriptor} */ (columnConfigBase); | |
| this._columns.splice(index, 0, columnConfig); | |
| if (this._dataGrid) { | |
| this._dataGrid.addColumn(NetworkLogViewColumns._convertToDataGridDescriptor(columnConfig), index); | |
| } | |
| this._saveColumnsSettings(); | |
| this._updateColumns(); | |
| return columnConfig; | |
| } | |
| /** | |
| * @param {string} oldHeaderId | |
| * @param {string} newHeaderTitle | |
| * @param {string=} newHeaderId | |
| * @return {boolean} | |
| */ | |
| _changeCustomHeader(oldHeaderId, newHeaderTitle, newHeaderId) { | |
| if (!newHeaderId) { | |
| newHeaderId = newHeaderTitle.toLowerCase(); | |
| } | |
| oldHeaderId = oldHeaderId.toLowerCase(); | |
| const oldIndex = this._columns.findIndex(columnConfig => columnConfig.id === oldHeaderId); | |
| const oldColumnConfig = this._columns[oldIndex]; | |
| const currentColumnConfig = this._columns.find(columnConfig => columnConfig.id === newHeaderId); | |
| if (!oldColumnConfig || (currentColumnConfig && oldHeaderId !== newHeaderId)) { | |
| return false; | |
| } | |
| this._removeCustomHeader(oldHeaderId); | |
| this._addCustomHeader(newHeaderTitle, newHeaderId, oldIndex); | |
| return true; | |
| } | |
| /** | |
| * @param {!Event} event | |
| * @return {?UI.PopoverRequest} | |
| */ | |
| _getPopoverRequest(event) { | |
| if (!this._gridMode) { | |
| return null; | |
| } | |
| const hoveredNode = this._networkLogView.hoveredNode(); | |
| if (!hoveredNode) { | |
| return null; | |
| } | |
| const anchor = event.target.enclosingNodeOrSelfWithClass('network-script-initiated'); | |
| if (!anchor) { | |
| return null; | |
| } | |
| const request = hoveredNode.request(); | |
| if (!request) { | |
| return null; | |
| } | |
| return { | |
| box: anchor.boxInWindow(), | |
| show: popover => { | |
| const content = Network.RequestInitiatorView.createStackTracePreview( | |
| /** @type {!SDK.NetworkRequest} */ (request), this._popupLinkifier, false, | |
| () => popover.setSizeBehavior(UI.GlassPane.SizeBehavior.MeasureContent)); | |
| popover.contentElement.appendChild(content.element); | |
| return Promise.resolve(true); | |
| }, | |
| hide: this._popupLinkifier.reset.bind(this._popupLinkifier) | |
| }; | |
| } | |
| /** | |
| * @param {!Array<number>} times | |
| * @param {string} className | |
| */ | |
| addEventDividers(times, className) { | |
| // TODO(allada) Remove this and pass in the color. | |
| let color = 'transparent'; | |
| switch (className) { | |
| case 'network-dcl-divider': | |
| color = '#0867CB'; | |
| break; | |
| case 'network-load-divider': | |
| color = '#B31412'; | |
| break; | |
| default: | |
| return; | |
| } | |
| const currentTimes = this._eventDividers.get(color) || []; | |
| this._eventDividers.set(color, currentTimes.concat(times)); | |
| this._networkLogView.scheduleRefresh(); | |
| } | |
| hideEventDividers() { | |
| this._eventDividersShown = true; | |
| this._redrawWaterfallColumn(); | |
| } | |
| showEventDividers() { | |
| this._eventDividersShown = false; | |
| this._redrawWaterfallColumn(); | |
| } | |
| /** | |
| * @param {number} time | |
| */ | |
| selectFilmStripFrame(time) { | |
| this._eventDividers.set(_filmStripDividerColor, [time]); | |
| this._redrawWaterfallColumn(); | |
| } | |
| clearFilmStripFrame() { | |
| this._eventDividers.delete(_filmStripDividerColor); | |
| this._redrawWaterfallColumn(); | |
| } | |
| } | |
| export const _initialSortColumn = 'waterfall'; | |
| /** @enum {string} */ | |
| export const _calculatorTypes = { | |
| Duration: 'Duration', | |
| Time: 'Time' | |
| }; | |
| /** | |
| * @type {!Object} column | |
| */ | |
| export const _defaultColumnConfig = { | |
| subtitle: null, | |
| visible: false, | |
| weight: 6, | |
| sortable: true, | |
| hideable: true, | |
| hideableGroup: null, | |
| nonSelectable: false, | |
| isResponseHeader: false, | |
| isCustomHeader: false, | |
| allowInSortByEvenWhenHidden: false | |
| }; | |
| /** | |
| * @type {!Array.<!Network.NetworkLogViewColumns.Descriptor>} column | |
| */ | |
| export const _defaultColumns = [ | |
| { | |
| id: 'name', | |
| title: Common.UIString('Name'), | |
| subtitle: Common.UIString('Path'), | |
| visible: true, | |
| weight: 20, | |
| hideable: true, | |
| hideableGroup: 'path', | |
| sortingFunction: Network.NetworkRequestNode.NameComparator | |
| }, | |
| { | |
| id: 'path', | |
| title: ls`Path`, | |
| hideable: true, | |
| hideableGroup: 'path', | |
| sortingFunction: Network.NetworkRequestNode.RequestPropertyComparator.bind(null, 'path') | |
| }, | |
| { | |
| id: 'url', | |
| title: ls`Url`, | |
| hideable: true, | |
| hideableGroup: 'path', | |
| sortingFunction: Network.NetworkRequestNode.RequestPropertyComparator.bind(null, 'url') | |
| }, | |
| { | |
| id: 'method', | |
| title: Common.UIString('Method'), | |
| sortingFunction: Network.NetworkRequestNode.RequestPropertyComparator.bind(null, 'requestMethod') | |
| }, | |
| { | |
| id: 'status', | |
| title: Common.UIString('Status'), | |
| visible: true, | |
| subtitle: Common.UIString('Text'), | |
| sortingFunction: Network.NetworkRequestNode.RequestPropertyComparator.bind(null, 'statusCode') | |
| }, | |
| { | |
| id: 'protocol', | |
| title: Common.UIString('Protocol'), | |
| sortingFunction: Network.NetworkRequestNode.RequestPropertyComparator.bind(null, 'protocol') | |
| }, | |
| { | |
| id: 'scheme', | |
| title: Common.UIString('Scheme'), | |
| sortingFunction: Network.NetworkRequestNode.RequestPropertyComparator.bind(null, 'scheme') | |
| }, | |
| { | |
| id: 'domain', | |
| title: Common.UIString('Domain'), | |
| sortingFunction: Network.NetworkRequestNode.RequestPropertyComparator.bind(null, 'domain') | |
| }, | |
| { | |
| id: 'remoteaddress', | |
| title: Common.UIString('Remote Address'), | |
| weight: 10, | |
| align: DataGrid.DataGrid.Align.Right, | |
| sortingFunction: Network.NetworkRequestNode.RemoteAddressComparator | |
| }, | |
| { | |
| id: 'type', | |
| title: Common.UIString('Type'), | |
| visible: true, | |
| sortingFunction: Network.NetworkRequestNode.TypeComparator | |
| }, | |
| { | |
| id: 'initiator', | |
| title: Common.UIString('Initiator'), | |
| visible: true, | |
| weight: 10, | |
| sortingFunction: Network.NetworkRequestNode.InitiatorComparator | |
| }, | |
| { | |
| id: 'cookies', | |
| title: Common.UIString('Cookies'), | |
| align: DataGrid.DataGrid.Align.Right, | |
| sortingFunction: Network.NetworkRequestNode.RequestCookiesCountComparator | |
| }, | |
| { | |
| id: 'setcookies', | |
| title: Common.UIString('Set Cookies'), | |
| align: DataGrid.DataGrid.Align.Right, | |
| sortingFunction: Network.NetworkRequestNode.ResponseCookiesCountComparator | |
| }, | |
| { | |
| id: 'size', | |
| title: Common.UIString('Size'), | |
| visible: true, | |
| subtitle: Common.UIString('Content'), | |
| align: DataGrid.DataGrid.Align.Right, | |
| sortingFunction: Network.NetworkRequestNode.SizeComparator | |
| }, | |
| { | |
| id: 'time', | |
| title: Common.UIString('Time'), | |
| visible: true, | |
| subtitle: Common.UIString('Latency'), | |
| align: DataGrid.DataGrid.Align.Right, | |
| sortingFunction: Network.NetworkRequestNode.RequestPropertyComparator.bind(null, 'duration') | |
| }, | |
| {id: 'priority', title: Common.UIString('Priority'), sortingFunction: Network.NetworkRequestNode.PriorityComparator}, | |
| { | |
| id: 'connectionid', | |
| title: Common.UIString('Connection ID'), | |
| sortingFunction: Network.NetworkRequestNode.RequestPropertyComparator.bind(null, 'connectionId') | |
| }, | |
| { | |
| id: 'cache-control', | |
| isResponseHeader: true, | |
| title: Common.UIString('Cache-Control'), | |
| sortingFunction: Network.NetworkRequestNode.ResponseHeaderStringComparator.bind(null, 'cache-control') | |
| }, | |
| { | |
| id: 'connection', | |
| isResponseHeader: true, | |
| // until IDs are supported for strings, the placeholder is used to workaround the limitation that | |
| // having multiple translations for a string is not supported | |
| title: ls`${'Connection'}`, | |
| sortingFunction: Network.NetworkRequestNode.ResponseHeaderStringComparator.bind(null, 'connection') | |
| }, | |
| { | |
| id: 'content-encoding', | |
| isResponseHeader: true, | |
| title: Common.UIString('Content-Encoding'), | |
| sortingFunction: Network.NetworkRequestNode.ResponseHeaderStringComparator.bind(null, 'content-encoding') | |
| }, | |
| { | |
| id: 'content-length', | |
| isResponseHeader: true, | |
| title: Common.UIString('Content-Length'), | |
| align: DataGrid.DataGrid.Align.Right, | |
| sortingFunction: Network.NetworkRequestNode.ResponseHeaderNumberComparator.bind(null, 'content-length') | |
| }, | |
| { | |
| id: 'etag', | |
| isResponseHeader: true, | |
| title: Common.UIString('ETag'), | |
| sortingFunction: Network.NetworkRequestNode.ResponseHeaderStringComparator.bind(null, 'etag') | |
| }, | |
| { | |
| id: 'keep-alive', | |
| isResponseHeader: true, | |
| title: Common.UIString('Keep-Alive'), | |
| sortingFunction: Network.NetworkRequestNode.ResponseHeaderStringComparator.bind(null, 'keep-alive') | |
| }, | |
| { | |
| id: 'last-modified', | |
| isResponseHeader: true, | |
| title: Common.UIString('Last-Modified'), | |
| sortingFunction: Network.NetworkRequestNode.ResponseHeaderDateComparator.bind(null, 'last-modified') | |
| }, | |
| { | |
| id: 'server', | |
| isResponseHeader: true, | |
| title: Common.UIString('Server'), | |
| sortingFunction: Network.NetworkRequestNode.ResponseHeaderStringComparator.bind(null, 'server') | |
| }, | |
| { | |
| id: 'vary', | |
| isResponseHeader: true, | |
| title: Common.UIString('Vary'), | |
| sortingFunction: Network.NetworkRequestNode.ResponseHeaderStringComparator.bind(null, 'vary') | |
| }, | |
| // This header is a placeholder to let datagrid know that it can be sorted by this column, but never shown. | |
| {id: 'waterfall', title: ls`Waterfall`, visible: false, hideable: false, allowInSortByEvenWhenHidden: true} | |
| ]; | |
| export const _filmStripDividerColor = '#fccc49'; | |
| /** | |
| * @enum {string} | |
| */ | |
| export const WaterfallSortIds = { | |
| StartTime: 'startTime', | |
| ResponseTime: 'responseReceivedTime', | |
| EndTime: 'endTime', | |
| Duration: 'duration', | |
| Latency: 'latency' | |
| }; | |
| /* Legacy exported object */ | |
| self.Network = self.Network || {}; | |
| /* Legacy exported object */ | |
| Network = Network || {}; | |
| /** | |
| * @constructor | |
| */ | |
| Network.NetworkLogViewColumns = NetworkLogViewColumns; | |
| Network.NetworkLogViewColumns._initialSortColumn = _initialSortColumn; | |
| /** | |
| * @typedef {{ | |
| * id: string, | |
| * title: string, | |
| * titleDOMFragment: (!DocumentFragment|undefined), | |
| * subtitle: (string|null), | |
| * visible: boolean, | |
| * weight: number, | |
| * hideable: boolean, | |
| * hideableGroup: ?string, | |
| * nonSelectable: boolean, | |
| * sortable: boolean, | |
| * align: (?DataGrid.DataGrid.Align|undefined), | |
| * isResponseHeader: boolean, | |
| * sortingFunction: (!function(!Network.NetworkNode, !Network.NetworkNode):number|undefined), | |
| * isCustomHeader: boolean, | |
| * allowInSortByEvenWhenHidden: boolean | |
| * }} | |
| */ | |
| Network.NetworkLogViewColumns.Descriptor; | |
| /** @enum {string} */ | |
| Network.NetworkLogViewColumns._calculatorTypes = _calculatorTypes; | |
| /** | |
| * @type {!Object} column | |
| */ | |
| Network.NetworkLogViewColumns._defaultColumnConfig = _defaultColumnConfig; | |
| /** | |
| * @type {!Array.<!Network.NetworkLogViewColumns.Descriptor>} column | |
| */ | |
| Network.NetworkLogViewColumns._defaultColumns = _defaultColumns; | |
| Network.NetworkLogViewColumns._filmStripDividerColor = _filmStripDividerColor; | |
| /** | |
| * @enum {string} | |
| */ | |
| Network.NetworkLogViewColumns.WaterfallSortIds = WaterfallSortIds; |