Index: third_party/WebKit/Source/devtools/front_end/bindings/ResourceMapping.js |
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings/ResourceMapping.js b/third_party/WebKit/Source/devtools/front_end/bindings/ResourceMapping.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..387c9e1e794e7ccf0b2f4e32ba2428bd3b80b8f6 |
--- /dev/null |
+++ b/third_party/WebKit/Source/devtools/front_end/bindings/ResourceMapping.js |
@@ -0,0 +1,285 @@ |
+// 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. |
+ |
+/** |
+ * @implements {SDK.SDKModelObserver<!SDK.ResourceTreeModel>} |
+ */ |
+Bindings.ResourceMapping = class { |
+ /** |
+ * @param {!SDK.TargetManager} targetManager |
+ * @param {!Workspace.Workspace} workspace |
+ */ |
+ constructor(targetManager, workspace) { |
+ this._workspace = workspace; |
+ /** @type {!Map<!SDK.ResourceTreeModel, !Bindings.ResourceMapping.ModelInfo>} */ |
+ this._modelToInfo = new Map(); |
+ targetManager.observeModels(SDK.ResourceTreeModel, this); |
+ } |
+ |
+ /** |
+ * @override |
+ * @param {!SDK.ResourceTreeModel} resourceTreeModel |
+ */ |
+ modelAdded(resourceTreeModel) { |
+ var info = new Bindings.ResourceMapping.ModelInfo(this._workspace, resourceTreeModel); |
+ this._modelToInfo.set(resourceTreeModel, info); |
+ } |
+ |
+ /** |
+ * @override |
+ * @param {!SDK.ResourceTreeModel} resourceTreeModel |
+ */ |
+ modelRemoved(resourceTreeModel) { |
+ var info = this._modelToInfo.get(resourceTreeModel); |
+ info.dispose(); |
+ this._modelToInfo.delete(resourceTreeModel); |
+ } |
+ |
+ /** |
+ * @param {!SDK.Target} target |
+ * @return {?Bindings.ResourceMapping.ModelInfo} |
+ */ |
+ _infoForTarget(target) { |
+ var resourceTreeModel = target.model(SDK.ResourceTreeModel); |
+ return resourceTreeModel ? this._modelToInfo.get(resourceTreeModel) : null; |
+ } |
+ |
+ /** |
+ * @param {!SDK.CSSLocation} cssLocation |
+ * @return {?Workspace.UILocation} |
+ */ |
+ cssLocationToUILocation(cssLocation) { |
+ var info = this._infoForTarget(cssLocation.cssModel().target()); |
+ if (!info) |
+ return null; |
+ var uiSourceCode = info._project.uiSourceCodeForURL(cssLocation.url); |
+ return uiSourceCode ? uiSourceCode.uiLocation(cssLocation.lineNumber, cssLocation.columnNumber) : null; |
+ } |
+ |
+ /** |
+ * @param {!SDK.DebuggerModel.Location} jsLocation |
+ * @return {?Workspace.UILocation} |
+ */ |
+ jsLocationToUILocation(jsLocation) { |
+ var script = jsLocation.script(); |
+ if (!script) |
+ return null; |
+ var info = this._infoForTarget(jsLocation.debuggerModel.target()); |
+ if (!info) |
+ return null; |
+ var uiSourceCode = info._project.uiSourceCodeForURL(script.sourceURL); |
+ return uiSourceCode ? uiSourceCode.uiLocation(jsLocation.lineNumber, jsLocation.columnNumber) : null; |
+ } |
+ |
+ /** |
+ * @param {!Workspace.UISourceCode} uiSourceCode |
+ * @param {number} lineNumber |
+ * @param {number} columnNumber |
+ * @return {?SDK.DebuggerModel.Location} |
+ */ |
+ uiLocationToJSLocation(uiSourceCode, lineNumber, columnNumber) { |
+ if (!uiSourceCode[Bindings.ResourceMapping._symbol]) |
+ return null; |
+ var target = Bindings.NetworkProject.targetForUISourceCode(uiSourceCode); |
+ if (!target) |
+ return null; |
+ var debuggerModel = target.model(SDK.DebuggerModel); |
+ if (!debuggerModel) |
+ return null; |
+ return debuggerModel.createRawLocationByURL(uiSourceCode.url(), lineNumber, columnNumber); |
+ } |
+ |
+ /** |
+ * @param {!SDK.Target} target |
+ */ |
+ _resetForTest(target) { |
+ var resourceTreeModel = target.model(SDK.ResourceTreeModel); |
+ var info = resourceTreeModel ? this._modelToInfo.get(resourceTreeModel) : null; |
+ if (info) |
+ info._resetForTest(); |
+ } |
+}; |
+ |
+Bindings.ResourceMapping.ModelInfo = class { |
+ /** |
+ * @param {!Workspace.Workspace} workspace |
+ * @param {!SDK.ResourceTreeModel} resourceTreeModel |
+ */ |
+ constructor(workspace, resourceTreeModel) { |
+ var target = resourceTreeModel.target(); |
+ this._project = new Bindings.ContentProviderBasedProject( |
+ workspace, 'resources:' + target.id(), Workspace.projectTypes.Network, '', false /* isServiceProject */); |
+ Bindings.NetworkProject.setTargetForProject(this._project, target); |
+ |
+ /** @type {!Map<string, !Bindings.ResourceMapping.Binding>} */ |
+ this._bindings = new Map(); |
+ |
+ this._eventListeners = [ |
+ resourceTreeModel.addEventListener(SDK.ResourceTreeModel.Events.ResourceAdded, this._resourceAdded, this), |
+ resourceTreeModel.addEventListener(SDK.ResourceTreeModel.Events.FrameWillNavigate, this._frameWillNavigate, this), |
+ resourceTreeModel.addEventListener(SDK.ResourceTreeModel.Events.FrameDetached, this._frameDetached, this) |
+ ]; |
+ } |
+ |
+ /** |
+ * @param {!SDK.Resource} resource |
+ */ |
+ _acceptsResource(resource) { |
+ var resourceType = resource.resourceType(); |
+ // Only load selected resource types from resources. |
+ if (resourceType !== Common.resourceTypes.Image && resourceType !== Common.resourceTypes.Font && |
+ resourceType !== Common.resourceTypes.Document && resourceType !== Common.resourceTypes.Manifest) |
+ return false; |
+ |
+ // Ignore non-images and non-fonts. |
+ if (resourceType === Common.resourceTypes.Image && resource.mimeType && !resource.mimeType.startsWith('image')) |
+ return false; |
+ if (resourceType === Common.resourceTypes.Font && resource.mimeType && !resource.mimeType.includes('font')) |
+ return false; |
+ if ((resourceType === Common.resourceTypes.Image || resourceType === Common.resourceTypes.Font) && |
+ resource.contentURL().startsWith('data:')) |
+ return false; |
+ return true; |
+ } |
+ |
+ /** |
+ * @param {!Common.Event} event |
+ */ |
+ _resourceAdded(event) { |
+ var resource = /** @type {!SDK.Resource} */ (event.data); |
+ if (!this._acceptsResource(resource)) |
+ return; |
+ |
+ var binding = this._bindings.get(resource.url); |
+ if (!binding) { |
+ binding = new Bindings.ResourceMapping.Binding(this._project, resource); |
+ this._bindings.set(resource.url, binding); |
+ } else { |
+ binding.addResource(resource); |
+ } |
+ } |
+ |
+ /** |
+ * @param {!SDK.ResourceTreeFrame} frame |
+ */ |
+ _removeFrameResources(frame) { |
+ for (var resource of frame.resources()) { |
+ if (!this._acceptsResource(resource)) |
+ continue; |
+ var binding = this._bindings.get(resource.url); |
+ if (binding._resources.size === 1) { |
+ binding.dispose(); |
+ this._bindings.delete(resource.url); |
+ } else { |
+ binding.removeResource(resource); |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * @param {!Common.Event} event |
+ */ |
+ _frameWillNavigate(event) { |
+ var frame = /** @type {!SDK.ResourceTreeFrame} */ (event.data); |
+ this._removeFrameResources(frame); |
+ } |
+ |
+ /** |
+ * @param {!Common.Event} event |
+ */ |
+ _frameDetached(event) { |
+ var frame = /** @type {!SDK.ResourceTreeFrame} */ (event.data); |
+ this._removeFrameResources(frame); |
+ } |
+ |
+ _resetForTest() { |
+ for (var binding of this._bindings.valuesArray()) |
+ binding.dispose(); |
+ this._bindings.clear(); |
+ } |
+ |
+ dispose() { |
+ Common.EventTarget.removeEventListeners(this._eventListeners); |
+ for (var binding of this._bindings.valuesArray()) |
+ binding.dispose(); |
+ this._bindings.clear(); |
+ this._project.removeProject(); |
+ } |
+}; |
+ |
+/** |
+ * @implements {Common.ContentProvider} |
+ */ |
+Bindings.ResourceMapping.Binding = class { |
+ /** |
+ * @param {!Bindings.ContentProviderBasedProject} project |
+ * @param {!SDK.Resource} resource |
+ */ |
+ constructor(project, resource) { |
+ this._resources = new Set([resource]); |
+ this._project = project; |
+ this._uiSourceCode = this._project.createUISourceCode(resource.url, resource.contentType()); |
+ this._uiSourceCode[Bindings.ResourceMapping._symbol] = true; |
+ Bindings.NetworkProject.setInitialFrameAttribution(this._uiSourceCode, resource.frameId); |
+ this._project.addUISourceCodeWithProvider( |
+ this._uiSourceCode, this, Bindings.resourceMetadata(resource), resource.mimeType); |
+ } |
+ |
+ /** |
+ * @param {!SDK.Resource} resource |
+ */ |
+ addResource(resource) { |
+ this._resources.add(resource); |
+ Bindings.NetworkProject.addFrameAttribution(this._uiSourceCode, resource.frameId); |
+ } |
+ |
+ /** |
+ * @param {!SDK.Resource} resource |
+ */ |
+ removeResource(resource) { |
+ this._resources.delete(resource); |
+ Bindings.NetworkProject.removeFrameAttribution(this._uiSourceCode, resource.frameId); |
+ } |
+ |
+ dispose() { |
+ this._project.removeFile(this._uiSourceCode.url()); |
+ } |
+ |
+ /** |
+ * @override |
+ * @return {string} |
+ */ |
+ contentURL() { |
+ return this._resources.firstValue().contentURL(); |
+ } |
+ |
+ /** |
+ * @override |
+ * @return {!Common.ResourceType} |
+ */ |
+ contentType() { |
+ return this._resources.firstValue().contentType(); |
+ } |
+ |
+ /** |
+ * @override |
+ * @return {!Promise<?string>} |
+ */ |
+ requestContent() { |
+ return this._resources.firstValue().requestContent(); |
+ } |
+ |
+ /** |
+ * @override |
+ * @param {string} query |
+ * @param {boolean} caseSensitive |
+ * @param {boolean} isRegex |
+ * @return {!Promise<!Array<!Common.ContentProvider.SearchMatch>>} |
+ */ |
+ searchInContent(query, caseSensitive, isRegex) { |
+ return this._resources.firstValue().searchInContent(query, caseSensitive, isRegex); |
+ } |
+}; |
+ |
+Bindings.ResourceMapping._symbol = Symbol('Bindings.ResourceMapping._symbol'); |