diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /devtools/server/actors/utils/TabSources.js | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'devtools/server/actors/utils/TabSources.js')
-rw-r--r-- | devtools/server/actors/utils/TabSources.js | 833 |
1 files changed, 833 insertions, 0 deletions
diff --git a/devtools/server/actors/utils/TabSources.js b/devtools/server/actors/utils/TabSources.js new file mode 100644 index 000000000..56e862939 --- /dev/null +++ b/devtools/server/actors/utils/TabSources.js @@ -0,0 +1,833 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const { Ci, Cu } = require("chrome"); +const DevToolsUtils = require("devtools/shared/DevToolsUtils"); +const { assert, fetch } = DevToolsUtils; +const EventEmitter = require("devtools/shared/event-emitter"); +const { OriginalLocation, GeneratedLocation } = require("devtools/server/actors/common"); +const { resolve } = require("promise"); +const { joinURI } = require("devtools/shared/path"); + +loader.lazyRequireGetter(this, "SourceActor", "devtools/server/actors/source", true); +loader.lazyRequireGetter(this, "isEvalSource", "devtools/server/actors/source", true); +loader.lazyRequireGetter(this, "SourceMapConsumer", "source-map", true); +loader.lazyRequireGetter(this, "SourceMapGenerator", "source-map", true); + +/** + * Manages the sources for a thread. Handles source maps, locations in the + * sources, etc for ThreadActors. + */ +function TabSources(threadActor, allowSourceFn = () => true) { + EventEmitter.decorate(this); + + this._thread = threadActor; + this._useSourceMaps = true; + this._autoBlackBox = true; + this._anonSourceMapId = 1; + this.allowSource = source => { + return !isHiddenSource(source) && allowSourceFn(source); + }; + + this.blackBoxedSources = new Set(); + this.prettyPrintedSources = new Map(); + this.neverAutoBlackBoxSources = new Set(); + + // generated Debugger.Source -> promise of SourceMapConsumer + this._sourceMaps = new Map(); + // sourceMapURL -> promise of SourceMapConsumer + this._sourceMapCache = Object.create(null); + // Debugger.Source -> SourceActor + this._sourceActors = new Map(); + // url -> SourceActor + this._sourceMappedSourceActors = Object.create(null); +} + +/** + * Matches strings of the form "foo.min.js" or "foo-min.js", etc. If the regular + * expression matches, we can be fairly sure that the source is minified, and + * treat it as such. + */ +const MINIFIED_SOURCE_REGEXP = /\bmin\.js$/; + +TabSources.prototype = { + /** + * Update preferences and clear out existing sources + */ + setOptions: function (options) { + let shouldReset = false; + + if ("useSourceMaps" in options) { + shouldReset = true; + this._useSourceMaps = options.useSourceMaps; + } + + if ("autoBlackBox" in options) { + shouldReset = true; + this._autoBlackBox = options.autoBlackBox; + } + + if (shouldReset) { + this.reset(); + } + }, + + /** + * Clear existing sources so they are recreated on the next access. + * + * @param Object opts + * Specify { sourceMaps: true } if you also want to clear + * the source map cache (usually done on reload). + */ + reset: function (opts = {}) { + this._sourceActors = new Map(); + this._sourceMaps = new Map(); + this._sourceMappedSourceActors = Object.create(null); + + if (opts.sourceMaps) { + this._sourceMapCache = Object.create(null); + } + }, + + /** + * Return the source actor representing the `source` (or + * `originalUrl`), creating one if none exists already. May return + * null if the source is disallowed. + * + * @param Debugger.Source source + * The source to make an actor for + * @param String originalUrl + * The original source URL of a sourcemapped source + * @param optional Debguger.Source generatedSource + * The generated source that introduced this source via source map, + * if any. + * @param optional String contentType + * The content type of the source, if immediately available. + * @returns a SourceActor representing the source or null. + */ + source: function ({ source, originalUrl, generatedSource, + isInlineSource, contentType }) { + assert(source || (originalUrl && generatedSource), + "TabSources.prototype.source needs an originalUrl or a source"); + + if (source) { + // If a source is passed, we are creating an actor for a real + // source, which may or may not be sourcemapped. + + if (!this.allowSource(source)) { + return null; + } + + // It's a hack, but inline HTML scripts each have real sources, + // but we want to represent all of them as one source as the + // HTML page. The actor representing this fake HTML source is + // stored in this array, which always has a URL, so check it + // first. + if (source.url in this._sourceMappedSourceActors) { + return this._sourceMappedSourceActors[source.url]; + } + + if (isInlineSource) { + // If it's an inline source, the fake HTML source hasn't been + // created yet (would have returned above), so flip this source + // into a sourcemapped state by giving it an `originalUrl` which + // is the HTML url. + originalUrl = source.url; + source = null; + } + else if (this._sourceActors.has(source)) { + return this._sourceActors.get(source); + } + } + else if (originalUrl) { + // Not all "original" scripts are distinctly separate from the + // generated script. Pretty-printed sources have a sourcemap for + // themselves, so we need to make sure there a real source + // doesn't already exist with this URL. + for (let [source, actor] of this._sourceActors) { + if (source.url === originalUrl) { + return actor; + } + } + + if (originalUrl in this._sourceMappedSourceActors) { + return this._sourceMappedSourceActors[originalUrl]; + } + } + + let actor = new SourceActor({ + thread: this._thread, + source: source, + originalUrl: originalUrl, + generatedSource: generatedSource, + isInlineSource: isInlineSource, + contentType: contentType + }); + + let sourceActorStore = this._thread.sourceActorStore; + var id = sourceActorStore.getReusableActorId(source, originalUrl); + if (id) { + actor.actorID = id; + } + + this._thread.threadLifetimePool.addActor(actor); + sourceActorStore.setReusableActorId(source, originalUrl, actor.actorID); + + if (this._autoBlackBox && + !this.neverAutoBlackBoxSources.has(actor.url) && + this._isMinifiedURL(actor.url)) { + + this.blackBox(actor.url); + this.neverAutoBlackBoxSources.add(actor.url); + } + + if (source) { + this._sourceActors.set(source, actor); + } + else { + this._sourceMappedSourceActors[originalUrl] = actor; + } + + this._emitNewSource(actor); + return actor; + }, + + _emitNewSource: function (actor) { + if (!actor.source) { + // Always notify if we don't have a source because that means + // it's something that has been sourcemapped, or it represents + // the HTML file that contains inline sources. + this.emit("newSource", actor); + } + else { + // If sourcemapping is enabled and a source has sourcemaps, we + // create `SourceActor` instances for both the original and + // generated sources. The source actors for the generated + // sources are only for internal use, however; breakpoints are + // managed by these internal actors. We only want to notify the + // user of the original sources though, so if the actor has a + // `Debugger.Source` instance and a valid source map (meaning + // it's a generated source), don't send the notification. + this.fetchSourceMap(actor.source).then(map => { + if (!map) { + this.emit("newSource", actor); + } + }); + } + }, + + getSourceActor: function (source) { + if (source.url in this._sourceMappedSourceActors) { + return this._sourceMappedSourceActors[source.url]; + } + + if (this._sourceActors.has(source)) { + return this._sourceActors.get(source); + } + + throw new Error("getSource: could not find source actor for " + + (source.url || "source")); + }, + + getSourceActorByURL: function (url) { + if (url) { + for (let [source, actor] of this._sourceActors) { + if (source.url === url) { + return actor; + } + } + + if (url in this._sourceMappedSourceActors) { + return this._sourceMappedSourceActors[url]; + } + } + + throw new Error("getSourceActorByURL: could not find source for " + url); + return null; + }, + + /** + * Returns true if the URL likely points to a minified resource, false + * otherwise. + * + * @param String aURL + * The URL to test. + * @returns Boolean + */ + _isMinifiedURL: function (aURL) { + if (!aURL) { + return false; + } + + try { + let url = new URL(aURL); + let pathname = url.pathname; + return MINIFIED_SOURCE_REGEXP.test(pathname.slice(pathname.lastIndexOf("/") + 1)); + } catch (e) { + // Not a valid URL so don't try to parse out the filename, just test the + // whole thing with the minified source regexp. + return MINIFIED_SOURCE_REGEXP.test(aURL); + } + }, + + /** + * Create a source actor representing this source. This ignores + * source mapping and always returns an actor representing this real + * source. Use `createSourceActors` if you want to respect source maps. + * + * @param Debugger.Source aSource + * The source instance to create an actor for. + * @returns SourceActor + */ + createNonSourceMappedActor: function (aSource) { + // Don't use getSourceURL because we don't want to consider the + // displayURL property if it's an eval source. We only want to + // consider real URLs, otherwise if there is a URL but it's + // invalid the code below will not set the content type, and we + // will later try to fetch the contents of the URL to figure out + // the content type, but it's a made up URL for eval sources. + let url = isEvalSource(aSource) ? null : aSource.url; + let spec = { source: aSource }; + + // XXX bug 915433: We can't rely on Debugger.Source.prototype.text + // if the source is an HTML-embedded <script> tag. Since we don't + // have an API implemented to detect whether this is the case, we + // need to be conservative and only treat valid js files as real + // sources. Otherwise, use the `originalUrl` property to treat it + // as an HTML source that manages multiple inline sources. + + // Assume the source is inline if the element that introduced it is not a + // script element, or does not have a src attribute. + let element = aSource.element ? aSource.element.unsafeDereference() : null; + if (element && (element.tagName !== "SCRIPT" || !element.hasAttribute("src"))) { + spec.isInlineSource = true; + } else if (aSource.introductionType === "wasm") { + // Wasm sources are not JavaScript. Give them their own content-type. + spec.contentType = "text/wasm"; + } else { + if (url) { + // There are a few special URLs that we know are JavaScript: + // inline `javascript:` and code coming from the console + if (url.indexOf("Scratchpad/") === 0 || + url.indexOf("javascript:") === 0 || + url === "debugger eval code") { + spec.contentType = "text/javascript"; + } else { + try { + let pathname = new URL(url).pathname; + let filename = pathname.slice(pathname.lastIndexOf("/") + 1); + let index = filename.lastIndexOf("."); + let extension = index >= 0 ? filename.slice(index + 1) : ""; + if (extension === "xml") { + // XUL inline scripts may not correctly have the + // `source.element` property, so do a blunt check here if + // it's an xml page. + spec.isInlineSource = true; + } + else if (extension === "js") { + spec.contentType = "text/javascript"; + } + } catch (e) { + // This only needs to be here because URL is not yet exposed to + // workers. (BUG 1258892) + const filename = url; + const index = filename.lastIndexOf("."); + const extension = index >= 0 ? filename.slice(index + 1) : ""; + if (extension === "js") { + spec.contentType = "text/javascript"; + } + } + } + } + else { + // Assume the content is javascript if there's no URL + spec.contentType = "text/javascript"; + } + } + + return this.source(spec); + }, + + /** + * This is an internal function that returns a promise of an array + * of source actors representing all the source mapped sources of + * `aSource`, or `null` if the source is not sourcemapped or + * sourcemapping is disabled. Users should call `createSourceActors` + * instead of this. + * + * @param Debugger.Source aSource + * The source instance to create actors for. + * @return Promise of an array of source actors + */ + _createSourceMappedActors: function (aSource) { + if (!this._useSourceMaps || !aSource.sourceMapURL) { + return resolve(null); + } + + return this.fetchSourceMap(aSource) + .then(map => { + if (map) { + return map.sources.map(s => { + return this.source({ originalUrl: s, generatedSource: aSource }); + }).filter(isNotNull); + } + return null; + }); + }, + + /** + * Creates the source actors representing the appropriate sources + * of `aSource`. If sourcemapped, returns actors for all of the original + * sources, otherwise returns a 1-element array with the actor for + * `aSource`. + * + * @param Debugger.Source aSource + * The source instance to create actors for. + * @param Promise of an array of source actors + */ + createSourceActors: function (aSource) { + return this._createSourceMappedActors(aSource).then(actors => { + let actor = this.createNonSourceMappedActor(aSource); + return (actors || [actor]).filter(isNotNull); + }); + }, + + /** + * Return a promise of a SourceMapConsumer for the source map for + * `aSource`; if we already have such a promise extant, return that. + * This will fetch the source map if we don't have a cached object + * and source maps are enabled (see `_fetchSourceMap`). + * + * @param Debugger.Source aSource + * The source instance to get sourcemaps for. + * @return Promise of a SourceMapConsumer + */ + fetchSourceMap: function (aSource) { + if (!this._useSourceMaps) { + return resolve(null); + } + else if (this._sourceMaps.has(aSource)) { + return this._sourceMaps.get(aSource); + } + else if (!aSource || !aSource.sourceMapURL) { + return resolve(null); + } + + let sourceMapURL = aSource.sourceMapURL; + if (aSource.url) { + sourceMapURL = joinURI(aSource.url, sourceMapURL); + } + let result = this._fetchSourceMap(sourceMapURL, aSource.url); + + // The promises in `_sourceMaps` must be the exact same instances + // as returned by `_fetchSourceMap` for `clearSourceMapCache` to + // work. + this._sourceMaps.set(aSource, result); + return result; + }, + + /** + * Return a promise of a SourceMapConsumer for the source map for + * `aSource`. The resolved result may be null if the source does not + * have a source map or source maps are disabled. + */ + getSourceMap: function (aSource) { + return resolve(this._sourceMaps.get(aSource)); + }, + + /** + * Set a SourceMapConsumer for the source map for + * |aSource|. + */ + setSourceMap: function (aSource, aMap) { + this._sourceMaps.set(aSource, resolve(aMap)); + }, + + /** + * Return a promise of a SourceMapConsumer for the source map located at + * |aAbsSourceMapURL|, which must be absolute. If there is already such a + * promise extant, return it. This will not fetch if source maps are + * disabled. + * + * @param string aAbsSourceMapURL + * The source map URL, in absolute form, not relative. + * @param string aScriptURL + * When the source map URL is a data URI, there is no sourceRoot on the + * source map, and the source map's sources are relative, we resolve + * them from aScriptURL. + */ + _fetchSourceMap: function (aAbsSourceMapURL, aSourceURL) { + assert(this._useSourceMaps, + "Cannot fetch sourcemaps if they are disabled"); + + if (this._sourceMapCache[aAbsSourceMapURL]) { + return this._sourceMapCache[aAbsSourceMapURL]; + } + + let fetching = fetch(aAbsSourceMapURL, { loadFromCache: false }) + .then(({ content }) => { + let map = new SourceMapConsumer(content); + this._setSourceMapRoot(map, aAbsSourceMapURL, aSourceURL); + return map; + }) + .then(null, error => { + if (!DevToolsUtils.reportingDisabled) { + DevToolsUtils.reportException("TabSources.prototype._fetchSourceMap", error); + } + return null; + }); + this._sourceMapCache[aAbsSourceMapURL] = fetching; + return fetching; + }, + + /** + * Sets the source map's sourceRoot to be relative to the source map url. + */ + _setSourceMapRoot: function (aSourceMap, aAbsSourceMapURL, aScriptURL) { + // No need to do this fiddling if we won't be fetching any sources over the + // wire. + if (aSourceMap.hasContentsOfAllSources()) { + return; + } + + const base = this._dirname( + aAbsSourceMapURL.indexOf("data:") === 0 + ? aScriptURL + : aAbsSourceMapURL); + aSourceMap.sourceRoot = aSourceMap.sourceRoot + ? joinURI(base, aSourceMap.sourceRoot) + : base; + }, + + _dirname: function (aPath) { + let url = new URL(aPath); + let href = url.href; + return href.slice(0, href.lastIndexOf("/")); + }, + + /** + * Clears the source map cache. Source maps are cached by URL so + * they can be reused across separate Debugger instances (once in + * this cache, they will never be reparsed again). They are + * also cached by Debugger.Source objects for usefulness. By default + * this just removes the Debugger.Source cache, but you can remove + * the lower-level URL cache with the `hard` option. + * + * @param aSourceMapURL string + * The source map URL to uncache + * @param opts object + * An object with the following properties: + * - hard: Also remove the lower-level URL cache, which will + * make us completely forget about the source map. + */ + clearSourceMapCache: function (aSourceMapURL, opts = { hard: false }) { + let oldSm = this._sourceMapCache[aSourceMapURL]; + + if (opts.hard) { + delete this._sourceMapCache[aSourceMapURL]; + } + + if (oldSm) { + // Clear out the current cache so all sources will get the new one + for (let [source, sm] of this._sourceMaps.entries()) { + if (sm === oldSm) { + this._sourceMaps.delete(source); + } + } + } + }, + + /* + * Forcefully change the source map of a source, changing the + * sourceMapURL and installing the source map in the cache. This is + * necessary to expose changes across Debugger instances + * (pretty-printing is the use case). Generate a random url if one + * isn't specified, allowing you to set "anonymous" source maps. + * + * @param aSource Debugger.Source + * The source to change the sourceMapURL property + * @param aUrl string + * The source map URL (optional) + * @param aMap SourceMapConsumer + * The source map instance + */ + setSourceMapHard: function (aSource, aUrl, aMap) { + let url = aUrl; + if (!url) { + // This is a littly hacky, but we want to forcefully set a + // sourcemap regardless of sourcemap settings. We want to + // literally change the sourceMapURL so that all debuggers will + // get this and pretty-printing will Just Work (Debugger.Source + // instances are per-debugger, so we can't key off that). To + // avoid tons of work serializing the sourcemap into a data url, + // just make a fake URL and stick the sourcemap there. + url = "internal://sourcemap" + (this._anonSourceMapId++) + "/"; + } + aSource.sourceMapURL = url; + + // Forcefully set the sourcemap cache. This will be used even if + // sourcemaps are disabled. + this._sourceMapCache[url] = resolve(aMap); + this.emit("updatedSource", this.getSourceActor(aSource)); + }, + + /** + * Return the non-source-mapped location of the given Debugger.Frame. If the + * frame does not have a script, the location's properties are all null. + * + * @param Debugger.Frame aFrame + * The frame whose location we are getting. + * @returns Object + * Returns an object of the form { source, line, column } + */ + getFrameLocation: function (aFrame) { + if (!aFrame || !aFrame.script) { + return new GeneratedLocation(); + } + let {lineNumber, columnNumber} = + aFrame.script.getOffsetLocation(aFrame.offset); + return new GeneratedLocation( + this.createNonSourceMappedActor(aFrame.script.source), + lineNumber, + columnNumber + ); + }, + + /** + * Returns a promise of the location in the original source if the source is + * source mapped, otherwise a promise of the same location. This can + * be called with a source from *any* Debugger instance and we make + * sure to that it works properly, reusing source maps if already + * fetched. Use this from any actor that needs sourcemapping. + */ + getOriginalLocation: function (generatedLocation) { + let { + generatedSourceActor, + generatedLine, + generatedColumn + } = generatedLocation; + let source = generatedSourceActor.source; + let url = source ? source.url : generatedSourceActor._originalUrl; + + // In certain scenarios the source map may have not been fetched + // yet (or at least tied to this Debugger.Source instance), so use + // `fetchSourceMap` instead of `getSourceMap`. This allows this + // function to be called from anywere (across debuggers) and it + // should just automatically work. + return this.fetchSourceMap(source).then(map => { + if (map) { + let { + source: originalUrl, + line: originalLine, + column: originalColumn, + name: originalName + } = map.originalPositionFor({ + line: generatedLine, + column: generatedColumn == null ? Infinity : generatedColumn + }); + + // Since the `Debugger.Source` instance may come from a + // different `Debugger` instance (any actor can call this + // method), we can't rely on any of the source discovery + // setup (`_discoverSources`, etc) to have been run yet. So + // we have to assume that the actor may not already exist, + // and we might need to create it, so use `source` and give + // it the required parameters for a sourcemapped source. + return new OriginalLocation( + originalUrl ? this.source({ + originalUrl: originalUrl, + generatedSource: source + }) : null, + originalLine, + originalColumn, + originalName + ); + } + + // No source map + return OriginalLocation.fromGeneratedLocation(generatedLocation); + }); + }, + + getAllGeneratedLocations: function (originalLocation) { + let { + originalSourceActor, + originalLine, + originalColumn + } = originalLocation; + + let source = (originalSourceActor.source || + originalSourceActor.generatedSource); + + return this.fetchSourceMap(source).then((map) => { + if (map) { + map.computeColumnSpans(); + + return map.allGeneratedPositionsFor({ + source: originalSourceActor.url, + line: originalLine, + column: originalColumn + }).map(({ line, column, lastColumn }) => { + return new GeneratedLocation( + this.createNonSourceMappedActor(source), + line, + column, + lastColumn + ); + }); + } + + return [GeneratedLocation.fromOriginalLocation(originalLocation)]; + }); + }, + + + /** + * Returns a promise of the location in the generated source corresponding to + * the original source and line given. + * + * When we pass a script S representing generated code to `sourceMap`, + * above, that returns a promise P. The process of resolving P populates + * the tables this function uses; thus, it won't know that S's original + * source URLs map to S until P is resolved. + */ + getGeneratedLocation: function (originalLocation) { + let { originalSourceActor } = originalLocation; + + // Both original sources and normal sources could have sourcemaps, + // because normal sources can be pretty-printed which generates a + // sourcemap for itself. Check both of the source properties to make it work + // for both kinds of sources. + let source = originalSourceActor.source || originalSourceActor.generatedSource; + + // See comment about `fetchSourceMap` in `getOriginalLocation`. + return this.fetchSourceMap(source).then((map) => { + if (map) { + let { + originalLine, + originalColumn + } = originalLocation; + + let { + line: generatedLine, + column: generatedColumn + } = map.generatedPositionFor({ + source: originalSourceActor.url, + line: originalLine, + column: originalColumn == null ? 0 : originalColumn, + bias: SourceMapConsumer.LEAST_UPPER_BOUND + }); + + return new GeneratedLocation( + this.createNonSourceMappedActor(source), + generatedLine, + generatedColumn + ); + } + + return GeneratedLocation.fromOriginalLocation(originalLocation); + }); + }, + + /** + * Returns true if URL for the given source is black boxed. + * + * @param aURL String + * The URL of the source which we are checking whether it is black + * boxed or not. + */ + isBlackBoxed: function (aURL) { + return this.blackBoxedSources.has(aURL); + }, + + /** + * Add the given source URL to the set of sources that are black boxed. + * + * @param aURL String + * The URL of the source which we are black boxing. + */ + blackBox: function (aURL) { + this.blackBoxedSources.add(aURL); + }, + + /** + * Remove the given source URL to the set of sources that are black boxed. + * + * @param aURL String + * The URL of the source which we are no longer black boxing. + */ + unblackBox: function (aURL) { + this.blackBoxedSources.delete(aURL); + }, + + /** + * Returns true if the given URL is pretty printed. + * + * @param aURL String + * The URL of the source that might be pretty printed. + */ + isPrettyPrinted: function (aURL) { + return this.prettyPrintedSources.has(aURL); + }, + + /** + * Add the given URL to the set of sources that are pretty printed. + * + * @param aURL String + * The URL of the source to be pretty printed. + */ + prettyPrint: function (aURL, aIndent) { + this.prettyPrintedSources.set(aURL, aIndent); + }, + + /** + * Return the indent the given URL was pretty printed by. + */ + prettyPrintIndent: function (aURL) { + return this.prettyPrintedSources.get(aURL); + }, + + /** + * Remove the given URL from the set of sources that are pretty printed. + * + * @param aURL String + * The URL of the source that is no longer pretty printed. + */ + disablePrettyPrint: function (aURL) { + this.prettyPrintedSources.delete(aURL); + }, + + iter: function () { + let actors = Object.keys(this._sourceMappedSourceActors).map(k => { + return this._sourceMappedSourceActors[k]; + }); + for (let actor of this._sourceActors.values()) { + if (!this._sourceMaps.has(actor.source)) { + actors.push(actor); + } + } + return actors; + } +}; + +/* + * Checks if a source should never be displayed to the user because + * it's either internal or we don't support in the UI yet. + */ +function isHiddenSource(aSource) { + // Ignore the internal Function.prototype script + return aSource.text === "() {\n}"; +} + +/** + * Returns true if its argument is not null. + */ +function isNotNull(aThing) { + return aThing !== null; +} + +exports.TabSources = TabSources; +exports.isHiddenSource = isHiddenSource; |