UNPKG

@dcloudio/uni-debugger

Version:

uni-app debugger

257 lines (231 loc) 9.15 kB
// 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. /** * @template T */ SDK.SourceMapManager = class extends Common.Object { /** * @param {!SDK.Target} target */ constructor(target) { super(); this._target = target; this._isEnabled = true; /** @type {!Map<!T, string>} */ this._relativeSourceURL = new Map(); /** @type {!Map<!T, string>} */ this._relativeSourceMapURL = new Map(); /** @type {!Map<!T, string>} */ this._resolvedSourceMapURL = new Map(); /** @type {!Map<string, !SDK.SourceMap>} */ this._sourceMapByURL = new Map(); /** @type {!Multimap<string, !T>} */ this._sourceMapURLToLoadingClients = new Multimap(); /** @type {!Multimap<string, !T>} */ this._sourceMapURLToClients = new Multimap(); SDK.targetManager.addEventListener(SDK.TargetManager.Events.InspectedURLChanged, this._inspectedURLChanged, this); } /** * @param {boolean} isEnabled */ setEnabled(isEnabled) { if (isEnabled === this._isEnabled) return; this._isEnabled = isEnabled; const clients = Array.from(this._resolvedSourceMapURL.keys()); for (const client of clients) { const relativeSourceURL = this._relativeSourceURL.get(client); const relativeSourceMapURL = this._relativeSourceMapURL.get(client); this.detachSourceMap(client); this.attachSourceMap(client, relativeSourceURL, relativeSourceMapURL); } } /** * @param {!Common.Event} event */ _inspectedURLChanged(event) { if (event.data !== this._target) return; const clients = Array.from(this._resolvedSourceMapURL.keys()); for (const client of clients) { const relativeSourceURL = this._relativeSourceURL.get(client); const relativeSourceMapURL = this._relativeSourceMapURL.get(client); const resolvedSourceMapURL = this._resolvedSourceMapURL.get(client); const sourceMapURL = this._resolveRelativeURLs(relativeSourceURL, relativeSourceMapURL).sourceMapURL; if (sourceMapURL !== resolvedSourceMapURL) { this.detachSourceMap(client); this.attachSourceMap(client, relativeSourceURL, relativeSourceMapURL); } } } /** * @param {!T} client * @return {?SDK.SourceMap} */ sourceMapForClient(client) { const sourceMapURL = this._resolvedSourceMapURL.get(client); return sourceMapURL ? this._sourceMapByURL.get(sourceMapURL) || null : null; } /** * @param {!SDK.SourceMap} sourceMap * @return {!Array<!T>} */ clientsForSourceMap(sourceMap) { if (this._sourceMapURLToClients.has(sourceMap.url())) return this._sourceMapURLToClients.get(sourceMap.url()).valuesArray(); return this._sourceMapURLToLoadingClients.get(sourceMap.url()).valuesArray(); } /** * @param {!SDK.SourceMap.EditResult} editResult */ applySourceMapEdit(editResult) { console.assert( this._sourceMapByURL.has(editResult.map.url()), 'Cannot apply edit result for non-existing source map'); this._sourceMapByURL.set(editResult.map.url(), editResult.map); this.dispatchEventToListeners( SDK.SourceMapManager.Events.SourceMapChanged, {sourceMap: editResult.map, newSources: editResult.newSources}); } /** * @param {string} sourceURL * @param {string} sourceMapURL * @return {!{sourceURL: ?string, sourceMapURL: ?string}} */ _resolveRelativeURLs(sourceURL, sourceMapURL) { // |sourceURL| can be a random string, but is generally an absolute path. // Complete it to inspected page url for relative links. const resolvedSourceURL = Common.ParsedURL.completeURL(this._target.inspectedURL(), sourceURL); const resolvedSourceMapURL = resolvedSourceURL ? Common.ParsedURL.completeURL(resolvedSourceURL, sourceMapURL) : null; return {sourceURL: resolvedSourceURL, sourceMapURL: resolvedSourceMapURL}; } /** * @param {!T} client * @param {string} sourceURL * @param {?string} sourceMapURL */ attachSourceMap(client, sourceURL, sourceMapURL) { if (!sourceMapURL) return; console.assert(!this._resolvedSourceMapURL.has(client), 'SourceMap is already attached to client'); const resolvedURLs = this._resolveRelativeURLs(sourceURL, sourceMapURL); if (!resolvedURLs.sourceURL || !resolvedURLs.sourceMapURL) return; this._relativeSourceURL.set(client, sourceURL); this._relativeSourceMapURL.set(client, sourceMapURL); this._resolvedSourceMapURL.set(client, resolvedURLs.sourceMapURL); sourceURL = resolvedURLs.sourceURL; sourceMapURL = resolvedURLs.sourceMapURL; if (!this._isEnabled) return; this.dispatchEventToListeners(SDK.SourceMapManager.Events.SourceMapWillAttach, client); if (this._sourceMapByURL.has(sourceMapURL)) { attach.call(this, sourceMapURL, client); return; } if (!this._sourceMapURLToLoadingClients.has(sourceMapURL)) { SDK.TextSourceMap.load(sourceMapURL, sourceURL) .then(onTextSourceMapLoaded.bind(this, sourceMapURL)) .then(onSourceMap.bind(this, sourceMapURL)); } this._sourceMapURLToLoadingClients.set(sourceMapURL, client); /** * @param {string} sourceMapURL * @param {?SDK.TextSourceMap} sourceMap * @return {!Promise<?SDK.SourceMap>} * @this {SDK.SourceMapManager} */ function onTextSourceMapLoaded(sourceMapURL, sourceMap) { if (!sourceMap) return Promise.resolve(/** @type {?SDK.SourceMap} */ (null)); const factoryExtension = this._factoryForSourceMap(sourceMap); if (!factoryExtension) return Promise.resolve(/** @type {?SDK.SourceMap} */ (sourceMap)); return factoryExtension.instance() .then(factory => factory.editableSourceMap(this._target, sourceMap)) .then(map => map || sourceMap) .catchException(/** @type {?SDK.SourceMap} */ (null)); } /** * @param {string} sourceMapURL * @param {?SDK.SourceMap} sourceMap * @this {SDK.SourceMapManager} */ function onSourceMap(sourceMapURL, sourceMap) { this._sourceMapLoadedForTest(); const clients = this._sourceMapURLToLoadingClients.get(sourceMapURL); this._sourceMapURLToLoadingClients.deleteAll(sourceMapURL); if (!clients.size) return; if (!sourceMap) { for (const client of clients) this.dispatchEventToListeners(SDK.SourceMapManager.Events.SourceMapFailedToAttach, client); return; } this._sourceMapByURL.set(sourceMapURL, sourceMap); for (const client of clients) attach.call(this, sourceMapURL, client); } /** * @param {string} sourceMapURL * @param {!T} client * @this {SDK.SourceMapManager} */ function attach(sourceMapURL, client) { this._sourceMapURLToClients.set(sourceMapURL, client); const sourceMap = this._sourceMapByURL.get(sourceMapURL); this.dispatchEventToListeners( SDK.SourceMapManager.Events.SourceMapAttached, {client: client, sourceMap: sourceMap}); } } /** * @param {!SDK.SourceMap} sourceMap * @return {?Runtime.Extension} */ _factoryForSourceMap(sourceMap) { const sourceExtensions = new Set(); for (const url of sourceMap.sourceURLs()) sourceExtensions.add(Common.ParsedURL.extractExtension(url)); for (const runtimeExtension of self.runtime.extensions(SDK.SourceMapFactory)) { const supportedExtensions = new Set(runtimeExtension.descriptor()['extensions']); if (supportedExtensions.containsAll(sourceExtensions)) return runtimeExtension; } return null; } /** * @param {!T} client */ detachSourceMap(client) { const sourceMapURL = this._resolvedSourceMapURL.get(client); this._relativeSourceURL.delete(client); this._relativeSourceMapURL.delete(client); this._resolvedSourceMapURL.delete(client); if (!sourceMapURL) return; if (!this._sourceMapURLToClients.hasValue(sourceMapURL, client)) { if (this._sourceMapURLToLoadingClients.delete(sourceMapURL, client)) this.dispatchEventToListeners(SDK.SourceMapManager.Events.SourceMapFailedToAttach, client); return; } this._sourceMapURLToClients.delete(sourceMapURL, client); const sourceMap = this._sourceMapByURL.get(sourceMapURL); if (!this._sourceMapURLToClients.has(sourceMapURL)) this._sourceMapByURL.delete(sourceMapURL); this.dispatchEventToListeners( SDK.SourceMapManager.Events.SourceMapDetached, {client: client, sourceMap: sourceMap}); } _sourceMapLoadedForTest() { } dispose() { SDK.targetManager.removeEventListener( SDK.TargetManager.Events.InspectedURLChanged, this._inspectedURLChanged, this); } }; SDK.SourceMapManager.Events = { SourceMapWillAttach: Symbol('SourceMapWillAttach'), SourceMapFailedToAttach: Symbol('SourceMapFailedToAttach'), SourceMapAttached: Symbol('SourceMapAttached'), SourceMapDetached: Symbol('SourceMapDetached'), SourceMapChanged: Symbol('SourceMapChanged') };