| // 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 |
| */ |
| Network.NetworkLogViewColumns = class { |
| /** |
| * @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(Network.NetworkLogViewColumns._calculatorTypes.Time, timeCalculator); |
| this._calculatorsMap.set(Network.NetworkLogViewColumns._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 = Network.NetworkLogViewColumns._defaultColumns; |
| |
| const defaultColumnConfig = Network.NetworkLogViewColumns._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(Network.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 = Network.NetworkLogViewColumns.WaterfallSortIds.StartTime; |
| this._dataGrid.markColumnAsSortedBy( |
| Network.NetworkLogViewColumns._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 {Network.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 {Network.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.ContextMenu} 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 = Network.NetworkLogViewColumns.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); |
| |
| contextMenu.show(); |
| |
| /** |
| * @param {!Network.NetworkLogViewColumns.WaterfallSortIds} sortId |
| * @this {Network.NetworkLogViewColumns} |
| */ |
| function setWaterfallMode(sortId) { |
| let calculator = this._calculatorsMap.get(Network.NetworkLogViewColumns._calculatorTypes.Time); |
| const waterfallSortIds = Network.NetworkLogViewColumns.WaterfallSortIds; |
| if (sortId === waterfallSortIds.Duration || sortId === waterfallSortIds.Latency) { |
| calculator = this._calculatorsMap.get(Network.NetworkLogViewColumns._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 columnConfig = /** @type {!Network.NetworkLogViewColumns.Descriptor} */ ( |
| Object.assign({}, Network.NetworkLogViewColumns._defaultColumnConfig, { |
| id: headerId, |
| title: headerTitle, |
| isResponseHeader: true, |
| isCustomHeader: true, |
| visible: true, |
| sortingFunction: Network.NetworkRequestNode.ResponseHeaderStringComparator.bind(null, headerId) |
| })); |
| this._columns.splice(index, 0, columnConfig); |
| if (this._dataGrid) { |
| this._dataGrid.addColumn(Network.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(Network.NetworkLogViewColumns._filmStripDividerColor, [time]); |
| this._redrawWaterfallColumn(); |
| } |
| |
| clearFilmStripFrame() { |
| this._eventDividers.delete(Network.NetworkLogViewColumns._filmStripDividerColor); |
| this._redrawWaterfallColumn(); |
| } |
| }; |
| |
| Network.NetworkLogViewColumns._initialSortColumn = 'waterfall'; |
| |
| /** |
| * @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 = { |
| Duration: 'Duration', |
| Time: 'Time' |
| }; |
| |
| /** |
| * @type {!Object} column |
| */ |
| Network.NetworkLogViewColumns._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 |
| */ |
| Network.NetworkLogViewColumns._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} |
| ]; |
| |
| Network.NetworkLogViewColumns._filmStripDividerColor = '#fccc49'; |
| |
| /** |
| * @enum {string} |
| */ |
| Network.NetworkLogViewColumns.WaterfallSortIds = { |
| StartTime: 'startTime', |
| ResponseTime: 'responseReceivedTime', |
| EndTime: 'endTime', |
| Duration: 'duration', |
| Latency: 'latency' |
| }; |