зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset 4d61e1b695c9 (bug 1513655) for ESlint failure on devtools/server/actors/thread.js. CLOSED TREE
This commit is contained in:
Родитель
432ea8fcdc
Коммит
f292d9f90e
|
@ -38,10 +38,11 @@ function handleThreadState(toolbox, event, packet) {
|
|||
function attachThread(toolbox) {
|
||||
const target = toolbox.target;
|
||||
|
||||
const useSourceMaps = false;
|
||||
const autoBlackBox = false;
|
||||
const ignoreFrameEnvironment = true;
|
||||
|
||||
const threadOptions = { autoBlackBox, ignoreFrameEnvironment };
|
||||
const threadOptions = { useSourceMaps, autoBlackBox, ignoreFrameEnvironment };
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const handleResponse = ([res, threadClient]) => {
|
||||
|
|
|
@ -281,118 +281,192 @@ const SourceActor = ActorClassWithSpec(sourceSpec, {
|
|||
}
|
||||
},
|
||||
|
||||
_reportLoadSourceError: function(error) {
|
||||
_reportLoadSourceError: function(error, map = null) {
|
||||
try {
|
||||
DevToolsUtils.reportException("SourceActor", error);
|
||||
|
||||
JSON.stringify(this.form(), null, 4).split(/\n/g)
|
||||
.forEach(line => console.error("\t", line));
|
||||
|
||||
if (!map) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.error("\t", "source map's sourceRoot =", map.sourceRoot);
|
||||
|
||||
console.error("\t", "source map's sources =");
|
||||
map.sources.forEach(s => {
|
||||
const hasSourceContent = map.sourceContentFor(s, true);
|
||||
console.error("\t\t", s, "\t",
|
||||
hasSourceContent ? "has source content" : "no source content");
|
||||
});
|
||||
|
||||
console.error("\t", "source map's sourcesContent =");
|
||||
map.sourcesContent.forEach(c => {
|
||||
if (c.length > 80) {
|
||||
c = c.slice(0, 77) + "...";
|
||||
}
|
||||
c = c.replace(/\n/g, "\\n");
|
||||
console.error("\t\t", c);
|
||||
});
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
},
|
||||
|
||||
_getSourceText: async function() {
|
||||
_getSourceText: function() {
|
||||
const toResolvedContent = t => ({
|
||||
content: t,
|
||||
contentType: this._contentType,
|
||||
});
|
||||
const isWasm = this.source && this.source.introductionType === "wasm";
|
||||
|
||||
if (isWasm) {
|
||||
const wasm = this.source.binary;
|
||||
const buffer = wasm.buffer;
|
||||
assert(
|
||||
wasm.byteOffset === 0 && wasm.byteLength === buffer.byteLength,
|
||||
"Typed array from wasm source binary must cover entire buffer"
|
||||
);
|
||||
return toResolvedContent(buffer);
|
||||
}
|
||||
|
||||
// If we are replaying then we can only use source saved during the
|
||||
// original recording. If we try to fetch it now it may have changed or
|
||||
// may no longer exist.
|
||||
if (this.dbg.replaying) {
|
||||
assert(!this._contentType);
|
||||
return this.dbg.replayingContent(this.url);
|
||||
}
|
||||
|
||||
// Use `source.text` if it exists, is not the "no source" string, and
|
||||
// the content type of the source is JavaScript or it is synthesized
|
||||
// wasm. It will be "no source" if the Debugger API wasn't able to load
|
||||
// the source because sources were discarded
|
||||
// (javascript.options.discardSystemSource == true). Re-fetch non-JS
|
||||
// sources to get the contentType from the headers.
|
||||
if (this.source &&
|
||||
this.source.text !== "[no source]" &&
|
||||
this._contentType &&
|
||||
(this._contentType.includes("javascript") ||
|
||||
this._contentType === "text/wasm")) {
|
||||
return toResolvedContent(this.source.text);
|
||||
}
|
||||
|
||||
// Only load the HTML page source from cache (which exists when
|
||||
// there are inline sources). Otherwise, we can't trust the
|
||||
// cache because we are most likely here because we are
|
||||
// fetching the original text for sourcemapped code, and the
|
||||
// page hasn't requested it before (if it has, it was a
|
||||
// previous debugging session).
|
||||
// Additionally, we should only try the cache if it is currently enabled
|
||||
// for the document. Without this check, the cache may return stale data
|
||||
// that doesn't match the document shown in the browser.
|
||||
const loadFromCache = this.isInlineSource && this.isCacheEnabled;
|
||||
|
||||
// Fetch the sources with the same principal as the original document
|
||||
const win = this.threadActor._parent.window;
|
||||
let principal, cacheKey;
|
||||
// On xpcshell, we don't have a window but a Sandbox
|
||||
if (!isWorker && win instanceof Ci.nsIDOMWindow) {
|
||||
const docShell = win.docShell;
|
||||
const channel = docShell.currentDocumentChannel;
|
||||
principal = channel.loadInfo.loadingPrincipal;
|
||||
|
||||
// Retrieve the cacheKey in order to load POST requests from cache
|
||||
// Note that chrome:// URLs don't support this interface.
|
||||
if (loadFromCache &&
|
||||
docShell.currentDocumentChannel instanceof Ci.nsICacheInfoChannel) {
|
||||
cacheKey = docShell.currentDocumentChannel.cacheKey;
|
||||
const genSource = this.generatedSource || this.source;
|
||||
return this.threadActor.sources.fetchSourceMap(genSource).then(map => {
|
||||
if (map) {
|
||||
try {
|
||||
const sourceContent = map.sourceContentFor(this.url);
|
||||
if (sourceContent) {
|
||||
return toResolvedContent(sourceContent);
|
||||
}
|
||||
} catch (error) {
|
||||
this._reportLoadSourceError(error, map);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const sourceFetched = fetch(this.url, {
|
||||
principal,
|
||||
cacheKey,
|
||||
loadFromCache,
|
||||
});
|
||||
if (isWasm) {
|
||||
const wasm = this.source.binary;
|
||||
const buffer = wasm.buffer;
|
||||
assert(
|
||||
wasm.byteOffset === 0 && wasm.byteLength === buffer.byteLength,
|
||||
"Typed array from wasm source binary must cover entire buffer"
|
||||
);
|
||||
return toResolvedContent(buffer);
|
||||
}
|
||||
|
||||
// Record the contentType we just learned during fetching
|
||||
return sourceFetched
|
||||
.then(result => {
|
||||
this._contentType = result.contentType;
|
||||
return result;
|
||||
}, error => {
|
||||
this._reportLoadSourceError(error);
|
||||
throw error;
|
||||
// If we are replaying then we can only use source saved during the
|
||||
// original recording. If we try to fetch it now it may have changed or
|
||||
// may no longer exist.
|
||||
if (this.dbg.replaying) {
|
||||
assert(!this._contentType);
|
||||
return this.dbg.replayingContent(this.url);
|
||||
}
|
||||
|
||||
// Use `source.text` if it exists, is not the "no source" string, and
|
||||
// the content type of the source is JavaScript or it is synthesized
|
||||
// wasm. It will be "no source" if the Debugger API wasn't able to load
|
||||
// the source because sources were discarded
|
||||
// (javascript.options.discardSystemSource == true). Re-fetch non-JS
|
||||
// sources to get the contentType from the headers.
|
||||
if (this.source &&
|
||||
this.source.text !== "[no source]" &&
|
||||
this._contentType &&
|
||||
(this._contentType.includes("javascript") ||
|
||||
this._contentType === "text/wasm")) {
|
||||
return toResolvedContent(this.source.text);
|
||||
}
|
||||
|
||||
// Only load the HTML page source from cache (which exists when
|
||||
// there are inline sources). Otherwise, we can't trust the
|
||||
// cache because we are most likely here because we are
|
||||
// fetching the original text for sourcemapped code, and the
|
||||
// page hasn't requested it before (if it has, it was a
|
||||
// previous debugging session).
|
||||
// Additionally, we should only try the cache if it is currently enabled
|
||||
// for the document. Without this check, the cache may return stale data
|
||||
// that doesn't match the document shown in the browser.
|
||||
const loadFromCache = this.isInlineSource && this.isCacheEnabled;
|
||||
|
||||
// Fetch the sources with the same principal as the original document
|
||||
const win = this.threadActor._parent.window;
|
||||
let principal, cacheKey;
|
||||
// On xpcshell, we don't have a window but a Sandbox
|
||||
if (!isWorker && win instanceof Ci.nsIDOMWindow) {
|
||||
const docShell = win.docShell;
|
||||
const channel = docShell.currentDocumentChannel;
|
||||
principal = channel.loadInfo.loadingPrincipal;
|
||||
|
||||
// Retrieve the cacheKey in order to load POST requests from cache
|
||||
// Note that chrome:// URLs don't support this interface.
|
||||
if (loadFromCache &&
|
||||
docShell.currentDocumentChannel instanceof Ci.nsICacheInfoChannel) {
|
||||
cacheKey = docShell.currentDocumentChannel.cacheKey;
|
||||
}
|
||||
}
|
||||
|
||||
const sourceFetched = fetch(this.url, {
|
||||
principal,
|
||||
cacheKey,
|
||||
loadFromCache,
|
||||
});
|
||||
|
||||
// Record the contentType we just learned during fetching
|
||||
return sourceFetched
|
||||
.then(result => {
|
||||
this._contentType = result.contentType;
|
||||
return result;
|
||||
}, error => {
|
||||
this._reportLoadSourceError(error, map);
|
||||
throw error;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Get all executable lines from the current source
|
||||
* @return Array - Executable lines of the current script
|
||||
*/
|
||||
getExecutableLines: async function() {
|
||||
const offsetsLines = new Set();
|
||||
for (const s of this.dbg.findScripts({ source: this.source })) {
|
||||
**/
|
||||
getExecutableLines: function() {
|
||||
function sortLines(lines) {
|
||||
// Converting the Set into an array
|
||||
lines = [...lines];
|
||||
lines.sort((a, b) => {
|
||||
return a - b;
|
||||
});
|
||||
return lines;
|
||||
}
|
||||
|
||||
if (this.generatedSource) {
|
||||
return this.threadActor.sources.getSourceMap(this.generatedSource).then(sm => {
|
||||
const lines = new Set();
|
||||
|
||||
// Position of executable lines in the generated source
|
||||
const offsets = this.getExecutableOffsets(this.generatedSource, false);
|
||||
for (const offset of offsets) {
|
||||
const {line, source: sourceUrl} = sm.originalPositionFor({
|
||||
line: offset.lineNumber,
|
||||
column: offset.columnNumber,
|
||||
});
|
||||
|
||||
if (sourceUrl === this.url) {
|
||||
lines.add(line);
|
||||
}
|
||||
}
|
||||
|
||||
return sortLines(lines);
|
||||
});
|
||||
}
|
||||
|
||||
const lines = this.getExecutableOffsets(this.source, true);
|
||||
return sortLines(lines);
|
||||
},
|
||||
|
||||
/**
|
||||
* Extract all executable offsets from the given script
|
||||
* @param String url - extract offsets of the script with this url
|
||||
* @param Boolean onlyLine - will return only the line number
|
||||
* @return Set - Executable offsets/lines of the script
|
||||
**/
|
||||
getExecutableOffsets: function(source, onlyLine) {
|
||||
const offsets = new Set();
|
||||
for (const s of this.dbg.findScripts({ source })) {
|
||||
for (const offset of s.getAllColumnOffsets()) {
|
||||
offsetsLines.add(offset.lineNumber);
|
||||
offsets.add(onlyLine ? offset.lineNumber : offset);
|
||||
}
|
||||
}
|
||||
|
||||
const lines = [...offsetsLines];
|
||||
lines.sort((a, b) => {
|
||||
return a - b;
|
||||
});
|
||||
return lines;
|
||||
return offsets;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -1274,7 +1274,7 @@ const browsingContextTargetPrototype = {
|
|||
// window-ready
|
||||
const threadActor = this.threadActor;
|
||||
if (isTopLevel && threadActor.state != "detached") {
|
||||
this.sources.reset();
|
||||
this.sources.reset({ sourceMaps: true });
|
||||
threadActor.clearDebuggees();
|
||||
threadActor.dbg.enabled = true;
|
||||
threadActor.maybePauseOnExceptions();
|
||||
|
|
|
@ -67,6 +67,7 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
|
|||
this._observingNetwork = false;
|
||||
|
||||
this._options = {
|
||||
useSourceMaps: false,
|
||||
autoBlackBox: false,
|
||||
};
|
||||
|
||||
|
@ -1954,6 +1955,7 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
|
|||
// ExtensionContent.jsm and ThreadActor would ignore them on a page reload
|
||||
// because it finds them in the _debuggerSourcesSeen WeakSet,
|
||||
// and so we also need to be sure that there is still a source actor for the source.
|
||||
let sourceActor;
|
||||
if (this._debuggerSourcesSeen.has(source) && this.sources.hasSourceActor(source)) {
|
||||
// When replaying, fall through and try to setup breakpoints, even for
|
||||
// sources we have seen before. When replaying, we can have actors for
|
||||
|
@ -1964,31 +1966,72 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
|
|||
if (!this.dbg.replaying) {
|
||||
return false;
|
||||
}
|
||||
sourceActor = this.sources.getSourceActor(source);
|
||||
} else {
|
||||
this.sources.createNonSourceMappedActor(source);
|
||||
sourceActor = this.sources.createNonSourceMappedActor(source);
|
||||
}
|
||||
|
||||
const bpActors = [...this.breakpointActorMap.findActors()];
|
||||
|
||||
// Bug 1225160: If addSource is called in response to a new script
|
||||
// notification, and this notification was triggered by loading a JSM from
|
||||
// chrome code, calling unsafeSynchronize could cause a debuggee timer to
|
||||
// fire. If this causes the JSM to be loaded a second time, the browser
|
||||
// will crash, because loading JSMS is not reentrant, and the first load
|
||||
// has not completed yet.
|
||||
//
|
||||
// The root of the problem is that unsafeSynchronize can cause debuggee
|
||||
// code to run. Unfortunately, fixing that is prohibitively difficult. The
|
||||
// best we can do at the moment is disable source maps for the browser
|
||||
// debugger, and carefully avoid the use of unsafeSynchronize in this
|
||||
// function when source maps are disabled.
|
||||
for (const actor of bpActors) {
|
||||
if (actor.isPending) {
|
||||
actor.originalLocation.originalSourceActor._setBreakpoint(actor);
|
||||
} else {
|
||||
actor.originalLocation.originalSourceActor._setBreakpointAtGeneratedLocation(
|
||||
actor, GeneratedLocation.fromOriginalLocation(actor.originalLocation)
|
||||
);
|
||||
if (this._options.useSourceMaps) {
|
||||
const promises = [];
|
||||
|
||||
// Go ahead and establish the source actors for this script, which
|
||||
// fetches sourcemaps if available and sends onNewSource
|
||||
// notifications.
|
||||
const sourceActorsCreated = this.sources._createSourceMappedActors(source);
|
||||
|
||||
if (bpActors.length) {
|
||||
// We need to use unsafeSynchronize here because if the page is being reloaded,
|
||||
// this call will replace the previous set of source actors for this source
|
||||
// with a new one. If the source actors have not been replaced by the time
|
||||
// we try to reset the breakpoints below, their location objects will still
|
||||
// point to the old set of source actors, which point to different
|
||||
// scripts.
|
||||
this.unsafeSynchronize(sourceActorsCreated);
|
||||
}
|
||||
|
||||
for (const actor of bpActors) {
|
||||
if (actor.isPending) {
|
||||
promises.push(actor.originalLocation.originalSourceActor._setBreakpoint(actor));
|
||||
} else {
|
||||
promises.push(
|
||||
this.sources.getAllGeneratedLocations(actor.originalLocation).then(
|
||||
(generatedLocations) => {
|
||||
if (generatedLocations.length > 0 &&
|
||||
generatedLocations[0].generatedSourceActor
|
||||
.actorID === sourceActor.actorID) {
|
||||
sourceActor._setBreakpointAtAllGeneratedLocations(
|
||||
actor, generatedLocations);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
if (promises.length > 0) {
|
||||
this.unsafeSynchronize(Promise.all(promises));
|
||||
}
|
||||
} else {
|
||||
// Bug 1225160: If addSource is called in response to a new script
|
||||
// notification, and this notification was triggered by loading a JSM from
|
||||
// chrome code, calling unsafeSynchronize could cause a debuggee timer to
|
||||
// fire. If this causes the JSM to be loaded a second time, the browser
|
||||
// will crash, because loading JSMS is not reentrant, and the first load
|
||||
// has not completed yet.
|
||||
//
|
||||
// The root of the problem is that unsafeSynchronize can cause debuggee
|
||||
// code to run. Unfortunately, fixing that is prohibitively difficult. The
|
||||
// best we can do at the moment is disable source maps for the browser
|
||||
// debugger, and carefully avoid the use of unsafeSynchronize in this
|
||||
// function when source maps are disabled.
|
||||
for (const actor of bpActors) {
|
||||
if (actor.isPending) {
|
||||
actor.originalLocation.originalSourceActor._setBreakpoint(actor);
|
||||
} else {
|
||||
actor.originalLocation.originalSourceActor._setBreakpointAtGeneratedLocation(
|
||||
actor, GeneratedLocation.fromOriginalLocation(actor.originalLocation)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,12 +5,15 @@
|
|||
"use strict";
|
||||
|
||||
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
||||
const { assert } = DevToolsUtils;
|
||||
const { assert, fetch } = DevToolsUtils;
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
const { OriginalLocation, GeneratedLocation } = require("devtools/server/actors/common");
|
||||
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, "WasmRemap", "devtools/shared/wasm-source-map", true);
|
||||
|
||||
/**
|
||||
* Manages the sources for a thread. Handles source maps, locations in the
|
||||
|
@ -20,6 +23,7 @@ function TabSources(threadActor, allowSourceFn = () => true) {
|
|||
EventEmitter.decorate(this);
|
||||
|
||||
this._thread = threadActor;
|
||||
this._useSourceMaps = true;
|
||||
this._autoBlackBox = true;
|
||||
this.allowSource = source => {
|
||||
return !isHiddenSource(source) && allowSourceFn(source);
|
||||
|
@ -28,10 +32,14 @@ function TabSources(threadActor, allowSourceFn = () => true) {
|
|||
this.blackBoxedSources = new Set();
|
||||
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._htmlDocumentSourceActors = Object.create(null);
|
||||
this._sourceMappedSourceActors = Object.create(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -48,6 +56,11 @@ TabSources.prototype = {
|
|||
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;
|
||||
|
@ -60,10 +73,19 @@ TabSources.prototype = {
|
|||
|
||||
/**
|
||||
* 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() {
|
||||
reset: function(opts = {}) {
|
||||
this._sourceActors = new Map();
|
||||
this._htmlDocumentSourceActors = Object.create(null);
|
||||
this._sourceMaps = new Map();
|
||||
this._sourceMappedSourceActors = Object.create(null);
|
||||
|
||||
if (opts.sourceMaps) {
|
||||
this._sourceMapCache = Object.create(null);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -73,46 +95,68 @@ TabSources.prototype = {
|
|||
*
|
||||
* @param Debugger.Source source
|
||||
* The source to make an actor for
|
||||
* @param boolean isInlineSource
|
||||
* True if this source is an inline HTML source, and should thus be
|
||||
* treated as a subsection of a larger source file.
|
||||
* @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, isInlineSource, contentType }) {
|
||||
assert(source,
|
||||
"TabSources.prototype.source needs a source");
|
||||
source: function({ source, originalUrl, generatedSource,
|
||||
isInlineSource, contentType }) {
|
||||
assert(source || (originalUrl && generatedSource),
|
||||
"TabSources.prototype.source needs an originalUrl or a source");
|
||||
|
||||
if (!this.allowSource(source)) {
|
||||
return null;
|
||||
}
|
||||
if (source) {
|
||||
// If a source is passed, we are creating an actor for a real
|
||||
// source, which may or may not be sourcemapped.
|
||||
|
||||
// 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._htmlDocumentSourceActors) {
|
||||
return this._htmlDocumentSourceActors[source.url];
|
||||
}
|
||||
if (!this.allowSource(source)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let originalUrl = null;
|
||||
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);
|
||||
// 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 (const [sourceData, actor] of this._sourceActors) {
|
||||
if (sourceData.url === originalUrl) {
|
||||
return actor;
|
||||
}
|
||||
}
|
||||
|
||||
if (originalUrl in this._sourceMappedSourceActors) {
|
||||
return this._sourceMappedSourceActors[originalUrl];
|
||||
}
|
||||
}
|
||||
|
||||
const actor = new SourceActor({
|
||||
thread: this._thread,
|
||||
source: source,
|
||||
originalUrl: originalUrl,
|
||||
generatedSource: generatedSource,
|
||||
isInlineSource: isInlineSource,
|
||||
contentType: contentType,
|
||||
});
|
||||
|
@ -136,16 +180,39 @@ TabSources.prototype = {
|
|||
if (source) {
|
||||
this._sourceActors.set(source, actor);
|
||||
} else {
|
||||
this._htmlDocumentSourceActors[originalUrl] = actor;
|
||||
this._sourceMappedSourceActors[originalUrl] = actor;
|
||||
}
|
||||
|
||||
this.emit("newSource", 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._htmlDocumentSourceActors) {
|
||||
return this._htmlDocumentSourceActors[source.url];
|
||||
if (source.url in this._sourceMappedSourceActors) {
|
||||
return this._sourceMappedSourceActors[source.url];
|
||||
}
|
||||
|
||||
if (this._sourceActors.has(source)) {
|
||||
|
@ -178,8 +245,8 @@ TabSources.prototype = {
|
|||
}
|
||||
}
|
||||
|
||||
if (url in this._htmlDocumentSourceActors) {
|
||||
return this._htmlDocumentSourceActors[url];
|
||||
if (url in this._sourceMappedSourceActors) {
|
||||
return this._sourceMappedSourceActors[url];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -288,6 +355,33 @@ TabSources.prototype = {
|
|||
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
|
||||
* `source`, or `null` if the source is not sourcemapped or
|
||||
* sourcemapping is disabled. Users should call `createSourceActors`
|
||||
* instead of this.
|
||||
*
|
||||
* @param Debugger.Source source
|
||||
* The source instance to create actors for.
|
||||
* @return Promise of an array of source actors
|
||||
*/
|
||||
_createSourceMappedActors: function(source) {
|
||||
if (!this._useSourceMaps || !source.sourceMapURL) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
return this.fetchSourceMap(source)
|
||||
.then(map => {
|
||||
if (map) {
|
||||
return map.sources.map(s => {
|
||||
return this.source({ originalUrl: s, generatedSource: source });
|
||||
}).filter(isNotNull);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates the source actors representing the appropriate sources
|
||||
* of `source`. If sourcemapped, returns actors for all of the original
|
||||
|
@ -298,9 +392,145 @@ TabSources.prototype = {
|
|||
* The source instance to create actors for.
|
||||
* @param Promise of an array of source actors
|
||||
*/
|
||||
createSourceActors: async function(source) {
|
||||
const actor = this.createNonSourceMappedActor(source);
|
||||
return actor ? [actor] : [];
|
||||
createSourceActors: function(source) {
|
||||
return this._createSourceMappedActors(source).then(actors => {
|
||||
const actor = this.createNonSourceMappedActor(source);
|
||||
return (actors || [actor]).filter(isNotNull);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Return a promise of a SourceMapConsumer for the source map for
|
||||
* `source`; 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 source
|
||||
* The source instance to get sourcemaps for.
|
||||
* @return Promise of a SourceMapConsumer
|
||||
*/
|
||||
fetchSourceMap: function(source) {
|
||||
if (!this._useSourceMaps) {
|
||||
return Promise.resolve(null);
|
||||
} else if (this._sourceMaps.has(source)) {
|
||||
return this._sourceMaps.get(source);
|
||||
} else if (!source || !source.sourceMapURL) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
let sourceMapURL = source.sourceMapURL;
|
||||
if (source.url) {
|
||||
sourceMapURL = joinURI(source.url, sourceMapURL);
|
||||
}
|
||||
let result = this._fetchSourceMap(sourceMapURL, source.url);
|
||||
|
||||
const isWasm = source.introductionType == "wasm";
|
||||
if (isWasm) {
|
||||
result = result.then((map) => new WasmRemap(map));
|
||||
}
|
||||
|
||||
// The promises in `_sourceMaps` must be the exact same instances
|
||||
// as returned by `_fetchSourceMap` for `clearSourceMapCache` to
|
||||
// work.
|
||||
this._sourceMaps.set(source, result);
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return a promise of a SourceMapConsumer for the source map for
|
||||
* `source`. The resolved result may be null if the source does not
|
||||
* have a source map or source maps are disabled.
|
||||
*/
|
||||
getSourceMap: function(source) {
|
||||
return Promise.resolve(this._sourceMaps.get(source));
|
||||
},
|
||||
|
||||
/**
|
||||
* Set a SourceMapConsumer for the source map for |source|.
|
||||
*/
|
||||
setSourceMap: function(source, map) {
|
||||
this._sourceMaps.set(source, Promise.resolve(map));
|
||||
},
|
||||
|
||||
/**
|
||||
* Return a promise of a SourceMapConsumer for the source map located at
|
||||
* |absSourceMapURL|, 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 absSourceMapURL
|
||||
* The source map URL, in absolute form, not relative.
|
||||
* @param string sourceURL
|
||||
* 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 sourceURL.
|
||||
*/
|
||||
_fetchSourceMap: function(absSourceMapURL, sourceURL) {
|
||||
assert(this._useSourceMaps,
|
||||
"Cannot fetch sourcemaps if they are disabled");
|
||||
|
||||
if (this._sourceMapCache[absSourceMapURL]) {
|
||||
return this._sourceMapCache[absSourceMapURL];
|
||||
}
|
||||
|
||||
const fetching = fetch(absSourceMapURL, { loadFromCache: false })
|
||||
.then(({ content }) => {
|
||||
return new SourceMapConsumer(content,
|
||||
this._getSourceMapRoot(absSourceMapURL, sourceURL));
|
||||
})
|
||||
.catch(error => {
|
||||
if (!DevToolsUtils.reportingDisabled) {
|
||||
DevToolsUtils.reportException("TabSources.prototype._fetchSourceMap", error);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
this._sourceMapCache[absSourceMapURL] = fetching;
|
||||
return fetching;
|
||||
},
|
||||
|
||||
/**
|
||||
* Compute the URL to pass to the SourceMapConsumer constructor as
|
||||
* the "source map's URL".
|
||||
*/
|
||||
_getSourceMapRoot: function(absSourceMapURL, scriptURL) {
|
||||
// Pass in the source map URL; except if it is a data: URL, fall
|
||||
// back to using the source's URL, if possible.
|
||||
if (scriptURL && absSourceMapURL.startsWith("data:")) {
|
||||
return scriptURL;
|
||||
}
|
||||
return absSourceMapURL;
|
||||
},
|
||||
|
||||
/**
|
||||
* 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 sourceMapURL 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(sourceMapURL, opts = { hard: false }) {
|
||||
const oldSm = this._sourceMapCache[sourceMapURL];
|
||||
|
||||
if (opts.hard) {
|
||||
delete this._sourceMapCache[sourceMapURL];
|
||||
}
|
||||
|
||||
if (oldSm) {
|
||||
// Clear out the current cache so all sources will get the new one
|
||||
for (const [source, sm] of this._sourceMaps.entries()) {
|
||||
if (sm === oldSm) {
|
||||
this._sourceMaps.delete(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -345,12 +575,84 @@ TabSources.prototype = {
|
|||
* sure to that it works properly, reusing source maps if already
|
||||
* fetched. Use this from any actor that needs sourcemapping.
|
||||
*/
|
||||
getOriginalLocation: async function(generatedLocation) {
|
||||
return OriginalLocation.fromGeneratedLocation(generatedLocation);
|
||||
getOriginalLocation: function(generatedLocation) {
|
||||
const {
|
||||
generatedSourceActor,
|
||||
generatedLine,
|
||||
generatedColumn,
|
||||
} = generatedLocation;
|
||||
const source = generatedSourceActor.source;
|
||||
|
||||
// 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) {
|
||||
const {
|
||||
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: async function(originalLocation) {
|
||||
return [GeneratedLocation.fromOriginalLocation(originalLocation)];
|
||||
getAllGeneratedLocations: function(originalLocation) {
|
||||
const {
|
||||
originalSourceActor,
|
||||
originalLine,
|
||||
originalColumn,
|
||||
} = originalLocation;
|
||||
|
||||
const 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)];
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -385,11 +687,13 @@ TabSources.prototype = {
|
|||
},
|
||||
|
||||
iter: function() {
|
||||
const actors = Object.keys(this._htmlDocumentSourceActors).map(k => {
|
||||
return this._htmlDocumentSourceActors[k];
|
||||
const actors = Object.keys(this._sourceMappedSourceActors).map(k => {
|
||||
return this._sourceMappedSourceActors[k];
|
||||
});
|
||||
for (const actor of this._sourceActors.values()) {
|
||||
actors.push(actor);
|
||||
if (!this._sourceMaps.has(actor.source)) {
|
||||
actors.push(actor);
|
||||
}
|
||||
}
|
||||
return actors;
|
||||
},
|
||||
|
@ -403,5 +707,12 @@ function isHiddenSource(source) {
|
|||
return source.introductionType === "Function.prototype";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if its argument is not null.
|
||||
*/
|
||||
function isNotNull(thing) {
|
||||
return thing !== null;
|
||||
}
|
||||
|
||||
exports.TabSources = TabSources;
|
||||
exports.isHiddenSource = isHiddenSource;
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
||||
"use strict";
|
||||
|
||||
var add = require("./lib/add");
|
||||
var subtract = require("./lib/subtract");
|
||||
var upperCase = require("upper-case");
|
||||
|
||||
(function () {
|
||||
return add(1, 2);
|
||||
});
|
||||
|
||||
},{"./lib/add":2,"./lib/subtract":3,"upper-case":4}],2:[function(require,module,exports){
|
||||
"use strict";
|
||||
|
||||
module.exports = function (a, b) {
|
||||
return a + b;
|
||||
};
|
||||
|
||||
},{}],3:[function(require,module,exports){
|
||||
"use strict";
|
||||
|
||||
module.exports = function (a, b) {
|
||||
return a - b;
|
||||
};
|
||||
|
||||
},{}],4:[function(require,module,exports){
|
||||
/**
|
||||
* Special language-specific overrides.
|
||||
*
|
||||
* Source: ftp://ftp.unicode.org/Public/UCD/latest/ucd/SpecialCasing.txt
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
var LANGUAGES = {
|
||||
tr: {
|
||||
regexp: /[\u0069]/g,
|
||||
map: {
|
||||
'\u0069': '\u0130'
|
||||
}
|
||||
},
|
||||
az: {
|
||||
regexp: /[\u0069]/g,
|
||||
map: {
|
||||
'\u0069': '\u0130'
|
||||
}
|
||||
},
|
||||
lt: {
|
||||
regexp: /[\u0069\u006A\u012F]\u0307|\u0069\u0307[\u0300\u0301\u0303]/g,
|
||||
map: {
|
||||
'\u0069\u0307': '\u0049',
|
||||
'\u006A\u0307': '\u004A',
|
||||
'\u012F\u0307': '\u012E',
|
||||
'\u0069\u0307\u0300': '\u00CC',
|
||||
'\u0069\u0307\u0301': '\u00CD',
|
||||
'\u0069\u0307\u0303': '\u0128'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upper case a string.
|
||||
*
|
||||
* @param {String} str
|
||||
* @return {String}
|
||||
*/
|
||||
module.exports = function (str, locale) {
|
||||
var lang = LANGUAGES[locale]
|
||||
|
||||
str = str == null ? '' : String(str)
|
||||
|
||||
if (lang) {
|
||||
str = str.replace(lang.regexp, function (m) { return lang.map[m] })
|
||||
}
|
||||
|
||||
return str.toUpperCase()
|
||||
}
|
||||
|
||||
},{}]},{},[1])
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3Vzci9sb2NhbC9saWIvbm9kZV9tb2R1bGVzL2Jyb3dzZXJpZnkvbm9kZV9tb2R1bGVzL2Jyb3dzZXItcGFjay9fcHJlbHVkZS5qcyIsIi9Vc2Vycy9qc2FudGVsbC9EZXYvc291cmNlLW1hcC10ZXN0L2luZGV4LmpzIiwiL1VzZXJzL2pzYW50ZWxsL0Rldi9zb3VyY2UtbWFwLXRlc3QvbGliL2FkZC5qcyIsIi9Vc2Vycy9qc2FudGVsbC9EZXYvc291cmNlLW1hcC10ZXN0L2xpYi9zdWJ0cmFjdC5qcyIsIm5vZGVfbW9kdWxlcy91cHBlci1jYXNlL3VwcGVyLWNhc2UuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztBQ0FBLElBQUksR0FBRyxHQUFHLE9BQU8sQ0FBQyxXQUFXLENBQUMsQ0FBQztBQUMvQixJQUFJLFFBQVEsR0FBRyxPQUFPLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztBQUN6QyxJQUFJLFNBQVMsR0FBRyxPQUFPLENBQUMsWUFBWSxDQUFDLENBQUM7O0FBRXRDLENBQUM7U0FBTSxHQUFHLENBQUMsQ0FBQyxFQUFDLENBQUMsQ0FBQztFQUFBLENBQUU7Ozs7O0FDSmpCLE1BQU0sQ0FBQyxPQUFPLEdBQUcsVUFBVSxDQUFDLEVBQUUsQ0FBQyxFQUFFO0FBQy9CLFNBQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQztDQUNkLENBQUM7Ozs7O0FDRkYsTUFBTSxDQUFDLE9BQU8sR0FBRyxVQUFVLENBQUMsRUFBRSxDQUFDLEVBQUU7QUFDL0IsU0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDO0NBQ2QsQ0FBQzs7O0FDRkY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsInZhciBhZGQgPSByZXF1aXJlKFwiLi9saWIvYWRkXCIpO1xudmFyIHN1YnRyYWN0ID0gcmVxdWlyZShcIi4vbGliL3N1YnRyYWN0XCIpO1xudmFyIHVwcGVyQ2FzZSA9IHJlcXVpcmUoXCJ1cHBlci1jYXNlXCIpO1xuXG4oKCkgPT4gYWRkKDEsMikpO1xuIiwibW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoYSwgYikge1xuICByZXR1cm4gYSArIGI7XG59O1xuIiwibW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoYSwgYikge1xuICByZXR1cm4gYSAtIGI7XG59O1xuIiwiLyoqXG4gKiBTcGVjaWFsIGxhbmd1YWdlLXNwZWNpZmljIG92ZXJyaWRlcy5cbiAqXG4gKiBTb3VyY2U6IGZ0cDovL2Z0cC51bmljb2RlLm9yZy9QdWJsaWMvVUNEL2xhdGVzdC91Y2QvU3BlY2lhbENhc2luZy50eHRcbiAqXG4gKiBAdHlwZSB7T2JqZWN0fVxuICovXG52YXIgTEFOR1VBR0VTID0ge1xuICB0cjoge1xuICAgIHJlZ2V4cDogL1tcXHUwMDY5XS9nLFxuICAgIG1hcDoge1xuICAgICAgJ1xcdTAwNjknOiAnXFx1MDEzMCdcbiAgICB9XG4gIH0sXG4gIGF6OiB7XG4gICAgcmVnZXhwOiAvW1xcdTAwNjldL2csXG4gICAgbWFwOiB7XG4gICAgICAnXFx1MDA2OSc6ICdcXHUwMTMwJ1xuICAgIH1cbiAgfSxcbiAgbHQ6IHtcbiAgICByZWdleHA6IC9bXFx1MDA2OVxcdTAwNkFcXHUwMTJGXVxcdTAzMDd8XFx1MDA2OVxcdTAzMDdbXFx1MDMwMFxcdTAzMDFcXHUwMzAzXS9nLFxuICAgIG1hcDoge1xuICAgICAgJ1xcdTAwNjlcXHUwMzA3JzogJ1xcdTAwNDknLFxuICAgICAgJ1xcdTAwNkFcXHUwMzA3JzogJ1xcdTAwNEEnLFxuICAgICAgJ1xcdTAxMkZcXHUwMzA3JzogJ1xcdTAxMkUnLFxuICAgICAgJ1xcdTAwNjlcXHUwMzA3XFx1MDMwMCc6ICdcXHUwMENDJyxcbiAgICAgICdcXHUwMDY5XFx1MDMwN1xcdTAzMDEnOiAnXFx1MDBDRCcsXG4gICAgICAnXFx1MDA2OVxcdTAzMDdcXHUwMzAzJzogJ1xcdTAxMjgnXG4gICAgfVxuICB9XG59XG5cbi8qKlxuICogVXBwZXIgY2FzZSBhIHN0cmluZy5cbiAqXG4gKiBAcGFyYW0gIHtTdHJpbmd9IHN0clxuICogQHJldHVybiB7U3RyaW5nfVxuICovXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChzdHIsIGxvY2FsZSkge1xuICB2YXIgbGFuZyA9IExBTkdVQUdFU1tsb2NhbGVdXG5cbiAgc3RyID0gc3RyID09IG51bGwgPyAnJyA6IFN0cmluZyhzdHIpXG5cbiAgaWYgKGxhbmcpIHtcbiAgICBzdHIgPSBzdHIucmVwbGFjZShsYW5nLnJlZ2V4cCwgZnVuY3Rpb24gKG0pIHsgcmV0dXJuIGxhbmcubWFwW21dIH0pXG4gIH1cblxuICByZXR1cm4gc3RyLnRvVXBwZXJDYXNlKClcbn1cbiJdfQ==
|
|
@ -403,6 +403,7 @@ async function attachTestTab(client, title) {
|
|||
async function attachTestThread(client, title, callback = () => {}) {
|
||||
const targetFront = await attachTestTab(client, title);
|
||||
const [response, threadClient] = await targetFront.attachThread({
|
||||
useSourceMaps: true,
|
||||
autoBlackBox: true,
|
||||
});
|
||||
Assert.equal(threadClient.state, "paused", "Thread client is paused");
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* eslint-disable no-shadow */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Test that we can black box source mapped sources.
|
||||
*/
|
||||
|
||||
var gDebuggee;
|
||||
var gClient;
|
||||
var gThreadClient;
|
||||
|
||||
const {SourceNode} = require("source-map");
|
||||
|
||||
function run_test() {
|
||||
initTestDebuggerServer();
|
||||
gDebuggee = addTestGlobal("test-black-box");
|
||||
gClient = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient.connect().then(function() {
|
||||
attachTestTabAndResume(
|
||||
gClient, "test-black-box",
|
||||
function(response, targetFront, threadClient) {
|
||||
gThreadClient = threadClient;
|
||||
|
||||
Promise.resolve(setup_code())
|
||||
.then(black_box_code)
|
||||
.then(run_code)
|
||||
.then(test_correct_location)
|
||||
.catch(function(error) {
|
||||
Assert.ok(false, "Should not get an error, got " + error);
|
||||
})
|
||||
.then(function() {
|
||||
finishClient(gClient);
|
||||
});
|
||||
});
|
||||
});
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function setup_code() {
|
||||
/* eslint-disable no-multi-spaces, no-undef */
|
||||
let { code, map } = (new SourceNode(null, null, null, [
|
||||
new SourceNode(1, 0, "a.js", "" + function a() {
|
||||
return b();
|
||||
}),
|
||||
"\n",
|
||||
new SourceNode(1, 0, "b.js", "" + function b() {
|
||||
debugger; // Don't want to stop here.
|
||||
return c();
|
||||
}),
|
||||
"\n",
|
||||
new SourceNode(1, 0, "c.js", "" + function c() {
|
||||
debugger; // Want to stop here.
|
||||
}),
|
||||
"\n",
|
||||
])).toStringWithSourceMap({
|
||||
file: "abc.js",
|
||||
sourceRoot: "http://example.com/",
|
||||
});
|
||||
/* eslint-enable no-multi-spaces, no-undef */
|
||||
|
||||
code += "//# sourceMappingURL=data:text/json," + map.toString();
|
||||
|
||||
Cu.evalInSandbox(code,
|
||||
gDebuggee,
|
||||
"1.8",
|
||||
"http://example.com/abc.js");
|
||||
}
|
||||
|
||||
function black_box_code() {
|
||||
const d = defer();
|
||||
|
||||
gThreadClient.getSources(function({ sources, error }) {
|
||||
Assert.ok(!error, "Shouldn't get an error getting sources");
|
||||
const source = sources.filter((s) => {
|
||||
return s.url.includes("b.js");
|
||||
})[0];
|
||||
Assert.ok(!!source, "We should have our source in the sources list");
|
||||
|
||||
gThreadClient.source(source).blackBox(function({ error }) {
|
||||
Assert.ok(!error, "Should not get an error black boxing");
|
||||
d.resolve(true);
|
||||
});
|
||||
});
|
||||
|
||||
return d.promise;
|
||||
}
|
||||
|
||||
function run_code() {
|
||||
const d = defer();
|
||||
|
||||
gClient.addOneTimeListener("paused", function(event, packet) {
|
||||
d.resolve(packet);
|
||||
gThreadClient.resume();
|
||||
});
|
||||
gDebuggee.a();
|
||||
|
||||
return d.promise;
|
||||
}
|
||||
|
||||
function test_correct_location(packet) {
|
||||
Assert.equal(packet.why.type, "debuggerStatement",
|
||||
"Should hit a debugger statement.");
|
||||
Assert.equal(packet.frame.where.source.url, "http://example.com/c.js",
|
||||
"Should have skipped over the debugger statement in the" +
|
||||
" black boxed source");
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/* 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";
|
||||
|
||||
/**
|
||||
* Test if getExecutableLines return correct information
|
||||
*/
|
||||
|
||||
var gDebuggee;
|
||||
var gClient;
|
||||
var gThreadClient;
|
||||
|
||||
const SOURCE_MAPPED_FILE = getFileUrl("sourcemapped.js");
|
||||
|
||||
function run_test() {
|
||||
initTestDebuggerServer();
|
||||
gDebuggee = addTestGlobal("test-get-executable-lines");
|
||||
gClient = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient.connect().then(function _onConnect() {
|
||||
attachTestTabAndResume(
|
||||
gClient,
|
||||
"test-get-executable-lines",
|
||||
function(response, targetFront, threadClient) {
|
||||
gThreadClient = threadClient;
|
||||
test_executable_lines();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function test_executable_lines() {
|
||||
gThreadClient.addOneTimeListener("newSource", function _onNewSource(evt, packet) {
|
||||
Assert.equal(evt, "newSource");
|
||||
|
||||
gThreadClient.getSources(function({error, sources}) {
|
||||
Assert.ok(!error);
|
||||
const source = gThreadClient.source(sources[0]);
|
||||
source.getExecutableLines(function(lines) {
|
||||
Assert.ok(arrays_equal([1, 2, 4, 6], lines));
|
||||
finishClient(gClient);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const code = readFile("sourcemapped.js") + "\n//# sourceMappingURL=" +
|
||||
getFileUrl("source-map-data/sourcemapped.map");
|
||||
|
||||
Cu.evalInSandbox(code, gDebuggee, "1.8",
|
||||
SOURCE_MAPPED_FILE, 1);
|
||||
}
|
||||
|
||||
function arrays_equal(a, b) {
|
||||
return !(a < b || b < a);
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* eslint-disable no-shadow */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Check getSources functionality with sourcemaps.
|
||||
*/
|
||||
|
||||
const {SourceNode} = require("source-map");
|
||||
|
||||
add_task(threadClientTest(async ({ threadClient, debuggee }) => {
|
||||
await threadClient.reconfigure({ useSourceMaps: true });
|
||||
addSources(debuggee);
|
||||
|
||||
const res = await threadClient.getSources();
|
||||
Assert.equal(res.sources.length, 3, "3 sources exist");
|
||||
|
||||
await threadClient.reconfigure({ useSourceMaps: false });
|
||||
|
||||
const res2 = await threadClient.getSources();
|
||||
Assert.equal(res2.sources.length, 1, "1 source exist");
|
||||
}, {
|
||||
// Bug 1304144 - This test does not run in a worker because the
|
||||
// `rpc` method which talks to the main thread does not work.
|
||||
doNotRunWorker: true,
|
||||
}));
|
||||
|
||||
function addSources(debuggee) {
|
||||
let { code, map } = (new SourceNode(null, null, null, [
|
||||
new SourceNode(1, 0, "a.js", "function a() { return 'a'; }\n"),
|
||||
new SourceNode(1, 0, "b.js", "function b() { return 'b'; }\n"),
|
||||
new SourceNode(1, 0, "c.js", "function c() { return 'c'; }\n"),
|
||||
])).toStringWithSourceMap({
|
||||
file: "abc.js",
|
||||
sourceRoot: "http://example.com/www/js/",
|
||||
});
|
||||
|
||||
code += "//# sourceMappingURL=data:text/json;base64," + btoa(map.toString());
|
||||
|
||||
Cu.evalInSandbox(code, debuggee, "1.8",
|
||||
"http://example.com/www/js/abc.js", 1);
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Check basic source map integration with the "newSource" packet in the RDP.
|
||||
*/
|
||||
|
||||
var gDebuggee;
|
||||
var gClient;
|
||||
var gThreadClient;
|
||||
|
||||
const {SourceNode} = require("source-map");
|
||||
|
||||
function run_test() {
|
||||
initTestDebuggerServer();
|
||||
gDebuggee = addTestGlobal("test-source-map");
|
||||
gClient = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient.connect().then(function() {
|
||||
attachTestTabAndResume(gClient, "test-source-map",
|
||||
function(response, targetFront, threadClient) {
|
||||
gThreadClient = threadClient;
|
||||
test_simple_source_map();
|
||||
});
|
||||
});
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function test_simple_source_map() {
|
||||
// Because we are source mapping, we should be notified of a.js, b.js, and
|
||||
// c.js as sources, and shouldn't receive abc.js or test_sourcemaps-01.js.
|
||||
const expectedSources = new Set(["http://example.com/www/js/a.js",
|
||||
"http://example.com/www/js/b.js",
|
||||
"http://example.com/www/js/c.js"]);
|
||||
|
||||
gThreadClient.addListener("newSource", function _onNewSource(event, packet) {
|
||||
Assert.equal(event, "newSource");
|
||||
Assert.equal(packet.type, "newSource");
|
||||
Assert.ok(!!packet.source);
|
||||
|
||||
Assert.ok(expectedSources.has(packet.source.url),
|
||||
"The source url should be one of our original sources.");
|
||||
expectedSources.delete(packet.source.url);
|
||||
|
||||
if (expectedSources.size === 0) {
|
||||
gThreadClient.removeListener("newSource", _onNewSource);
|
||||
finishClient(gClient);
|
||||
}
|
||||
});
|
||||
|
||||
let { code, map } = (new SourceNode(null, null, null, [
|
||||
new SourceNode(1, 0, "a.js", "function a() { return 'a'; }\n"),
|
||||
new SourceNode(1, 0, "b.js", "function b() { return 'b'; }\n"),
|
||||
new SourceNode(1, 0, "c.js", "function c() { return 'c'; }\n"),
|
||||
])).toStringWithSourceMap({
|
||||
file: "abc.js",
|
||||
sourceRoot: "http://example.com/www/js/",
|
||||
});
|
||||
|
||||
code += "//# sourceMappingURL=data:text/json;base64," + btoa(map.toString());
|
||||
|
||||
Cu.evalInSandbox(code, gDebuggee, "1.8",
|
||||
"http://example.com/www/js/abc.js", 1);
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Check basic source map integration with the "sources" packet in the RDP.
|
||||
*/
|
||||
|
||||
var gDebuggee;
|
||||
var gClient;
|
||||
var gThreadClient;
|
||||
|
||||
const {SourceNode} = require("source-map");
|
||||
|
||||
function run_test() {
|
||||
initTestDebuggerServer();
|
||||
gDebuggee = addTestGlobal("test-source-map");
|
||||
gClient = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient.connect().then(function() {
|
||||
attachTestTabAndResume(gClient, "test-source-map",
|
||||
function(response, targetFront, threadClient) {
|
||||
gThreadClient = threadClient;
|
||||
test_simple_source_map();
|
||||
});
|
||||
});
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function test_simple_source_map() {
|
||||
// Because we are source mapping, we should be notified of a.js, b.js, and
|
||||
// c.js as sources, and shouldn"t receive abc.js or test_sourcemaps-01.js.
|
||||
const expectedSources = new Set(["http://example.com/www/js/a.js",
|
||||
"http://example.com/www/js/b.js",
|
||||
"http://example.com/www/js/c.js"]);
|
||||
|
||||
gClient.addOneTimeListener("paused", function(event, packet) {
|
||||
gThreadClient.getSources(function(response) {
|
||||
Assert.ok(!response.error, "Should not get an error");
|
||||
|
||||
for (const s of response.sources) {
|
||||
Assert.notEqual(s.url, "http://example.com/www/js/abc.js",
|
||||
"Shouldn't get the generated source's url.");
|
||||
expectedSources.delete(s.url);
|
||||
}
|
||||
|
||||
Assert.equal(expectedSources.size, 0,
|
||||
"Should have found all the expected sources sources by now.");
|
||||
|
||||
finishClient(gClient);
|
||||
});
|
||||
});
|
||||
|
||||
let { code, map } = (new SourceNode(null, null, null, [
|
||||
new SourceNode(1, 0, "a.js", "function a() { return 'a'; }\n"),
|
||||
new SourceNode(1, 0, "b.js", "function b() { return 'b'; }\n"),
|
||||
new SourceNode(1, 0, "c.js", "function c() { return 'c'; }\n"),
|
||||
new SourceNode(1, 0, "d.js", "debugger;\n"),
|
||||
])).toStringWithSourceMap({
|
||||
file: "abc.js",
|
||||
sourceRoot: "http://example.com/www/js/",
|
||||
});
|
||||
|
||||
code += "//# sourceMappingURL=data:text/json;base64," + btoa(map.toString());
|
||||
|
||||
Cu.evalInSandbox(code, gDebuggee, "1.8",
|
||||
"http://example.com/www/js/abc.js", 1);
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Check setting breakpoints in source mapped sources.
|
||||
*/
|
||||
|
||||
var gDebuggee;
|
||||
var gClient;
|
||||
var gThreadClient;
|
||||
|
||||
const {SourceNode} = require("source-map");
|
||||
|
||||
function run_test() {
|
||||
Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
|
||||
});
|
||||
initTestDebuggerServer();
|
||||
gDebuggee = addTestGlobal("test-source-map");
|
||||
gClient = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient.connect().then(function() {
|
||||
attachTestTabAndResume(gClient, "test-source-map",
|
||||
function(response, targetFront, threadClient) {
|
||||
gThreadClient = threadClient;
|
||||
test_simple_source_map();
|
||||
});
|
||||
});
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function testBreakpointMapping(name, callback) {
|
||||
(async function() {
|
||||
let response = await waitForPause(gThreadClient);
|
||||
Assert.equal(response.why.type, "debuggerStatement");
|
||||
|
||||
const source = await getSource(gThreadClient, "http://example.com/www/js/" + name + ".js");
|
||||
response = await setBreakpoint(source, {
|
||||
// Setting the breakpoint on an empty line so that it is pushed down one
|
||||
// line and we can check the source mapped actualLocation later.
|
||||
line: 3,
|
||||
});
|
||||
|
||||
// Should not slide breakpoints for sourcemapped sources
|
||||
Assert.ok(!response.actualLocation);
|
||||
|
||||
await setBreakpoint(source, { line: 4 });
|
||||
|
||||
// The eval will cause us to resume, then we get an unsolicited pause
|
||||
// because of our breakpoint, we resume again to finish the eval, and
|
||||
// finally receive our last pause which has the result of the client
|
||||
// evaluation.
|
||||
response = await gThreadClient.eval(null, name + "()");
|
||||
Assert.equal(response.type, "resumed");
|
||||
|
||||
response = await waitForPause(gThreadClient);
|
||||
Assert.equal(response.why.type, "breakpoint");
|
||||
// Assert that we paused because of the breakpoint at the correct
|
||||
// location in the code by testing that the value of `ret` is still
|
||||
// undefined.
|
||||
Assert.equal(response.frame.environment.bindings.variables.ret.value.type,
|
||||
"undefined");
|
||||
|
||||
response = await resume(gThreadClient);
|
||||
|
||||
response = await waitForPause(gThreadClient);
|
||||
Assert.equal(response.why.type, "clientEvaluated");
|
||||
Assert.equal(response.why.frameFinished.return, name);
|
||||
|
||||
response = await resume(gThreadClient);
|
||||
|
||||
callback();
|
||||
})();
|
||||
|
||||
gDebuggee.eval("(" + function() {
|
||||
debugger;
|
||||
} + "());");
|
||||
}
|
||||
|
||||
function test_simple_source_map() {
|
||||
const expectedSources = new Set([
|
||||
"http://example.com/www/js/a.js",
|
||||
"http://example.com/www/js/b.js",
|
||||
"http://example.com/www/js/c.js",
|
||||
]);
|
||||
|
||||
gThreadClient.addListener("newSource", function _onNewSource(event, packet) {
|
||||
expectedSources.delete(packet.source.url);
|
||||
if (expectedSources.size > 0) {
|
||||
return;
|
||||
}
|
||||
gThreadClient.removeListener("newSource", _onNewSource);
|
||||
|
||||
function finish() {
|
||||
finishClient(gClient);
|
||||
}
|
||||
|
||||
testBreakpointMapping("a", function() {
|
||||
testBreakpointMapping("b", function() {
|
||||
testBreakpointMapping("c", finish);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const a = new SourceNode(null, null, null, [
|
||||
new SourceNode(1, 0, "a.js", "function a() {\n"),
|
||||
new SourceNode(2, 0, "a.js", " var ret;\n"),
|
||||
new SourceNode(3, 0, "a.js", " // Empty line\n"),
|
||||
new SourceNode(4, 0, "a.js", " ret = 'a';\n"),
|
||||
new SourceNode(5, 0, "a.js", " return ret;\n"),
|
||||
new SourceNode(6, 0, "a.js", "}\n"),
|
||||
]);
|
||||
const b = new SourceNode(null, null, null, [
|
||||
new SourceNode(1, 0, "b.js", "function b() {\n"),
|
||||
new SourceNode(2, 0, "b.js", " var ret;\n"),
|
||||
new SourceNode(3, 0, "b.js", " // Empty line\n"),
|
||||
new SourceNode(4, 0, "b.js", " ret = 'b';\n"),
|
||||
new SourceNode(5, 0, "b.js", " return ret;\n"),
|
||||
new SourceNode(6, 0, "b.js", "}\n"),
|
||||
]);
|
||||
const c = new SourceNode(null, null, null, [
|
||||
new SourceNode(1, 0, "c.js", "function c() {\n"),
|
||||
new SourceNode(2, 0, "c.js", " var ret;\n"),
|
||||
new SourceNode(3, 0, "c.js", " // Empty line\n"),
|
||||
new SourceNode(4, 0, "c.js", " ret = 'c';\n"),
|
||||
new SourceNode(5, 0, "c.js", " return ret;\n"),
|
||||
new SourceNode(6, 0, "c.js", "}\n"),
|
||||
]);
|
||||
|
||||
let { code, map } = (new SourceNode(null, null, null, [
|
||||
a, b, c,
|
||||
])).toStringWithSourceMap({
|
||||
file: "http://example.com/www/js/abc.js",
|
||||
sourceRoot: "http://example.com/www/js/",
|
||||
});
|
||||
|
||||
code += "//# sourceMappingURL=data:text/json;base64," + btoa(map.toString());
|
||||
|
||||
Cu.evalInSandbox(code, gDebuggee, "1.8",
|
||||
"http://example.com/www/js/abc.js", 1);
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Check that absolute source map urls work.
|
||||
*/
|
||||
|
||||
var gDebuggee;
|
||||
var gClient;
|
||||
var gThreadClient;
|
||||
|
||||
function run_test() {
|
||||
initTestDebuggerServer();
|
||||
gDebuggee = addTestGlobal("test-source-map");
|
||||
gClient = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient.connect().then(function() {
|
||||
attachTestTabAndResume(gClient, "test-source-map",
|
||||
function(response, targetFront, threadClient) {
|
||||
gThreadClient = threadClient;
|
||||
test_absolute_source_map();
|
||||
});
|
||||
});
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function test_absolute_source_map() {
|
||||
gThreadClient.addOneTimeListener("newSource", function _onNewSource(event, packet) {
|
||||
Assert.equal(event, "newSource");
|
||||
Assert.equal(packet.type, "newSource");
|
||||
Assert.ok(!!packet.source);
|
||||
|
||||
Assert.ok(packet.source.url.includes("sourcemapped.coffee"),
|
||||
"The new source should be a coffee file.");
|
||||
Assert.equal(packet.source.url.indexOf("sourcemapped.js"), -1,
|
||||
"The new source should not be a js file.");
|
||||
|
||||
finishClient(gClient);
|
||||
});
|
||||
|
||||
const code = readFile("sourcemapped.js")
|
||||
+ "\n//# sourceMappingURL=" + getFileUrl("source-map-data/sourcemapped.map");
|
||||
|
||||
Cu.evalInSandbox(code, gDebuggee, "1.8",
|
||||
getFileUrl("sourcemapped.js"), 1);
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Check that relative source map urls work.
|
||||
*/
|
||||
|
||||
var gDebuggee;
|
||||
var gClient;
|
||||
var gThreadClient;
|
||||
|
||||
function run_test() {
|
||||
initTestDebuggerServer();
|
||||
gDebuggee = addTestGlobal("test-source-map");
|
||||
gClient = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient.connect().then(function() {
|
||||
attachTestTabAndResume(gClient, "test-source-map",
|
||||
function(response, targetFront, threadClient) {
|
||||
gThreadClient = threadClient;
|
||||
test_relative_source_map();
|
||||
});
|
||||
});
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function test_relative_source_map() {
|
||||
gThreadClient.addOneTimeListener("newSource", function _onNewSource(event, packet) {
|
||||
Assert.equal(event, "newSource");
|
||||
Assert.equal(packet.type, "newSource");
|
||||
Assert.ok(!!packet.source);
|
||||
|
||||
Assert.ok(packet.source.url.includes("sourcemapped.coffee"),
|
||||
"The new source should be a coffee file.");
|
||||
Assert.equal(packet.source.url.indexOf("sourcemapped.js"), -1,
|
||||
"The new source should not be a js file.");
|
||||
|
||||
finishClient(gClient);
|
||||
});
|
||||
|
||||
const code = readFile("sourcemapped.js")
|
||||
+ "\n//# sourceMappingURL=source-map-data/sourcemapped.map";
|
||||
|
||||
Cu.evalInSandbox(code, gDebuggee, "1.8",
|
||||
getFileUrl("sourcemapped.js"), 1);
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Check that we can load sources whose content is embedded in the
|
||||
* "sourcesContent" field of a source map.
|
||||
*/
|
||||
|
||||
var gDebuggee;
|
||||
var gClient;
|
||||
var gThreadClient;
|
||||
|
||||
const {SourceNode} = require("source-map");
|
||||
|
||||
function run_test() {
|
||||
initTestDebuggerServer();
|
||||
gDebuggee = addTestGlobal("test-source-map");
|
||||
gClient = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient.connect().then(function() {
|
||||
attachTestTabAndResume(gClient, "test-source-map",
|
||||
function(response, targetFront, threadClient) {
|
||||
gThreadClient = threadClient;
|
||||
test_source_content();
|
||||
});
|
||||
});
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function test_source_content() {
|
||||
let numNewSources = 0;
|
||||
|
||||
gThreadClient.addListener("newSource", function _onNewSource(event, packet) {
|
||||
if (++numNewSources !== 3) {
|
||||
return;
|
||||
}
|
||||
gThreadClient.removeListener("newSource", _onNewSource);
|
||||
|
||||
gThreadClient.getSources(function(response) {
|
||||
Assert.ok(!response.error, "Should not get an error");
|
||||
|
||||
testContents(response.sources, 0, (timesCalled) => {
|
||||
Assert.equal(timesCalled, 3);
|
||||
finishClient(gClient);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const node = new SourceNode(null, null, null, [
|
||||
new SourceNode(1, 0, "a.js", "function a() { return 'a'; }\n"),
|
||||
new SourceNode(1, 0, "b.js", "function b() { return 'b'; }\n"),
|
||||
new SourceNode(1, 0, "c.js", "function c() { return 'c'; }\n"),
|
||||
]);
|
||||
|
||||
node.setSourceContent("a.js", "content for a.js");
|
||||
node.setSourceContent("b.js", "content for b.js");
|
||||
node.setSourceContent("c.js", "content for c.js");
|
||||
|
||||
let { code, map } = node.toStringWithSourceMap({
|
||||
file: "abc.js",
|
||||
});
|
||||
|
||||
code += "//# sourceMappingURL=data:text/json;base64," + btoa(map.toString());
|
||||
|
||||
Cu.evalInSandbox(code, gDebuggee, "1.8",
|
||||
"http://example.com/www/js/abc.js", 1);
|
||||
}
|
||||
|
||||
function testContents(sources, timesCalled, callback) {
|
||||
if (sources.length === 0) {
|
||||
callback(timesCalled);
|
||||
return;
|
||||
}
|
||||
|
||||
const source = sources[0];
|
||||
const sourceClient = gThreadClient.source(sources[0]);
|
||||
|
||||
if (sourceClient.url) {
|
||||
sourceClient.source().then(response => {
|
||||
const expectedContent = "content for " + source.url.replace(/^.*\//, "");
|
||||
Assert.equal(response.source, expectedContent,
|
||||
"Should have the expected source content");
|
||||
|
||||
testContents(sources.slice(1), timesCalled + 1, callback);
|
||||
});
|
||||
} else {
|
||||
testContents(sources.slice(1), timesCalled, callback);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* eslint-disable no-shadow */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Test that we don't permanently cache sources from source maps.
|
||||
*/
|
||||
|
||||
var gDebuggee;
|
||||
var gClient;
|
||||
var gThreadClient;
|
||||
|
||||
const {SourceNode} = require("source-map");
|
||||
|
||||
function run_test() {
|
||||
initTestDebuggerServer();
|
||||
gDebuggee = addTestGlobal("test-source-map");
|
||||
gClient = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient.connect().then(function() {
|
||||
attachTestTabAndResume(gClient, "test-source-map",
|
||||
function(response, targetFront, threadClient) {
|
||||
gThreadClient = threadClient;
|
||||
test_cached_original_sources();
|
||||
});
|
||||
});
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function test_cached_original_sources() {
|
||||
writeFile("temp.js", "initial content");
|
||||
|
||||
gThreadClient.addOneTimeListener("newSource", onNewSource);
|
||||
|
||||
const node = new SourceNode(1, 0,
|
||||
getFileUrl("temp.js"),
|
||||
"function funcFromTemp() {}\n");
|
||||
let { code, map } = node.toStringWithSourceMap({
|
||||
file: "abc.js",
|
||||
});
|
||||
code += "//# sourceMappingURL=data:text/json;base64," + btoa(map.toString());
|
||||
|
||||
Cu.evalInSandbox(code, gDebuggee, "1.8",
|
||||
"http://example.com/www/js/abc.js", 1);
|
||||
}
|
||||
|
||||
function onNewSource(event, packet) {
|
||||
const sourceClient = gThreadClient.source(packet.source);
|
||||
sourceClient.source().then(function(response) {
|
||||
Assert.equal(response.source, "initial content",
|
||||
"The correct source content should be sent");
|
||||
|
||||
writeFile("temp.js", "new content");
|
||||
|
||||
sourceClient.source().then(function(response) {
|
||||
Assert.equal(response.source, "new content",
|
||||
"The correct source content should not be cached, " +
|
||||
"so we should get the new content");
|
||||
|
||||
do_get_file("temp.js").remove(false);
|
||||
finishClient(gClient);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Regression test for bug 882986 regarding sourcesContent and absolute source
|
||||
* URLs.
|
||||
*/
|
||||
|
||||
var gDebuggee;
|
||||
var gClient;
|
||||
var gThreadClient;
|
||||
|
||||
function run_test() {
|
||||
initTestDebuggerServer();
|
||||
gDebuggee = addTestGlobal("test-source-map");
|
||||
gClient = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient.connect().then(function() {
|
||||
attachTestTabAndResume(gClient, "test-source-map",
|
||||
function(response, targetFront, threadClient) {
|
||||
gThreadClient = threadClient;
|
||||
test_source_maps();
|
||||
});
|
||||
});
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function test_source_maps() {
|
||||
gThreadClient.addOneTimeListener("newSource", function(event, packet) {
|
||||
const sourceClient = gThreadClient.source(packet.source);
|
||||
sourceClient.source().then(function({source}) {
|
||||
Assert.equal(source, "foo",
|
||||
"Should load the source from the sourcesContent field");
|
||||
finishClient(gClient);
|
||||
});
|
||||
});
|
||||
|
||||
let code = "'nothing here';\n";
|
||||
code += "//# sourceMappingURL=data:text/json," + JSON.stringify({
|
||||
version: 3,
|
||||
file: "foo.js",
|
||||
sources: ["/a"],
|
||||
names: [],
|
||||
mappings: "AACA",
|
||||
sourcesContent: ["foo"],
|
||||
});
|
||||
Cu.evalInSandbox(code, gDebuggee, "1.8",
|
||||
"http://example.com/foo.js", 1);
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Check that source maps and breakpoints work with minified javascript.
|
||||
*/
|
||||
|
||||
var gDebuggee;
|
||||
var gClient;
|
||||
var gThreadClient;
|
||||
|
||||
function run_test() {
|
||||
initTestDebuggerServer();
|
||||
gDebuggee = addTestGlobal("test-source-map");
|
||||
gClient = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient.connect().then(function() {
|
||||
attachTestTabAndResume(gClient, "test-source-map",
|
||||
function(response, targetFront, threadClient) {
|
||||
gThreadClient = threadClient;
|
||||
test_minified();
|
||||
});
|
||||
});
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function test_minified() {
|
||||
gThreadClient.addOneTimeListener("newSource", function _onNewSource(event, packet) {
|
||||
Assert.equal(event, "newSource");
|
||||
Assert.equal(packet.type, "newSource");
|
||||
Assert.ok(!!packet.source);
|
||||
|
||||
Assert.equal(packet.source.url, "http://example.com/foo.js",
|
||||
"The new source should be foo.js");
|
||||
Assert.equal(packet.source.url.indexOf("foo.min.js"), -1,
|
||||
"The new source should not be the minified file");
|
||||
});
|
||||
|
||||
gThreadClient.addOneTimeListener("paused", function(event, packet) {
|
||||
Assert.equal(event, "paused");
|
||||
Assert.equal(packet.why.type, "debuggerStatement");
|
||||
|
||||
const location = {
|
||||
line: 5,
|
||||
};
|
||||
|
||||
getSource(gThreadClient, "http://example.com/foo.js").then(source => {
|
||||
source.setBreakpoint(location).then(() => testHitBreakpoint());
|
||||
});
|
||||
});
|
||||
|
||||
// This is the original foo.js, which was then minified with uglifyjs version
|
||||
// 2.2.5 and the "--mangle" option.
|
||||
//
|
||||
// (function () {
|
||||
// debugger;
|
||||
// function foo(n) {
|
||||
// var bar = n + n;
|
||||
// var unused = null;
|
||||
// return bar;
|
||||
// }
|
||||
// for (var i = 0; i < 10; i++) {
|
||||
// foo(i);
|
||||
// }
|
||||
// }());
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
const code = '(function(){debugger;function r(r){var n=r+r;var u=null;return n}for(var n=0;n<10;n++){r(n)}})();\n//# sourceMappingURL=data:text/json,{"file":"foo.min.js","version":3,"sources":["foo.js"],"names":["foo","n","bar","unused","i"],"mappings":"CAAC,WACC,QACA,SAASA,GAAIC,GACX,GAAIC,GAAMD,EAAIA,CACd,IAAIE,GAAS,IACb,OAAOD,GAET,IAAK,GAAIE,GAAI,EAAGA,EAAI,GAAIA,IAAK,CAC3BJ,EAAII"}';
|
||||
|
||||
Cu.evalInSandbox(code, gDebuggee, "1.8",
|
||||
"http://example.com/foo.min.js", 1);
|
||||
}
|
||||
|
||||
function testHitBreakpoint(timesHit = 0) {
|
||||
gClient.addOneTimeListener("paused", function(event, packet) {
|
||||
++timesHit;
|
||||
|
||||
Assert.equal(event, "paused");
|
||||
Assert.equal(packet.why.type, "breakpoint");
|
||||
|
||||
if (timesHit === 10) {
|
||||
gThreadClient.resume(() => finishClient(gClient));
|
||||
} else {
|
||||
testHitBreakpoint(timesHit);
|
||||
}
|
||||
});
|
||||
|
||||
gThreadClient.resume();
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Check that we source map frame locations for the frame we are paused at.
|
||||
*/
|
||||
|
||||
var gDebuggee;
|
||||
var gClient;
|
||||
var gThreadClient;
|
||||
|
||||
const {SourceNode} = require("source-map");
|
||||
|
||||
function run_test() {
|
||||
initTestDebuggerServer();
|
||||
gDebuggee = addTestGlobal("test-source-map");
|
||||
gClient = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient.connect().then(function() {
|
||||
attachTestTabAndResume(
|
||||
gClient, "test-source-map",
|
||||
function(response, targetFront, threadClient) {
|
||||
gThreadClient = threadClient;
|
||||
Promise.resolve(define_code())
|
||||
.then(run_code)
|
||||
.then(test_frame_location)
|
||||
.catch(error => {
|
||||
dump(error + "\n");
|
||||
dump(error.stack);
|
||||
Assert.ok(false);
|
||||
})
|
||||
.then(() => {
|
||||
finishClient(gClient);
|
||||
});
|
||||
});
|
||||
});
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function define_code() {
|
||||
let { code, map } = (new SourceNode(null, null, null, [
|
||||
new SourceNode(1, 0, "a.js", "function a() {\n"),
|
||||
new SourceNode(2, 0, "a.js", " b();\n"),
|
||||
new SourceNode(3, 0, "a.js", "}\n"),
|
||||
new SourceNode(1, 0, "b.js", "function b() {\n"),
|
||||
new SourceNode(2, 0, "b.js", " c();\n"),
|
||||
new SourceNode(3, 0, "b.js", "}\n"),
|
||||
new SourceNode(1, 0, "c.js", "function c() {\n"),
|
||||
new SourceNode(2, 0, "c.js", " debugger;\n"),
|
||||
new SourceNode(3, 0, "c.js", "}\n"),
|
||||
])).toStringWithSourceMap({
|
||||
file: "abc.js",
|
||||
sourceRoot: "http://example.com/www/js/",
|
||||
});
|
||||
|
||||
code += "//# sourceMappingURL=data:text/json," + map.toString();
|
||||
|
||||
Cu.evalInSandbox(code, gDebuggee, "1.8",
|
||||
"http://example.com/www/js/abc.js", 1);
|
||||
}
|
||||
|
||||
function run_code() {
|
||||
const d = defer();
|
||||
gClient.addOneTimeListener("paused", function(event, packet) {
|
||||
d.resolve(packet);
|
||||
gThreadClient.resume();
|
||||
});
|
||||
gDebuggee.a();
|
||||
return d.promise;
|
||||
}
|
||||
|
||||
function test_frame_location({ frame: { where: { source, line, column } } }) {
|
||||
Assert.equal(source.url, "http://example.com/www/js/c.js");
|
||||
Assert.equal(line, 2);
|
||||
Assert.equal(column, 0);
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Check that we source map frame locations returned by "frames" requests.
|
||||
*/
|
||||
|
||||
var gDebuggee;
|
||||
var gClient;
|
||||
var gThreadClient;
|
||||
|
||||
const {SourceNode} = require("source-map");
|
||||
|
||||
function run_test() {
|
||||
initTestDebuggerServer();
|
||||
gDebuggee = addTestGlobal("test-source-map");
|
||||
gClient = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient.connect().then(function() {
|
||||
attachTestTabAndResume(
|
||||
gClient, "test-source-map",
|
||||
function(response, targetFront, threadClient) {
|
||||
gThreadClient = threadClient;
|
||||
Promise.resolve(define_code())
|
||||
.then(run_code)
|
||||
.then(test_frames)
|
||||
.catch(error => {
|
||||
dump(error + "\n");
|
||||
dump(error.stack);
|
||||
Assert.ok(false);
|
||||
})
|
||||
.then(() => {
|
||||
finishClient(gClient);
|
||||
});
|
||||
});
|
||||
});
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function define_code() {
|
||||
let { code, map } = (new SourceNode(null, null, null, [
|
||||
new SourceNode(1, 0, "a.js", "function a() {\n"),
|
||||
new SourceNode(2, 0, "a.js", " b();\n"),
|
||||
new SourceNode(3, 0, "a.js", "}\n"),
|
||||
new SourceNode(1, 0, "b.js", "function b() {\n"),
|
||||
new SourceNode(2, 0, "b.js", " c();\n"),
|
||||
new SourceNode(3, 0, "b.js", "}\n"),
|
||||
new SourceNode(1, 0, "c.js", "function c() {\n"),
|
||||
new SourceNode(2, 0, "c.js", " debugger;\n"),
|
||||
new SourceNode(3, 0, "c.js", "}\n"),
|
||||
])).toStringWithSourceMap({
|
||||
file: "abc.js",
|
||||
sourceRoot: "http://example.com/www/js/",
|
||||
});
|
||||
|
||||
code += "//# sourceMappingURL=data:text/json," + map.toString();
|
||||
|
||||
Cu.evalInSandbox(code, gDebuggee, "1.8",
|
||||
"http://example.com/www/js/abc.js", 1);
|
||||
}
|
||||
|
||||
function run_code() {
|
||||
const d = defer();
|
||||
gClient.addOneTimeListener("paused", function() {
|
||||
gThreadClient.getFrames(0, 3, function(response) {
|
||||
d.resolve(response);
|
||||
gThreadClient.resume();
|
||||
});
|
||||
});
|
||||
gDebuggee.a();
|
||||
return d.promise;
|
||||
}
|
||||
|
||||
function test_frames({ error, frames }) {
|
||||
Assert.ok(!error);
|
||||
Assert.equal(frames.length, 3);
|
||||
check_frame(frames[0], "http://example.com/www/js/c.js");
|
||||
check_frame(frames[1], "http://example.com/www/js/b.js");
|
||||
check_frame(frames[2], "http://example.com/www/js/a.js");
|
||||
}
|
||||
|
||||
function check_frame({ where: { source, line, column } }, expectedUrl) {
|
||||
Assert.equal(source.url, expectedUrl);
|
||||
Assert.equal(line, 2);
|
||||
Assert.equal(column, 0);
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Check that we continue stepping when a single original source's line
|
||||
* corresponds to multiple generated js lines.
|
||||
*/
|
||||
|
||||
var gDebuggee;
|
||||
var gClient;
|
||||
var gThreadClient;
|
||||
|
||||
const {SourceNode} = require("source-map");
|
||||
|
||||
function run_test() {
|
||||
initTestDebuggerServer();
|
||||
gDebuggee = addTestGlobal("test-source-map");
|
||||
gClient = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient.connect().then(function() {
|
||||
attachTestTabAndResume(gClient, "test-source-map",
|
||||
function(response, targetFront, threadClient) {
|
||||
gThreadClient = threadClient;
|
||||
define_code();
|
||||
});
|
||||
});
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function define_code() {
|
||||
let { code, map } = (new SourceNode(null, null, null, [
|
||||
new SourceNode(1, 0, "a.js", "function runTest() {\n"),
|
||||
// A bunch of js lines map to the same original source line.
|
||||
new SourceNode(2, 0, "a.js", " debugger;\n"),
|
||||
new SourceNode(2, 0, "a.js", " var sum = 0;\n"),
|
||||
new SourceNode(2, 0, "a.js", " for (var i = 0; i < 5; i++) {\n"),
|
||||
new SourceNode(2, 0, "a.js", " sum += i;\n"),
|
||||
new SourceNode(2, 0, "a.js", " }\n"),
|
||||
// And now we have a new line in the original source that we should stop at.
|
||||
new SourceNode(3, 0, "a.js", " sum;\n"),
|
||||
new SourceNode(3, 0, "a.js", "}\n"),
|
||||
])).toStringWithSourceMap({
|
||||
file: "abc.js",
|
||||
sourceRoot: "http://example.com/",
|
||||
});
|
||||
|
||||
code += "//# sourceMappingURL=data:text/json," + map.toString();
|
||||
|
||||
Cu.evalInSandbox(code, gDebuggee, "1.8",
|
||||
"http://example.com/abc.js", 1);
|
||||
|
||||
run_code();
|
||||
}
|
||||
|
||||
function run_code() {
|
||||
gClient.addOneTimeListener("paused", function(event, packet) {
|
||||
Assert.equal(packet.why.type, "debuggerStatement");
|
||||
step_in();
|
||||
});
|
||||
gDebuggee.runTest();
|
||||
}
|
||||
|
||||
function step_in() {
|
||||
gClient.addOneTimeListener("paused", function(event, packet) {
|
||||
Assert.equal(packet.why.type, "resumeLimit");
|
||||
const { frame: { environment, where: { source, line } } } = packet;
|
||||
// Stepping should have moved us to the next source mapped line.
|
||||
Assert.equal(source.url, "http://example.com/a.js");
|
||||
Assert.equal(line, 3);
|
||||
// Which should have skipped over the for loop in the generated js and sum
|
||||
// should be calculated.
|
||||
Assert.equal(environment.bindings.variables.sum.value, 10);
|
||||
finishClient(gClient);
|
||||
});
|
||||
gThreadClient.stepIn();
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Test that we don't permanently cache source maps across reloads.
|
||||
*/
|
||||
|
||||
var gDebuggee;
|
||||
var gClient;
|
||||
var gThreadClient;
|
||||
var gTargetFront;
|
||||
|
||||
const {SourceNode} = require("source-map");
|
||||
|
||||
function run_test() {
|
||||
initTestDebuggerServer();
|
||||
gDebuggee = addTestGlobal("test-source-map");
|
||||
gClient = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient.connect().then(function() {
|
||||
attachTestTabAndResume(gClient, "test-source-map",
|
||||
function(response, targetFront, threadClient) {
|
||||
gThreadClient = threadClient;
|
||||
gTargetFront = targetFront;
|
||||
setup_code();
|
||||
});
|
||||
});
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
// The MAP_FILE_NAME is .txt so that the OS will definitely have an extension ->
|
||||
// content type mapping for the extension. If it doesn't (like .map or .json),
|
||||
// it logs console errors, which cause the test to fail. See bug 907839.
|
||||
const MAP_FILE_NAME = "temporary-generated.txt";
|
||||
|
||||
const TEMP_FILE_1 = "temporary1.js";
|
||||
const TEMP_FILE_2 = "temporary2.js";
|
||||
const TEMP_GENERATED_SOURCE = "temporary-generated.js";
|
||||
|
||||
function setup_code() {
|
||||
const node = new SourceNode(1, 0,
|
||||
getFileUrl(TEMP_FILE_1, true),
|
||||
"function temporary1() {}\n");
|
||||
let { code, map } = node.toStringWithSourceMap({
|
||||
file: getFileUrl(TEMP_GENERATED_SOURCE, true),
|
||||
});
|
||||
|
||||
code += "//# sourceMappingURL=" + getFileUrl(MAP_FILE_NAME, true);
|
||||
writeFile(MAP_FILE_NAME, map.toString());
|
||||
|
||||
Cu.evalInSandbox(code,
|
||||
gDebuggee,
|
||||
"1.8",
|
||||
getFileUrl(TEMP_GENERATED_SOURCE, true),
|
||||
1);
|
||||
|
||||
test_initial_sources();
|
||||
}
|
||||
|
||||
function test_initial_sources() {
|
||||
gThreadClient.getSources(function({ error, sources }) {
|
||||
Assert.ok(!error);
|
||||
sources = sources.filter(source => source.url);
|
||||
Assert.equal(sources.length, 1);
|
||||
Assert.equal(sources[0].url, getFileUrl(TEMP_FILE_1, true));
|
||||
reload(gTargetFront).then(setup_new_code);
|
||||
});
|
||||
}
|
||||
|
||||
function setup_new_code() {
|
||||
const node = new SourceNode(1, 0,
|
||||
getFileUrl(TEMP_FILE_2, true),
|
||||
"function temporary2() {}\n");
|
||||
let { code, map } = node.toStringWithSourceMap({
|
||||
file: getFileUrl(TEMP_GENERATED_SOURCE, true),
|
||||
});
|
||||
|
||||
code += "\n//# sourceMappingURL=" + getFileUrl(MAP_FILE_NAME, true);
|
||||
writeFile(MAP_FILE_NAME, map.toString());
|
||||
|
||||
gThreadClient.addOneTimeListener("newSource", test_new_sources);
|
||||
Cu.evalInSandbox(code,
|
||||
gDebuggee,
|
||||
"1.8",
|
||||
getFileUrl(TEMP_GENERATED_SOURCE, true),
|
||||
1);
|
||||
}
|
||||
|
||||
function test_new_sources() {
|
||||
gThreadClient.getSources(function({ error, sources }) {
|
||||
Assert.ok(!error);
|
||||
sources = sources.filter(source => source.url);
|
||||
|
||||
// Should now have TEMP_FILE_2 as a source.
|
||||
Assert.equal(sources.length, 1);
|
||||
const s = sources.filter(source => source.url === getFileUrl(TEMP_FILE_2, true))[0];
|
||||
Assert.ok(!!s);
|
||||
|
||||
finish_test();
|
||||
});
|
||||
}
|
||||
|
||||
function finish_test() {
|
||||
do_get_file(MAP_FILE_NAME).remove(false);
|
||||
finishClient(gClient);
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Verify that we can load the contents of every source in a source map produced
|
||||
* by babel and browserify.
|
||||
*/
|
||||
|
||||
var gDebuggee;
|
||||
var gClient;
|
||||
|
||||
function run_test() {
|
||||
initTestDebuggerServer();
|
||||
gDebuggee = addTestGlobal("test-sourcemaps");
|
||||
gClient = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient.connect().then(function() {
|
||||
attachTestThread(gClient, "test-sourcemaps", testSourcemap);
|
||||
});
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
const testSourcemap = async function(threadResponse, targetFront, threadClient,
|
||||
tabResponse) {
|
||||
evalTestCode();
|
||||
|
||||
const { sources } = await getSources(threadClient);
|
||||
|
||||
for (const form of sources) {
|
||||
const sourceResponse = await getSourceContent(threadClient.source(form));
|
||||
ok(sourceResponse, "Should be able to get the source response");
|
||||
ok(sourceResponse.source, "Should have the source text as well");
|
||||
}
|
||||
|
||||
finishClient(gClient);
|
||||
};
|
||||
|
||||
const TEST_FILE = "babel_and_browserify_script_with_source_map.js";
|
||||
|
||||
function evalTestCode() {
|
||||
const testFileContents = readFile(TEST_FILE);
|
||||
Cu.evalInSandbox(testFileContents,
|
||||
gDebuggee,
|
||||
"1.8",
|
||||
getFileUrl(TEST_FILE),
|
||||
1);
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Check that we properly handle frames that cannot be sourcemapped
|
||||
* when using sourcemaps.
|
||||
*/
|
||||
|
||||
var gDebuggee;
|
||||
var gClient;
|
||||
var gThreadClient;
|
||||
|
||||
const {SourceNode} = require("source-map");
|
||||
|
||||
function run_test() {
|
||||
Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
|
||||
});
|
||||
initTestDebuggerServer();
|
||||
gDebuggee = addTestGlobal("test-source-map");
|
||||
gClient = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient.connect().then(function() {
|
||||
attachTestTabAndResume(gClient, "test-source-map",
|
||||
function(response, targetFront, threadClient) {
|
||||
gThreadClient = threadClient;
|
||||
test_source_map();
|
||||
});
|
||||
});
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function test_source_map() {
|
||||
// Set up debuggee code.
|
||||
const a = new SourceNode(1, 1, "a.js", "function a() { b(); }");
|
||||
const b = new SourceNode(null, null, null, "function b() { c(); }");
|
||||
const c = new SourceNode(1, 1, "c.js", "function c() { d(); }");
|
||||
const d = new SourceNode(null, null, null, "function d() { e(); }");
|
||||
const e = new SourceNode(1, 1, "e.js", "function e() { debugger; }");
|
||||
const { map, code } = (new SourceNode(
|
||||
null, null, null, [a, b, c, d, e]
|
||||
)).toStringWithSourceMap({
|
||||
file: "root.js",
|
||||
sourceRoot: "root",
|
||||
});
|
||||
Cu.evalInSandbox(
|
||||
code + "//# sourceMappingURL=data:text/json;base64," + btoa(map.toString()),
|
||||
gDebuggee,
|
||||
"1.8",
|
||||
"http://example.com/www/js/abc.js",
|
||||
1
|
||||
);
|
||||
|
||||
gThreadClient.addOneTimeListener("paused", function(event, packet) {
|
||||
gThreadClient.getFrames(0, 50, function({ error, frames }) {
|
||||
Assert.ok(!error);
|
||||
Assert.equal(frames.length, 4);
|
||||
// b.js should be skipped
|
||||
Assert.equal(frames[0].where.source.url, "http://example.com/www/js/root/e.js");
|
||||
Assert.equal(frames[1].where.source.url, "http://example.com/www/js/root/c.js");
|
||||
Assert.equal(frames[2].where.source.url, "http://example.com/www/js/root/a.js");
|
||||
Assert.equal(frames[3].where.source.url, null);
|
||||
|
||||
finishClient(gClient);
|
||||
});
|
||||
});
|
||||
|
||||
// Trigger it.
|
||||
gDebuggee.eval("a()");
|
||||
}
|
|
@ -142,7 +142,7 @@ TestTargetActor.prototype = {
|
|||
},
|
||||
|
||||
onReload: function(request) {
|
||||
this.sources.reset();
|
||||
this.sources.reset({ sourceMaps: true });
|
||||
this.threadActor.clearDebuggees();
|
||||
this.threadActor.dbg.addDebuggees();
|
||||
return {};
|
||||
|
|
|
@ -5,6 +5,7 @@ firefox-appdir = browser
|
|||
skip-if = toolkit == 'android'
|
||||
|
||||
support-files =
|
||||
babel_and_browserify_script_with_source_map.js
|
||||
completions.js
|
||||
source-map-data/sourcemapped.coffee
|
||||
source-map-data/sourcemapped.map
|
||||
|
@ -56,6 +57,7 @@ support-files =
|
|||
[test_blackboxing-03.js]
|
||||
[test_blackboxing-04.js]
|
||||
[test_blackboxing-05.js]
|
||||
[test_blackboxing-06.js]
|
||||
[test_blackboxing-07.js]
|
||||
[test_frameactor-01.js]
|
||||
[test_frameactor-02.js]
|
||||
|
@ -137,8 +139,24 @@ reason = bug 1104838
|
|||
[test_listsources-01.js]
|
||||
[test_listsources-02.js]
|
||||
[test_listsources-03.js]
|
||||
[test_listsources-04.js]
|
||||
[test_new_source-01.js]
|
||||
[test_new_source-02.js]
|
||||
[test_sourcemaps-01.js]
|
||||
[test_sourcemaps-02.js]
|
||||
[test_sourcemaps-03.js]
|
||||
[test_sourcemaps-04.js]
|
||||
[test_sourcemaps-05.js]
|
||||
[test_sourcemaps-06.js]
|
||||
[test_sourcemaps-07.js]
|
||||
[test_sourcemaps-08.js]
|
||||
[test_sourcemaps-09.js]
|
||||
[test_sourcemaps-10.js]
|
||||
[test_sourcemaps-11.js]
|
||||
[test_sourcemaps-12.js]
|
||||
[test_sourcemaps-13.js]
|
||||
[test_sourcemaps-16.js]
|
||||
[test_sourcemaps-17.js]
|
||||
[test_objectgrips-01.js]
|
||||
[test_objectgrips-02.js]
|
||||
[test_objectgrips-03.js]
|
||||
|
@ -211,6 +229,7 @@ reason = bug 937197
|
|||
[test_symbols-01.js]
|
||||
[test_symbols-02.js]
|
||||
[test_get-executable-lines.js]
|
||||
[test_get-executable-lines-source-map.js]
|
||||
[test_xpcshell_debugging.js]
|
||||
support-files = xpcshell_debugging_script.js
|
||||
[test_setBreakpoint-at-the-beginning-of-a-line.js]
|
||||
|
|
|
@ -37,6 +37,8 @@ BuiltinProvider.prototype = {
|
|||
// ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
|
||||
"acorn/util/walk": "resource://devtools/shared/acorn/walk.js",
|
||||
// ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
|
||||
"source-map": "resource://devtools/shared/sourcemap/source-map.js",
|
||||
// ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
|
||||
// Allow access to xpcshell test items from the loader.
|
||||
"xpcshell-test": "resource://test",
|
||||
|
||||
|
|
|
@ -423,6 +423,7 @@ DebuggerClient.prototype = {
|
|||
* The actor ID for the thread to attach.
|
||||
* @param object options
|
||||
* Configuration options.
|
||||
* - useSourceMaps: whether to use source maps or not.
|
||||
*/
|
||||
attachThread: function(threadActor, options = {}) {
|
||||
if (this._clients.has(threadActor)) {
|
||||
|
|
|
@ -33,6 +33,7 @@ class BrowsingContextTargetFront extends FrontClassWithSpec(browsingContextTarge
|
|||
*
|
||||
* @param object options
|
||||
* Configuration options.
|
||||
* - useSourceMaps: whether to use source maps or not.
|
||||
*/
|
||||
attachThread(options = {}) {
|
||||
if (this.thread) {
|
||||
|
|
|
@ -26,6 +26,7 @@ DIRS += [
|
|||
'qrcode',
|
||||
'screenshot',
|
||||
'security',
|
||||
'sourcemap',
|
||||
'sprintfjs',
|
||||
'specs',
|
||||
'transport',
|
||||
|
@ -74,6 +75,7 @@ DevToolsModules(
|
|||
'task.js',
|
||||
'ThreadSafeDevToolsUtils.js',
|
||||
'throttle.js',
|
||||
'wasm-source-map.js',
|
||||
)
|
||||
|
||||
with Files('**'):
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
Rather than make changes to the built files here, make them upstream and then
|
||||
upgrade our tree's copy of the built files.
|
||||
|
||||
To upgrade the source-map library:
|
||||
|
||||
$ git clone https://github.com/mozilla/source-map.git
|
||||
$ cd source-map
|
||||
$ git checkout <latest-tagged-version>
|
||||
$ npm install
|
||||
$ npm run-script build -or- nodejs Makefile.dryice.js (if you have issues with npm)
|
||||
$ cp dist/source-map.js /path/to/mozilla-central/devtools/shared/sourcemap/source-map.js
|
||||
$ cp dist/test/* /path/to/mozilla-central/devtools/shared/sourcemap/tests/unit/
|
|
@ -0,0 +1,11 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
|
||||
|
||||
DevToolsModules(
|
||||
'source-map.js'
|
||||
)
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,14 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
function doesNotThrow(f) {
|
||||
try {
|
||||
f();
|
||||
} catch(e) {
|
||||
ok(false, e + e.stack);
|
||||
}
|
||||
}
|
||||
|
||||
var assert = this;
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -0,0 +1,15 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
head = head_sourcemap.js
|
||||
|
||||
[test_util.js]
|
||||
[test_source_node.js]
|
||||
[test_source_map_generator.js]
|
||||
[test_source_map_consumer.js]
|
||||
[test_quick_sort.js]
|
||||
[test_dog_fooding.js]
|
||||
[test_binary_search.js]
|
||||
[test_base64_vlq.js]
|
||||
[test_base64.js]
|
||||
[test_array_set.js]
|
||||
[test_api.js]
|
|
@ -0,0 +1,85 @@
|
|||
/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test WasmRemap
|
||||
|
||||
const { WasmRemap } = require("devtools/shared/wasm-source-map");
|
||||
const { SourceMapConsumer } = require("source-map");
|
||||
|
||||
const testMap1 = {
|
||||
version: 3,
|
||||
file: "min.js",
|
||||
names: [],
|
||||
sources: ["one.js", "two.js"],
|
||||
sourceRoot: "/the/root",
|
||||
mappings: "CAAC,IAAM,SACU,GAAC",
|
||||
};
|
||||
const testMap1Entries = [
|
||||
{ offset: 1, line: 1, column: 1 },
|
||||
{ offset: 5, line: 1, column: 7 },
|
||||
{ offset: 14, line: 2, column: 17 },
|
||||
{ offset: 17, line: 2, column: 18 },
|
||||
];
|
||||
const testMap2 = {
|
||||
version: 3,
|
||||
file: "none.js",
|
||||
names: [],
|
||||
sources: ["zero.js"],
|
||||
mappings: "",
|
||||
sourcesContent: ["//test"],
|
||||
};
|
||||
|
||||
function run_test() {
|
||||
const map1 = new SourceMapConsumer(testMap1);
|
||||
const remap1 = new WasmRemap(map1);
|
||||
|
||||
equal(remap1.file, "min.js");
|
||||
equal(remap1.hasContentsOfAllSources(), false);
|
||||
equal(remap1.sources.length, 2);
|
||||
equal(remap1.sources[0], "/the/root/one.js");
|
||||
equal(remap1.sources[1], "/the/root/two.js");
|
||||
|
||||
const expectedEntries = testMap1Entries.slice(0);
|
||||
remap1.eachMapping(function(entry) {
|
||||
const expected = expectedEntries.shift();
|
||||
equal(entry.generatedLine, expected.offset);
|
||||
equal(entry.generatedColumn, 0);
|
||||
equal(entry.originalLine, expected.line);
|
||||
equal(entry.originalColumn, expected.column);
|
||||
equal(entry.name, null);
|
||||
});
|
||||
|
||||
const pos1 = remap1.originalPositionFor({line: 5, column: 0});
|
||||
equal(pos1.line, 1);
|
||||
equal(pos1.column, 7);
|
||||
equal(pos1.source, "/the/root/one.js");
|
||||
|
||||
const pos2 = remap1.generatedPositionFor({
|
||||
source: "/the/root/one.js",
|
||||
line: 2,
|
||||
column: 18,
|
||||
});
|
||||
equal(pos2.line, 17);
|
||||
equal(pos2.column, 0);
|
||||
equal(pos2.lastColumn, undefined);
|
||||
|
||||
remap1.computeColumnSpans();
|
||||
const pos3 = remap1.allGeneratedPositionsFor({
|
||||
source: "/the/root/one.js",
|
||||
line: 2,
|
||||
column: 17,
|
||||
});
|
||||
equal(pos3.length, 1);
|
||||
equal(pos3[0].line, 14);
|
||||
equal(pos3[0].column, 0);
|
||||
equal(pos3[0].lastColumn, Infinity);
|
||||
|
||||
const map2 = new SourceMapConsumer(testMap2);
|
||||
const remap2 = new WasmRemap(map2);
|
||||
equal(remap2.file, "none.js");
|
||||
equal(remap2.hasContentsOfAllSources(), true);
|
||||
equal(remap2.sourceContentFor("zero.js"), "//test");
|
||||
}
|
|
@ -40,4 +40,5 @@ run-if = nightly_build
|
|||
[test_stack.js]
|
||||
[test_defer.js]
|
||||
[test_executeSoon.js]
|
||||
[test_wasm-source-map.js]
|
||||
[test_protocol_index.js]
|
||||
|
|
|
@ -579,6 +579,8 @@ this.worker = new WorkerDebuggerLoader({
|
|||
// ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
|
||||
"promise": "resource://gre/modules/Promise-backend.js",
|
||||
// ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
|
||||
"source-map": "resource://devtools/shared/sourcemap/source-map.js",
|
||||
// ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
|
||||
"xpcshell-test": "resource://test",
|
||||
// ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
|
||||
},
|
||||
|
|
Загрузка…
Ссылка в новой задаче