Files
react-native-devtools-frontend/front_end/developer_resources/DeveloperResourcesListView.js
T
Wolfgang Beyer d376239b44 Move FrameManager from browser_sdk to sdk
Change-Id: I4a14a449d1de6ea21912bb130d5ca03bf8b28d57
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2287519
Commit-Queue: Wolfgang Beyer <wolfi@chromium.org>
Reviewed-by: Sigurd Schneider <sigurds@chromium.org>
2020-07-08 15:29:26 +00:00

272 lines
8.2 KiB
JavaScript

// Copyright (c) 2020 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 DataGrid from '../data_grid/data_grid.js';
import {ls} from '../platform/platform.js';
import * as SDK from '../sdk/sdk.js'; // eslint-disable-line no-unused-vars
import * as TextUtils from '../text_utils/text_utils.js';
import * as UI from '../ui/ui.js';
export class DeveloperResourcesListView extends UI.Widget.VBox {
/**
* @param {function(!SDK.PageResourceLoader.PageResource):boolean} isVisibleFilter
*/
constructor(isVisibleFilter) {
super(true);
/** @type {!Map<!SDK.PageResourceLoader.PageResource, !GridNode>} */
this._nodeForItem = new Map();
this._isVisibleFilter = isVisibleFilter;
/** @type {?RegExp} */
this._highlightRegExp = null;
this.registerRequiredCSS('developer_resources/developerResourcesListView.css');
const columns = [
{id: 'status', title: ls`Status`, width: '60px', fixedWidth: true, sortable: true},
{id: 'url', title: ls`URL`, width: '250px', fixedWidth: false, sortable: true},
{id: 'frame', title: ls`Frame`, width: '80px', fixedWidth: false, sortable: true}, {
id: 'size',
title: ls`Total Bytes`,
width: '80px',
fixedWidth: true,
sortable: true,
align: DataGrid.DataGrid.Align.Right
},
{
id: 'errorMessage',
title: ls`Error`,
width: '200px',
fixedWidth: false,
sortable: true,
}
];
/** @type {!DataGrid.SortableDataGrid.SortableDataGrid<!GridNode>} */
this._dataGrid = new DataGrid.SortableDataGrid.SortableDataGrid({displayName: ls`Developer Resources`, columns});
this._dataGrid.setResizeMethod(DataGrid.DataGrid.ResizeMethod.Last);
this._dataGrid.element.classList.add('flex-auto');
this._dataGrid.addEventListener(DataGrid.DataGrid.Events.SortingChanged, this._sortingChanged, this);
const dataGridWidget = this._dataGrid.asWidget();
dataGridWidget.show(this.contentElement);
this.setDefaultFocusedChild(dataGridWidget);
}
/**
* @param {!Iterable<!SDK.PageResourceLoader.PageResource>} items
*/
update(items) {
let hadUpdates = false;
const rootNode = this._dataGrid.rootNode();
for (const item of items) {
let node = this._nodeForItem.get(item);
if (node) {
if (this._isVisibleFilter(node.item)) {
hadUpdates = node._refreshIfNeeded() || hadUpdates;
}
continue;
}
node = new GridNode(item);
this._nodeForItem.set(item, node);
if (this._isVisibleFilter(node.item)) {
rootNode.appendChild(node);
hadUpdates = true;
}
}
if (hadUpdates) {
this._sortingChanged();
}
}
reset() {
this._nodeForItem.clear();
this._dataGrid.rootNode().removeChildren();
}
/**
* @param {?RegExp} highlightRegExp
*/
updateFilterAndHighlight(highlightRegExp) {
this._highlightRegExp = highlightRegExp;
let hadTreeUpdates = false;
for (const node of this._nodeForItem.values()) {
const shouldBeVisible = this._isVisibleFilter(node.item);
const isVisible = !!node.parent;
if (shouldBeVisible) {
node._setHighlight(this._highlightRegExp);
}
if (shouldBeVisible === isVisible) {
continue;
}
hadTreeUpdates = true;
if (!shouldBeVisible) {
node.remove();
} else {
this._dataGrid.rootNode().appendChild(node);
}
}
if (hadTreeUpdates) {
this._sortingChanged();
}
}
_sortingChanged() {
const columnId = this._dataGrid.sortColumnId();
if (!columnId) {
return;
}
const sortFunction = GridNode.sortFunctionForColumn(columnId);
if (sortFunction) {
this._dataGrid.sortNodes(sortFunction, !this._dataGrid.isSortOrderAscending());
}
}
}
/**
* @extends {DataGrid.SortableDataGrid.SortableDataGridNode<!GridNode>}
*/
class GridNode extends DataGrid.SortableDataGrid.SortableDataGridNode {
/**
* @param {!SDK.PageResourceLoader.PageResource} item
*/
constructor(item) {
super();
this.item = item;
/** @type {?RegExp} */
this._highlightRegExp = null;
}
/**
* @param {?RegExp} highlightRegExp
*/
_setHighlight(highlightRegExp) {
if (this._highlightRegExp === highlightRegExp) {
return;
}
this._highlightRegExp = highlightRegExp;
this.refresh();
}
/**
* @return {boolean}
*/
_refreshIfNeeded() {
this.refresh();
return true;
}
/**
* @override
* @param {string} columnId
* @return {!HTMLElement}
*/
createCell(columnId) {
const cell = /** @type {!HTMLElement} */ (this.createTD(columnId));
switch (columnId) {
case 'url': {
cell.title = this.item.url;
const outer = cell.createChild('div', 'url-outer');
const prefix = outer.createChild('div', 'url-prefix');
const suffix = outer.createChild('div', 'url-suffix');
const splitURL = /^(.*)(\/[^/]*)$/.exec(this.item.url);
prefix.textContent = splitURL ? splitURL[1] : this.item.url;
suffix.textContent = splitURL ? splitURL[2] : '';
if (this._highlightRegExp) {
this._highlight(outer, this.item.url);
}
this.setCellAccessibleName(this.item.url, cell, columnId);
break;
}
case 'frame': {
const frame = SDK.FrameManager.FrameManager.instance().getFrame(this.item.frameId);
if (frame) {
cell.textContent = frame.displayName();
} else {
cell.textContent = this.item.frameId;
}
cell.onmouseenter = () => {
const frame = SDK.FrameManager.FrameManager.instance().getFrame(this.item.frameId);
if (frame) {
frame.highlight();
}
};
cell.onmouseleave = () => SDK.OverlayModel.OverlayModel.hideDOMNodeHighlight();
break;
}
case 'status': {
if (this.item.success === null) {
cell.textContent = ls`pending`;
} else {
cell.textContent = this.item.success ? ls`success` : ls`failure`;
}
break;
}
case 'size': {
const size = this.item.size;
if (size !== null) {
const sizeSpan = cell.createChild('span');
sizeSpan.textContent = Number.withThousandsSeparator(size);
const sizeAccessibleName = (size === 1) ? ls`1 byte` : ls`${size} bytes`;
this.setCellAccessibleName(sizeAccessibleName, cell, columnId);
}
break;
}
case 'errorMessage': {
cell.classList.add('error-message');
if (this.item.errorMessage) {
cell.textContent = this.item.errorMessage;
if (this._highlightRegExp) {
this._highlight(cell, this.item.errorMessage);
}
}
break;
}
}
return cell;
}
/**
* @param {!Element} element
* @param {string} textContent
*/
_highlight(element, textContent) {
if (!this._highlightRegExp) {
return;
}
const matches = this._highlightRegExp.exec(textContent);
if (!matches || !matches.length) {
return;
}
const range = new TextUtils.TextRange.SourceRange(matches.index, matches[0].length);
UI.UIUtils.highlightRangesWithStyleClass(element, [range], 'filter-highlight');
}
/**
*
* @param {string} columnId
* @returns {null|function(!GridNode, !GridNode):number}
*/
static sortFunctionForColumn(columnId) {
/**
* @param {*} x
*/
const nullToNegative = x => x === null ? -1 : Number(x);
switch (columnId) {
case 'url':
return (a, b) => a.item.url.localeCompare(b.item.url);
case 'status':
return (a, b) => {
return nullToNegative(a.item.success) - nullToNegative(b.item.success);
};
case 'size':
return (a, b) => nullToNegative(a.item.size) - nullToNegative(b.item.size);
case 'frame':
return (a, b) => a.item.frameId.localeCompare(b.item.frameId);
case 'errorMessage':
return (a, b) => (a.item.errorMessage || '').localeCompare(b.item.errorMessage || '');
default:
console.assert(false, 'Unknown sort field: ' + columnId);
return null;
}
}
}