blob: 83b2d20ffc6ff0268d2b1b86610900cb71e0889e [file] [log] [blame]
// Copyright 2017 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.
Resources.ResourcesSection = class {
/**
* @param {!Resources.ResourcesPanel} storagePanel
* @param {!UI.TreeElement} treeElement
*/
constructor(storagePanel, treeElement) {
this._panel = storagePanel;
this._treeElement = treeElement;
/** @type {!Map<string, !Resources.FrameTreeElement>} */
this._treeElementForFrameId = new Map();
function addListener(eventType, handler, target) {
SDK.targetManager.addModelListener(SDK.ResourceTreeModel, eventType, event => handler.call(target, event.data));
}
addListener(SDK.ResourceTreeModel.Events.FrameAdded, this._frameAdded, this);
addListener(SDK.ResourceTreeModel.Events.FrameNavigated, this._frameNavigated, this);
addListener(SDK.ResourceTreeModel.Events.FrameDetached, this._frameDetached, this);
addListener(SDK.ResourceTreeModel.Events.ResourceAdded, this._resourceAdded, this);
const mainTarget = SDK.targetManager.mainTarget();
const resourceTreeModel = mainTarget && mainTarget.model(SDK.ResourceTreeModel);
const mainFrame = resourceTreeModel && resourceTreeModel.mainFrame;
if (mainFrame) {
this._frameAdded(mainFrame);
}
}
/**
* @param {!SDK.ResourceTreeFrame} frame
* @returns {?SDK.ResourceTreeFrame}
*/
static _getParentFrame(frame) {
const parentFrame = frame.parentFrame;
if (parentFrame) {
return parentFrame;
}
const parentTarget = frame.resourceTreeModel().target().parentTarget();
if (!parentTarget) {
return null;
}
return parentTarget.model(SDK.ResourceTreeModel).mainFrame;
}
/**
* @param {?SDK.ResourceTreeFrame} frame
* @return {boolean}
*/
_expandFrame(frame) {
if (!frame) {
return false;
}
let treeElement = this._treeElementForFrameId.get(frame.id);
if (!treeElement && !this._expandFrame(Resources.ResourcesSection._getParentFrame(frame))) {
return false;
}
treeElement = this._treeElementForFrameId.get(frame.id);
if (!treeElement) {
return false;
}
treeElement.expand();
return true;
}
/**
* @param {!SDK.Resource} resource
* @param {number=} line
* @param {number=} column
* @return {!Promise}
*/
async revealResource(resource, line, column) {
if (!this._expandFrame(resource.frame())) {
return;
}
const resourceTreeElement = Resources.FrameResourceTreeElement.forResource(resource);
if (resourceTreeElement) {
await resourceTreeElement.revealResource(line, column);
}
}
/**
* @param {!SDK.ResourceTreeFrame} frame
*/
_frameAdded(frame) {
const parentFrame = Resources.ResourcesSection._getParentFrame(frame);
const parentTreeElement = parentFrame ? this._treeElementForFrameId.get(parentFrame.id) : this._treeElement;
if (!parentTreeElement) {
return;
}
const frameTreeElement = new Resources.FrameTreeElement(this, frame);
this._treeElementForFrameId.set(frame.id, frameTreeElement);
parentTreeElement.appendChild(frameTreeElement);
}
/**
* @param {!SDK.ResourceTreeFrame} frame
*/
_frameDetached(frame) {
const frameTreeElement = this._treeElementForFrameId.get(frame.id);
if (!frameTreeElement) {
return;
}
this._treeElementForFrameId.remove(frame.id);
if (frameTreeElement.parent) {
frameTreeElement.parent.removeChild(frameTreeElement);
}
}
/**
* @param {!SDK.ResourceTreeFrame} frame
*/
_frameNavigated(frame) {
const frameTreeElement = this._treeElementForFrameId.get(frame.id);
if (frameTreeElement) {
frameTreeElement.frameNavigated(frame);
}
}
/**
* @param {!SDK.Resource} resource
*/
_resourceAdded(resource) {
const frameTreeElement = this._treeElementForFrameId.get(resource.frameId);
if (!frameTreeElement) {
// This is a frame's main resource, it will be retained
// and re-added by the resource manager;
return;
}
frameTreeElement.appendResource(resource);
}
reset() {
this._treeElement.removeChildren();
this._treeElementForFrameId.clear();
}
};
Resources.FrameTreeElement = class extends Resources.BaseStorageTreeElement {
/**
* @param {!Resources.ResourcesSection} section
* @param {!SDK.ResourceTreeFrame} frame
*/
constructor(section, frame) {
super(section._panel, '', false);
this._populated = false;
this._section = section;
this._frame = frame;
this._frameId = frame.id;
this._categoryElements = {};
this._treeElementForResource = {};
this.setExpandable(true);
this.frameNavigated(frame);
const icon = UI.Icon.create('largeicon-navigator-frame', 'navigator-tree-item');
icon.classList.add('navigator-frame-tree-item');
this.setLeadingIcons([icon]);
}
/**
* @param {!SDK.ResourceTreeFrame} frame
*/
frameNavigated(frame) {
this.invalidateChildren();
this._frameId = frame.id;
this.title = frame.displayName();
this._categoryElements = {};
this._treeElementForResource = {};
}
get itemURL() {
return 'frame://' + encodeURI(this.titleAsText());
}
/**
* @override
* @return {boolean}
*/
onselect(selectedByUser) {
super.onselect(selectedByUser);
this._section._panel.showCategoryView(this.titleAsText(), null);
this.listItemElement.classList.remove('hovered');
SDK.OverlayModel.hideDOMNodeHighlight();
return false;
}
set hovered(hovered) {
if (hovered) {
this.listItemElement.classList.add('hovered');
this._frame.resourceTreeModel().domModel().overlayModel().highlightFrame(this._frameId);
} else {
this.listItemElement.classList.remove('hovered');
SDK.OverlayModel.hideDOMNodeHighlight();
}
}
/**
* @param {!SDK.Resource} resource
*/
appendResource(resource) {
if (!this._populated) {
return;
}
const statusCode = resource['statusCode'];
if (statusCode >= 301 && statusCode <= 303) {
return;
}
const resourceType = resource.resourceType();
const categoryName = resourceType.name();
let categoryElement = resourceType === Common.resourceTypes.Document ? this : this._categoryElements[categoryName];
if (!categoryElement) {
categoryElement = new Resources.StorageCategoryTreeElement(
this._section._panel, resource.resourceType().category().title, categoryName);
this._categoryElements[resourceType.name()] = categoryElement;
this._insertInPresentationOrder(this, categoryElement);
}
const resourceTreeElement = new Resources.FrameResourceTreeElement(this._section._panel, resource);
this._insertInPresentationOrder(categoryElement, resourceTreeElement);
this._treeElementForResource[resource.url] = resourceTreeElement;
}
/**
* @param {string} url
* @return {?SDK.Resource}
*/
resourceByURL(url) {
const treeElement = this._treeElementForResource[url];
return treeElement ? treeElement._resource : null;
}
/**
* @override
* @param {!UI.TreeElement} treeElement
*/
appendChild(treeElement) {
if (!this._populated) {
return;
}
this._insertInPresentationOrder(this, treeElement);
}
_insertInPresentationOrder(parentTreeElement, childTreeElement) {
// Insert in the alphabetical order, first frames, then resources. Document resource goes last.
function typeWeight(treeElement) {
if (treeElement instanceof Resources.StorageCategoryTreeElement) {
return 2;
}
if (treeElement instanceof Resources.FrameTreeElement) {
return 1;
}
return 3;
}
function compare(treeElement1, treeElement2) {
const typeWeight1 = typeWeight(treeElement1);
const typeWeight2 = typeWeight(treeElement2);
let result;
if (typeWeight1 > typeWeight2) {
result = 1;
} else if (typeWeight1 < typeWeight2) {
result = -1;
} else {
result = treeElement1.titleAsText().localeCompare(treeElement2.titleAsText());
}
return result;
}
const childCount = parentTreeElement.childCount();
let i;
for (i = 0; i < childCount; ++i) {
if (compare(childTreeElement, parentTreeElement.childAt(i)) < 0) {
break;
}
}
parentTreeElement.insertChild(childTreeElement, i);
}
/**
* @override
* @returns {!Promise}
*/
async onpopulate() {
this._populated = true;
for (const child of this._frame.childFrames) {
this._section._frameAdded(child);
}
for (const resource of this._frame.resources()) {
this.appendResource(resource);
}
}
};
Resources.FrameResourceTreeElement = class extends Resources.BaseStorageTreeElement {
/**
* @param {!Resources.ResourcesPanel} storagePanel
* @param {!SDK.Resource} resource
*/
constructor(storagePanel, resource) {
super(storagePanel, resource.displayName, false);
this._panel = storagePanel;
/** @type {!SDK.Resource} */
this._resource = resource;
/** @type {?Promise<!UI.Widget>} */
this._previewPromise = null;
this.tooltip = resource.url;
this._resource[Resources.FrameResourceTreeElement._symbol] = this;
const icon = UI.Icon.create('largeicon-navigator-file', 'navigator-tree-item');
icon.classList.add('navigator-file-tree-item');
icon.classList.add('navigator-' + resource.resourceType().name() + '-tree-item');
this.setLeadingIcons([icon]);
}
/**
* @param {!SDK.Resource} resource
*/
static forResource(resource) {
return resource[Resources.FrameResourceTreeElement._symbol];
}
get itemURL() {
return this._resource.url;
}
/**
* @return {!Promise<!UI.Widget>}
*/
_preparePreview() {
if (this._previewPromise) {
return this._previewPromise;
}
const viewPromise = SourceFrame.PreviewFactory.createPreview(this._resource, this._resource.mimeType);
this._previewPromise = viewPromise.then(view => {
if (view) {
return view;
}
return new UI.EmptyWidget(this._resource.url);
});
return this._previewPromise;
}
/**
* @override
* @return {boolean}
*/
onselect(selectedByUser) {
super.onselect(selectedByUser);
this._panel.scheduleShowView(this._preparePreview());
return false;
}
/**
* @override
* @return {boolean}
*/
ondblclick(event) {
Host.InspectorFrontendHost.openInNewTab(this._resource.url);
return false;
}
/**
* @override
*/
onattach() {
super.onattach();
this.listItemElement.draggable = true;
this.listItemElement.addEventListener('dragstart', this._ondragstart.bind(this), false);
this.listItemElement.addEventListener('contextmenu', this._handleContextMenuEvent.bind(this), true);
}
/**
* @param {!MouseEvent} event
* @return {boolean}
*/
_ondragstart(event) {
event.dataTransfer.setData('text/plain', this._resource.content || '');
event.dataTransfer.effectAllowed = 'copy';
return true;
}
_handleContextMenuEvent(event) {
const contextMenu = new UI.ContextMenu(event);
contextMenu.appendApplicableItems(this._resource);
contextMenu.show();
}
/**
* @param {number=} line
* @param {number=} column
*/
async revealResource(line, column) {
this.revealAndSelect(true);
const view = await this._panel.scheduleShowView(this._preparePreview());
if (!(view instanceof SourceFrame.ResourceSourceFrame) || typeof line !== 'number') {
return;
}
view.revealPosition(line, column, true);
}
};
Resources.FrameResourceTreeElement._symbol = Symbol('treeElement');