Chromium Code Reviews| Index: third_party/WebKit/Source/devtools/front_end/resources/ResourcesSection.js |
| diff --git a/third_party/WebKit/Source/devtools/front_end/resources/ResourcesSection.js b/third_party/WebKit/Source/devtools/front_end/resources/ResourcesSection.js |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..c6b4f4e13f7a029df440cd8c965e1eb8ba3d686c |
| --- /dev/null |
| +++ b/third_party/WebKit/Source/devtools/front_end/resources/ResourcesSection.js |
| @@ -0,0 +1,358 @@ |
| +// 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); |
|
caseq
2017/03/03 21:59:54
let's keep listeners private.
eostroukhov
2017/03/04 00:16:36
Done.
|
| + addListener(SDK.ResourceTreeModel.Events.FrameNavigated, this.frameNavigated, this); |
| + addListener(SDK.ResourceTreeModel.Events.FrameDetached, this.frameDetached, this); |
| + addListener(SDK.ResourceTreeModel.Events.ResourceAdded, this.resourceAdded, this); |
| + |
| + var mainTarget = SDK.targetManager.mainTarget(); |
| + var resourceTreeModel = mainTarget && mainTarget.hasDOMCapability() && SDK.ResourceTreeModel.fromTarget(mainTarget); |
| + var mainFrame = resourceTreeModel && resourceTreeModel.mainFrame; |
| + if (mainFrame) |
| + this.populateFrame(mainFrame); |
|
dgozman
2017/02/25 00:54:42
Why populate with main frame?
eostroukhov
2017/02/25 01:00:22
It is the root node.
|
| + } |
| + |
| + /** |
| + * @param {!SDK.ResourceTreeFrame} frame |
| + * @returns {?SDK.ResourceTreeFrame} |
| + */ |
| + static _getParentFrame(frame) { |
| + var parentFrame = frame.parentFrame; |
| + if (parentFrame) |
| + return parentFrame; |
| + var parentTarget = frame.target().parentTarget(); |
| + while (parentTarget && !parentTarget.hasDOMCapability()) |
|
caseq
2017/03/03 21:59:54
Does it ever happen?
eostroukhov
2017/03/04 00:16:36
Changed to console.assert, as per offline discussi
|
| + parentTarget = parentTarget.parentTarget(); |
| + if (!parentTarget) |
| + return null; |
| + var model = SDK.ResourceTreeModel.fromTarget(parentTarget); |
| + return model.mainFrame; |
| + } |
| + |
| + /** |
| + * @param {!SDK.ResourceTreeFrame} frame |
| + */ |
| + frameAdded(frame) { |
| + var parentFrame = Resources.ResourcesSection._getParentFrame(frame); |
| + var parentTreeElement = parentFrame ? this._treeElementForFrameId.get(parentFrame.id) : this._treeElement; |
| + if (!parentTreeElement) { |
| + console.warn('No frame to route ' + frame.url + ' to.'); |
|
caseq
2017/03/03 21:59:54
nit: `No frame to route ${frame.url} to.`
eostroukhov
2017/03/04 00:16:36
Done.
|
| + return; |
| + } |
| + |
| + var frameTreeElement = new Resources.FrameTreeElement(this._panel, frame); |
| + this._treeElementForFrameId.set(frame.id, frameTreeElement); |
| + parentTreeElement.appendChild(frameTreeElement); |
| + } |
| + |
| + /** |
| + * @param {!SDK.ResourceTreeFrame} frame |
| + */ |
| + frameDetached(frame) { |
| + var 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) { |
| + if (!Resources.ResourcesSection._getParentFrame(frame)) |
| + return; |
| + var frameTreeElement = this._treeElementForFrameId.get(frame.id); |
| + if (frameTreeElement) |
| + frameTreeElement.frameNavigated(frame); |
| + } |
| + |
| + /** |
| + * @param {!SDK.Resource} resource |
| + */ |
| + resourceAdded(resource) { |
| + var statusCode = resource['statusCode']; |
| + if (statusCode >= 301 && statusCode <= 303) |
| + return; |
| + |
| + var 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(); |
| + } |
| + |
| + /** |
| + * @param {!SDK.ResourceTreeFrame} frame |
| + */ |
| + populateFrame(frame) { |
| + this.frameAdded(frame); |
| + for (var child of frame.childFrames) |
| + this.populateFrame(child); |
| + for (var resource of frame.resources()) |
| + this.resourceAdded(resource); |
| + } |
| +}; |
| + |
| +Resources.FrameTreeElement = class extends Resources.BaseStorageTreeElement { |
|
dgozman
2017/02/25 00:54:42
Didn't we agree on the frame picker? Why is there
eostroukhov
2017/02/25 01:00:22
This is a refactoring change, functionality remain
caseq
2017/03/03 21:59:55
Mind re-uploading with --similarity=<something-sma
eostroukhov
2017/03/04 00:16:36
I tried that.
|
| + /** |
| + * @param {!Resources.ResourcesPanel} storagePanel |
| + * @param {!SDK.ResourceTreeFrame} frame |
| + */ |
| + constructor(storagePanel, frame) { |
| + super(storagePanel, '', false); |
| + this._panel = storagePanel; |
| + this._frame = frame; |
| + this._frameId = frame.id; |
| + this._categoryElements = {}; |
| + this._treeElementForResource = {}; |
| + this.frameNavigated(frame); |
| + |
| + var icon = UI.Icon.create('largeicon-navigator-frame', 'navigator-tree-item'); |
| + icon.classList.add('navigator-frame-tree-item'); |
| + this.setLeadingIcons([icon]); |
| + } |
| + |
| + frameNavigated(frame) { |
| + this.removeChildren(); |
| + 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._panel.showCategoryView(this.titleAsText()); |
| + |
| + this.listItemElement.classList.remove('hovered'); |
| + SDK.DOMModel.hideDOMNodeHighlight(); |
| + return false; |
| + } |
| + |
| + set hovered(hovered) { |
| + if (hovered) { |
| + this.listItemElement.classList.add('hovered'); |
| + var domModel = SDK.DOMModel.fromTarget(this._frame.target()); |
| + if (domModel) |
| + domModel.highlightFrame(this._frameId); |
| + } else { |
| + this.listItemElement.classList.remove('hovered'); |
| + SDK.DOMModel.hideDOMNodeHighlight(); |
| + } |
| + } |
| + |
| + /** |
| + * @param {!SDK.Resource} resource |
| + */ |
| + appendResource(resource) { |
| + var resourceType = resource.resourceType(); |
| + var categoryName = resourceType.name(); |
| + var categoryElement = resourceType === Common.resourceTypes.Document ? this : this._categoryElements[categoryName]; |
| + if (!categoryElement) { |
| + categoryElement = |
| + new Resources.StorageCategoryTreeElement(this._panel, resource.resourceType().category().title, categoryName); |
| + this._categoryElements[resourceType.name()] = categoryElement; |
| + this._insertInPresentationOrder(this, categoryElement); |
| + } |
| + var resourceTreeElement = new Resources.FrameResourceTreeElement(this._panel, resource); |
| + this._insertInPresentationOrder(categoryElement, resourceTreeElement); |
| + this._treeElementForResource[resource.url] = resourceTreeElement; |
| + } |
| + |
| + /** |
| + * @param {string} url |
| + * @return {?SDK.Resource} |
| + */ |
| + resourceByURL(url) { |
| + var treeElement = this._treeElementForResource[url]; |
| + return treeElement ? treeElement._resource : null; |
| + } |
| + |
| + /** |
| + * @override |
| + */ |
| + appendChild(treeElement) { |
| + 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) { |
| + var typeWeight1 = typeWeight(treeElement1); |
| + var typeWeight2 = typeWeight(treeElement2); |
| + |
| + var result; |
| + if (typeWeight1 > typeWeight2) |
| + result = 1; |
| + else if (typeWeight1 < typeWeight2) |
| + result = -1; |
| + else |
| + result = treeElement1.titleAsText().localeCompare(treeElement2.titleAsText()); |
| + return result; |
| + } |
| + |
| + var childCount = parentTreeElement.childCount(); |
| + var i; |
| + for (i = 0; i < childCount; ++i) { |
| + if (compare(childTreeElement, parentTreeElement.childAt(i)) < 0) |
| + break; |
| + } |
| + parentTreeElement.insertChild(childTreeElement, i); |
| + } |
| +}; |
| + |
| +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 {?SourceFrame.ResourceSourceFrame} */ |
| + this._sourceFrame = null; |
| + this.tooltip = resource.url; |
| + this._resource[Resources.FrameResourceTreeElement._symbol] = this; |
| + |
| + var 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]; |
| + } |
| + |
| + /** |
| + * @param {!SDK.Resource} resource |
| + * @return {?UI.Widget} |
| + */ |
| + static resourceViewForResource(resource) { |
| + if (resource.hasTextContent()) { |
| + var treeElement = Resources.FrameResourceTreeElement.forResource(resource); |
| + if (!treeElement) |
| + return null; |
| + return treeElement._sourceView(); |
| + } |
| + |
| + switch (resource.resourceType()) { |
| + case Common.resourceTypes.Image: |
| + return new SourceFrame.ImageView(resource.mimeType, resource); |
| + case Common.resourceTypes.Font: |
| + return new SourceFrame.FontView(resource.mimeType, resource); |
| + default: |
| + return new UI.EmptyWidget(resource.url); |
| + } |
| + } |
| + |
| + get itemURL() { |
| + return this._resource.url; |
| + } |
| + |
| + /** |
| + * @override |
| + * @return {boolean} |
| + */ |
| + onselect(selectedByUser) { |
| + super.onselect(selectedByUser); |
| + this.showView(Resources.FrameResourceTreeElement.resourceViewForResource(this._resource)); |
| + return false; |
| + } |
| + |
| + /** |
| + * @override |
| + * @return {boolean} |
| + */ |
| + ondblclick(event) { |
| + 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) { |
| + var contextMenu = new UI.ContextMenu(event); |
| + contextMenu.appendApplicableItems(this._resource); |
| + contextMenu.show(); |
| + } |
| + |
| + /** |
| + * @return {!SourceFrame.ResourceSourceFrame} |
| + */ |
| + _sourceView() { |
| + if (!this._sourceFrame) { |
| + this._sourceFrame = new SourceFrame.ResourceSourceFrame(this._resource); |
| + this._sourceFrame.setHighlighterType(this._resource.canonicalMimeType()); |
| + } |
| + return this._sourceFrame; |
| + } |
| +}; |
| + |
| +Resources.FrameResourceTreeElement._symbol = Symbol('treeElement'); |