mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-06 07:48:38 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
833
devtools/server/actors/utils/TabSources.js
Normal file
833
devtools/server/actors/utils/TabSources.js
Normal file
|
|
@ -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;
|
||||
Loading…
Add table
Add a link
Reference in a new issue