UNPKG

@dcloudio/uni-debugger

Version:

uni-app debugger

379 lines (343 loc) 14.2 kB
/* * Copyright (C) 2012 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @implements {Bindings.DebuggerSourceMapping} * @unrestricted */ Bindings.CompilerScriptMapping = class { /** * @param {!SDK.DebuggerModel} debuggerModel * @param {!Workspace.Workspace} workspace * @param {!Bindings.DebuggerWorkspaceBinding} debuggerWorkspaceBinding */ constructor(debuggerModel, workspace, debuggerWorkspaceBinding) { this._debuggerModel = debuggerModel; this._sourceMapManager = this._debuggerModel.sourceMapManager(); this._workspace = workspace; this._debuggerWorkspaceBinding = debuggerWorkspaceBinding; const target = debuggerModel.target(); this._regularProject = new Bindings.ContentProviderBasedProject( workspace, 'jsSourceMaps::' + target.id(), Workspace.projectTypes.Network, '', false /* isServiceProject */); this._contentScriptsProject = new Bindings.ContentProviderBasedProject( workspace, 'jsSourceMaps:extensions:' + target.id(), Workspace.projectTypes.ContentScripts, '', false /* isServiceProject */); Bindings.NetworkProject.setTargetForProject(this._regularProject, target); Bindings.NetworkProject.setTargetForProject(this._contentScriptsProject, target); /** @type {!Map<string, !Bindings.CompilerScriptMapping.Binding>} */ this._regularBindings = new Map(); /** @type {!Map<string, !Bindings.CompilerScriptMapping.Binding>} */ this._contentScriptsBindings = new Map(); /** @type {!Map<!SDK.Script, !Workspace.UISourceCode>} */ this._stubUISourceCodes = new Map(); this._stubProject = new Bindings.ContentProviderBasedProject( workspace, 'jsSourceMaps:stub:' + target.id(), Workspace.projectTypes.Service, '', true /* isServiceProject */); this._eventListeners = [ this._sourceMapManager.addEventListener( SDK.SourceMapManager.Events.SourceMapWillAttach, this._sourceMapWillAttach, this), this._sourceMapManager.addEventListener( SDK.SourceMapManager.Events.SourceMapFailedToAttach, this._sourceMapFailedToAttach, this), this._sourceMapManager.addEventListener( SDK.SourceMapManager.Events.SourceMapAttached, this._sourceMapAttached, this), this._sourceMapManager.addEventListener( SDK.SourceMapManager.Events.SourceMapDetached, this._sourceMapDetached, this), ]; } /** * @param {!SDK.Script} script */ _addStubUISourceCode(script) { const stubUISourceCode = this._stubProject.addContentProvider( script.sourceURL + ':sourcemap', Common.StaticContentProvider.fromString( script.sourceURL, Common.resourceTypes.Script, '\n\n\n\n\n// Please wait a bit.\n// Compiled script is not shown while source map is being loaded!'), 'text/javascript'); this._stubUISourceCodes.set(script, stubUISourceCode); } /** * @param {!SDK.Script} script */ _removeStubUISourceCode(script) { const uiSourceCode = this._stubUISourceCodes.get(script); this._stubUISourceCodes.delete(script); this._stubProject.removeFile(uiSourceCode.url()); this._debuggerWorkspaceBinding.updateLocations(script); } /** * @param {!Workspace.UISourceCode} uiSourceCode * @return {?string} */ static uiSourceCodeOrigin(uiSourceCode) { const sourceMap = uiSourceCode[Bindings.CompilerScriptMapping._sourceMapSymbol]; if (!sourceMap) return null; return sourceMap.compiledURL(); } /** * @param {!SDK.DebuggerModel.Location} rawLocation * @return {boolean} */ mapsToSourceCode(rawLocation) { const script = rawLocation.script(); const sourceMap = script ? this._sourceMapManager.sourceMapForClient(script) : null; if (!sourceMap) return true; return !!sourceMap.findEntry(rawLocation.lineNumber, rawLocation.columnNumber); } /** * @param {string} url * @param {boolean} isContentScript */ uiSourceCodeForURL(url, isContentScript) { return isContentScript ? this._contentScriptsProject.uiSourceCodeForURL(url) : this._regularProject.uiSourceCodeForURL(url); } /** * @override * @param {!SDK.DebuggerModel.Location} rawLocation * @return {?Workspace.UILocation} */ rawLocationToUILocation(rawLocation) { const script = rawLocation.script(); if (!script) return null; const lineNumber = rawLocation.lineNumber - script.lineOffset; let columnNumber = rawLocation.columnNumber; if (!lineNumber) columnNumber -= script.columnOffset; const stubUISourceCode = this._stubUISourceCodes.get(script); if (stubUISourceCode) return new Workspace.UILocation(stubUISourceCode, lineNumber, columnNumber); const sourceMap = this._sourceMapManager.sourceMapForClient(script); if (!sourceMap) return null; const entry = sourceMap.findEntry(lineNumber, columnNumber); if (!entry || !entry.sourceURL) return null; const uiSourceCode = script.isContentScript() ? this._contentScriptsProject.uiSourceCodeForURL(entry.sourceURL) : this._regularProject.uiSourceCodeForURL(entry.sourceURL); if (!uiSourceCode) return null; return uiSourceCode.uiLocation( /** @type {number} */ (entry.sourceLineNumber), /** @type {number} */ (entry.sourceColumnNumber)); } /** * @override * @param {!Workspace.UISourceCode} uiSourceCode * @param {number} lineNumber * @param {number} columnNumber * @return {?SDK.DebuggerModel.Location} */ uiLocationToRawLocation(uiSourceCode, lineNumber, columnNumber) { const sourceMap = uiSourceCode[Bindings.CompilerScriptMapping._sourceMapSymbol]; if (!sourceMap) return null; const scripts = this._sourceMapManager.clientsForSourceMap(sourceMap); const script = scripts.length ? scripts[0] : null; if (!script) return null; const entry = sourceMap.sourceLineMapping(uiSourceCode.url(), lineNumber, columnNumber); if (!entry) return null; return this._debuggerModel.createRawLocation( script, entry.lineNumber + script.lineOffset, !entry.lineNumber ? entry.columnNumber + script.columnOffset : entry.columnNumber); } /** * @param {!Common.Event} event */ _sourceMapWillAttach(event) { const script = /** @type {!SDK.Script} */ (event.data); // Create stub UISourceCode for the time source mapping is being loaded. this._addStubUISourceCode(script); this._debuggerWorkspaceBinding.updateLocations(script); } /** * @param {!Common.Event} event */ _sourceMapFailedToAttach(event) { const script = /** @type {!SDK.Script} */ (event.data); this._removeStubUISourceCode(script); } /** * @param {!Common.Event} event */ _sourceMapAttached(event) { const script = /** @type {!SDK.Script} */ (event.data.client); const sourceMap = /** @type {!SDK.SourceMap} */ (event.data.sourceMap); this._removeStubUISourceCode(script); if (Bindings.blackboxManager.isBlackboxedURL(script.sourceURL, script.isContentScript())) return; Bindings.blackboxManager.sourceMapLoaded(script, sourceMap); this._populateSourceMapSources(script, sourceMap); this._sourceMapAttachedForTest(sourceMap); } /** * @param {!Common.Event} event */ _sourceMapDetached(event) { const script = /** @type {!SDK.Script} */ (event.data.client); const frameId = script[Bindings.CompilerScriptMapping._frameIdSymbol]; const sourceMap = /** @type {!SDK.SourceMap} */ (event.data.sourceMap); const bindings = script.isContentScript() ? this._contentScriptsBindings : this._regularBindings; for (const sourceURL of sourceMap.sourceURLs()) { const binding = bindings.get(sourceURL); binding.removeSourceMap(sourceMap, frameId); if (!binding._uiSourceCode) bindings.delete(sourceURL); } this._debuggerWorkspaceBinding.updateLocations(script); } /** * @param {!SDK.Script} script * @return {?SDK.SourceMap} */ sourceMapForScript(script) { return this._sourceMapManager.sourceMapForClient(script); } /** * @param {!SDK.Script} script */ maybeLoadSourceMap(script) { const sourceMap = this._sourceMapManager.sourceMapForClient(script); if (!sourceMap) return; this._populateSourceMapSources(script, sourceMap); } /** * @param {?SDK.SourceMap} sourceMap */ _sourceMapAttachedForTest(sourceMap) { } /** * @param {!SDK.Script} script * @param {!SDK.SourceMap} sourceMap */ _populateSourceMapSources(script, sourceMap) { const frameId = Bindings.frameIdForScript(script); script[Bindings.CompilerScriptMapping._frameIdSymbol] = frameId; const project = script.isContentScript() ? this._contentScriptsProject : this._regularProject; const bindings = script.isContentScript() ? this._contentScriptsBindings : this._regularBindings; for (const sourceURL of sourceMap.sourceURLs()) { let binding = bindings.get(sourceURL); if (!binding) { binding = new Bindings.CompilerScriptMapping.Binding(project, sourceURL); bindings.set(sourceURL, binding); } binding.addSourceMap(sourceMap, frameId); } this._debuggerWorkspaceBinding.updateLocations(script); } /** * @override * @param {!Workspace.UISourceCode} uiSourceCode * @param {number} lineNumber * @return {boolean} */ static uiLineHasMapping(uiSourceCode, lineNumber) { const sourceMap = uiSourceCode[Bindings.CompilerScriptMapping._sourceMapSymbol]; if (!sourceMap) return true; return !!sourceMap.sourceLineMapping(uiSourceCode.url(), lineNumber, 0); } dispose() { Common.EventTarget.removeEventListeners(this._eventListeners); this._regularProject.dispose(); this._contentScriptsProject.dispose(); this._stubProject.dispose(); } }; Bindings.CompilerScriptMapping._frameIdSymbol = Symbol('Bindings.CompilerScriptMapping._frameIdSymbol'); Bindings.CompilerScriptMapping._sourceMapSymbol = Symbol('Bindings.CompilerScriptMapping._sourceMapSymbol'); Bindings.CompilerScriptMapping.Binding = class { /** * @param {!Bindings.ContentProviderBasedProject} project * @param {string} url */ constructor(project, url) { this._project = project; this._url = url; /** @type {!Array<!SDK.SourceMap>} */ this._referringSourceMaps = []; this._activeSourceMap = null; this._uiSourceCode = null; } /** * @param {string} frameId */ _recreateUISourceCodeIfNeeded(frameId) { const sourceMap = this._referringSourceMaps.peekLast(); if (this._activeSourceMap === sourceMap) return; this._activeSourceMap = sourceMap; const newUISourceCode = this._project.createUISourceCode(this._url, Common.resourceTypes.SourceMapScript); newUISourceCode[Bindings.CompilerScriptMapping._sourceMapSymbol] = sourceMap; const contentProvider = sourceMap.sourceContentProvider(this._url, Common.resourceTypes.SourceMapScript); const mimeType = Common.ResourceType.mimeFromURL(this._url) || 'text/javascript'; const embeddedContent = sourceMap.embeddedContentByURL(this._url); const metadata = typeof embeddedContent === 'string' ? new Workspace.UISourceCodeMetadata(null, embeddedContent.length) : null; if (this._uiSourceCode) { Bindings.NetworkProject.cloneInitialFrameAttribution(this._uiSourceCode, newUISourceCode); this._project.removeFile(this._uiSourceCode.url()); } else { Bindings.NetworkProject.setInitialFrameAttribution(newUISourceCode, frameId); } this._uiSourceCode = newUISourceCode; this._project.addUISourceCodeWithProvider(this._uiSourceCode, contentProvider, metadata, mimeType); } /** * @param {!SDK.SourceMap} sourceMap * @param {string} frameId */ addSourceMap(sourceMap, frameId) { if (this._uiSourceCode) Bindings.NetworkProject.addFrameAttribution(this._uiSourceCode, frameId); this._referringSourceMaps.push(sourceMap); this._recreateUISourceCodeIfNeeded(frameId); } /** * @param {!SDK.SourceMap} sourceMap * @param {string} frameId */ removeSourceMap(sourceMap, frameId) { Bindings.NetworkProject.removeFrameAttribution( /** @type {!Workspace.UISourceCode} */ (this._uiSourceCode), frameId); const lastIndex = this._referringSourceMaps.lastIndexOf(sourceMap); if (lastIndex !== -1) this._referringSourceMaps.splice(lastIndex, 1); if (!this._referringSourceMaps.length) { this._project.removeFile(this._uiSourceCode.url()); this._uiSourceCode = null; } else { this._recreateUISourceCodeIfNeeded(frameId); } } };