/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
"use strict";
const {
WorkerDispatcher,
} = require(
"resource://devtools/client/shared/worker-utils.js");
const EventEmitter = require(
"resource://devtools/shared/event-emitter.js");
const { LocalizationHelper } = require(
"resource://devtools/shared/l10n.js");
const L10N =
new LocalizationHelper(
"devtools/client/locales/toolbox.properties"
);
const SOURCE_MAP_WORKER_URL =
"resource://devtools/client/shared/source-map-loader/worker.js";
const {
originalToGeneratedId,
generatedToOriginalId,
isGeneratedId,
isOriginalId,
} = require(
"resource://devtools/client/shared/source-map-loader/utils/index.js");
class SourceMapLoader
extends WorkerDispatcher {
constructor(targetCommand) {
super(SOURCE_MAP_WORKER_URL);
this.#targetCommand = targetCommand;
}
#setSourceMapForGeneratedSources =
this.task(
"setSourceMapForGeneratedSources"
);
#getOriginalURLs =
this.task(
"getOriginalURLs");
#getOriginalSourceText =
this.task(
"getOriginalSourceText");
#targetCommand =
null;
async getOriginalURLs(urlInfo) {
try {
return await
this.#getOriginalURLs(urlInfo);
}
catch (error) {
// Note that tests may not pass the targetCommand
if (
this.#targetCommand) {
// Catch all errors and log them to the Web Console for users to see.
const message = L10N.getFormatStr(
"toolbox.sourceMapFailure",
error,
urlInfo.url,
urlInfo.sourceMapURL
);
this.#targetCommand.targetFront.logWarningInPage(message,
"source map");
}
// And re-throw the error so that the debugger can also interpret the error
throw error;
}
}
hasOriginalURL =
this.task(
"hasOriginalURL");
getOriginalRanges =
this.task(
"getOriginalRanges");
getGeneratedRanges =
this.task(
"getGeneratedRanges", {
queue:
true,
});
getGeneratedLocation =
this.task(
"getGeneratedLocation", {
queue:
true,
});
getOriginalLocation =
this.task(
"getOriginalLocation", {
queue:
true,
});
getOriginalLocations =
this.task(
"getOriginalLocations");
getGeneratedRangesForOriginal =
this.task(
"getGeneratedRangesForOriginal");
getFileGeneratedRange =
this.task(
"getFileGeneratedRange");
loadSourceMap =
this.task(
"loadSourceMap");
async getOriginalSourceText(originalSourceId) {
try {
return await
this.#getOriginalSourceText(originalSourceId);
}
catch (error) {
const message = L10N.getFormatStr(
"toolbox.sourceMapSourceFailure",
error.message,
error.metadata ? error.metadata.url :
""
);
// Note that tests may not pass the targetCommand
if (
this.#targetCommand) {
// Catch all errors and log them to the Web Console for users to see.
this.#targetCommand.targetFront.logWarningInPage(message,
"source map");
}
// Also replace the result with the error text.
// Note that this result has to have the same form
// as whatever the upstream getOriginalSourceText
// returns.
return {
text: message,
contentType:
"text/plain",
};
}
}
clearSourceMaps =
this.task(
"clearSourceMaps");
getOriginalStackFrames =
this.task(
"getOriginalStackFrames");
async setSourceMapForGeneratedSources(generatedIds, sourceMap) {
const rv = await
this.#setSourceMapForGeneratedSources(
generatedIds,
sourceMap
);
// Notify and ensure waiting for the SourceMapURLService to process the source map before resolving.
// Otherwise tests start failing because of pending request made by this component.
await
this.emitAsync(
"source-map-created", generatedIds);
return rv;
}
destroy() {
// Request to stop the underlying DOM Worker
this.stop();
// Unregister all listeners
this.clearEvents();
// SourceMapLoader may be leaked and so it is important to clear most outer references
this.#targetCommand =
null;
}
}
EventEmitter.decorate(SourceMapLoader.prototype);
module.exports = {
SourceMapLoader,
originalToGeneratedId,
generatedToOriginalId,
isGeneratedId,
isOriginalId,
};