diff --git a/toolkit/devtools/sourcemap/SourceMap.jsm b/toolkit/devtools/sourcemap/SourceMap.jsm
index 3b829efa11cb..62dce36e2c6f 100644
--- a/toolkit/devtools/sourcemap/SourceMap.jsm
+++ b/toolkit/devtools/sourcemap/SourceMap.jsm
@@ -1,4 +1,4 @@
-/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
+/* -*- Mode: js; js-indent-level: 2; -*- */
/*
* Copyright 2011 Mozilla Foundation and contributors
* Licensed under the New BSD license. See LICENSE or:
@@ -46,7 +46,7 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou
* - sourceRoot: Optional. The URL root from which all sources are relative.
* - sourcesContent: Optional. An array of contents of the original source files.
* - mappings: A string of base64 VLQs which contain the actual mappings.
- * - file: The generated file this source map is associated with.
+ * - file: Optional. The generated file this source map is associated with.
*
* Here is an example source map, taken from the source map spec[0]:
*
@@ -133,7 +133,7 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou
Object.defineProperty(SourceMapConsumer.prototype, 'sources', {
get: function () {
return this._sources.toArray().map(function (s) {
- return this.sourceRoot ? util.join(this.sourceRoot, s) : s;
+ return this.sourceRoot != null ? util.join(this.sourceRoot, s) : s;
}, this);
}
});
@@ -194,6 +194,12 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou
}
});
+ SourceMapConsumer.prototype._nextCharIsMappingSeparator =
+ function SourceMapConsumer_nextCharIsMappingSeparator(aStr) {
+ var c = aStr.charAt(0);
+ return c === ";" || c === ",";
+ };
+
/**
* Parse the mappings in a string in to a data structure which we can easily
* query (the ordered arrays in the `this.__generatedMappings` and
@@ -207,10 +213,9 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou
var previousOriginalColumn = 0;
var previousSource = 0;
var previousName = 0;
- var mappingSeparator = /^[,;]/;
var str = aStr;
+ var temp = {};
var mapping;
- var temp;
while (str.length > 0) {
if (str.charAt(0) === ';') {
@@ -226,41 +231,41 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou
mapping.generatedLine = generatedLine;
// Generated column.
- temp = base64VLQ.decode(str);
+ base64VLQ.decode(str, temp);
mapping.generatedColumn = previousGeneratedColumn + temp.value;
previousGeneratedColumn = mapping.generatedColumn;
str = temp.rest;
- if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) {
+ if (str.length > 0 && !this._nextCharIsMappingSeparator(str)) {
// Original source.
- temp = base64VLQ.decode(str);
+ base64VLQ.decode(str, temp);
mapping.source = this._sources.at(previousSource + temp.value);
previousSource += temp.value;
str = temp.rest;
- if (str.length === 0 || mappingSeparator.test(str.charAt(0))) {
+ if (str.length === 0 || this._nextCharIsMappingSeparator(str)) {
throw new Error('Found a source, but no line and column');
}
// Original line.
- temp = base64VLQ.decode(str);
+ base64VLQ.decode(str, temp);
mapping.originalLine = previousOriginalLine + temp.value;
previousOriginalLine = mapping.originalLine;
// Lines are stored 0-based
mapping.originalLine += 1;
str = temp.rest;
- if (str.length === 0 || mappingSeparator.test(str.charAt(0))) {
+ if (str.length === 0 || this._nextCharIsMappingSeparator(str)) {
throw new Error('Found a source and line, but no column');
}
// Original column.
- temp = base64VLQ.decode(str);
+ base64VLQ.decode(str, temp);
mapping.originalColumn = previousOriginalColumn + temp.value;
previousOriginalColumn = mapping.originalColumn;
str = temp.rest;
- if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) {
+ if (str.length > 0 && !this._nextCharIsMappingSeparator(str)) {
// Original name.
- temp = base64VLQ.decode(str);
+ base64VLQ.decode(str, temp);
mapping.name = this._names.at(previousName + temp.value);
previousName += temp.value;
str = temp.rest;
@@ -274,6 +279,7 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou
}
}
+ this.__generatedMappings.sort(util.compareByGeneratedPositions);
this.__originalMappings.sort(util.compareByOriginalPositions);
};
@@ -329,9 +335,9 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou
"generatedColumn",
util.compareByGeneratedPositions);
- if (mapping) {
+ if (mapping && mapping.generatedLine === needle.generatedLine) {
var source = util.getArg(mapping, 'source', null);
- if (source && this.sourceRoot) {
+ if (source != null && this.sourceRoot != null) {
source = util.join(this.sourceRoot, source);
}
return {
@@ -361,7 +367,7 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou
return null;
}
- if (this.sourceRoot) {
+ if (this.sourceRoot != null) {
aSource = util.relative(this.sourceRoot, aSource);
}
@@ -370,7 +376,7 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou
}
var url;
- if (this.sourceRoot
+ if (this.sourceRoot != null
&& (url = util.urlParse(this.sourceRoot))) {
// XXX: file:// URIs and absolute paths lead to unexpected behavior for
// many users. We can help them out when they expect file:// URIs to
@@ -413,7 +419,7 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou
originalColumn: util.getArg(aArgs, 'column')
};
- if (this.sourceRoot) {
+ if (this.sourceRoot != null) {
needle.source = util.relative(this.sourceRoot, needle.source);
}
@@ -475,7 +481,7 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou
var sourceRoot = this.sourceRoot;
mappings.map(function (mapping) {
var source = mapping.source;
- if (source && sourceRoot) {
+ if (source != null && sourceRoot != null) {
source = util.join(sourceRoot, source);
}
return {
@@ -521,8 +527,8 @@ define('source-map/util', ['require', 'exports', 'module' , ], function(require,
}
exports.getArg = getArg;
- var urlRegexp = /([\w+\-.]+):\/\/((\w+:\w+)@)?([\w.]+)?(:(\d+))?(\S+)?/;
- var dataUrlRegexp = /^data:.+\,.+/;
+ var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/;
+ var dataUrlRegexp = /^data:.+\,.+$/;
function urlParse(aUrl) {
var match = aUrl.match(urlRegexp);
@@ -531,18 +537,22 @@ define('source-map/util', ['require', 'exports', 'module' , ], function(require,
}
return {
scheme: match[1],
- auth: match[3],
- host: match[4],
- port: match[6],
- path: match[7]
+ auth: match[2],
+ host: match[3],
+ port: match[4],
+ path: match[5]
};
}
exports.urlParse = urlParse;
function urlGenerate(aParsedUrl) {
- var url = aParsedUrl.scheme + "://";
+ var url = '';
+ if (aParsedUrl.scheme) {
+ url += aParsedUrl.scheme + ':';
+ }
+ url += '//';
if (aParsedUrl.auth) {
- url += aParsedUrl.auth + "@"
+ url += aParsedUrl.auth + '@';
}
if (aParsedUrl.host) {
url += aParsedUrl.host;
@@ -557,22 +567,146 @@ define('source-map/util', ['require', 'exports', 'module' , ], function(require,
}
exports.urlGenerate = urlGenerate;
- function join(aRoot, aPath) {
- var url;
+ /**
+ * Normalizes a path, or the path portion of a URL:
+ *
+ * - Replaces consequtive slashes with one slash.
+ * - Removes unnecessary '.' parts.
+ * - Removes unnecessary '
/..' parts.
+ *
+ * Based on code in the Node.js 'path' core module.
+ *
+ * @param aPath The path or url to normalize.
+ */
+ function normalize(aPath) {
+ var path = aPath;
+ var url = urlParse(aPath);
+ if (url) {
+ if (!url.path) {
+ return aPath;
+ }
+ path = url.path;
+ }
+ var isAbsolute = (path.charAt(0) === '/');
- if (aPath.match(urlRegexp) || aPath.match(dataUrlRegexp)) {
+ var parts = path.split(/\/+/);
+ for (var part, up = 0, i = parts.length - 1; i >= 0; i--) {
+ part = parts[i];
+ if (part === '.') {
+ parts.splice(i, 1);
+ } else if (part === '..') {
+ up++;
+ } else if (up > 0) {
+ if (part === '') {
+ // The first part is blank if the path is absolute. Trying to go
+ // above the root is a no-op. Therefore we can remove all '..' parts
+ // directly after the root.
+ parts.splice(i + 1, up);
+ up = 0;
+ } else {
+ parts.splice(i, 2);
+ up--;
+ }
+ }
+ }
+ path = parts.join('/');
+
+ if (path === '') {
+ path = isAbsolute ? '/' : '.';
+ }
+
+ if (url) {
+ url.path = path;
+ return urlGenerate(url);
+ }
+ return path;
+ }
+ exports.normalize = normalize;
+
+ /**
+ * Joins two paths/URLs.
+ *
+ * @param aRoot The root path or URL.
+ * @param aPath The path or URL to be joined with the root.
+ *
+ * - If aPath is a URL or a data URI, aPath is returned, unless aPath is a
+ * scheme-relative URL: Then the scheme of aRoot, if any, is prepended
+ * first.
+ * - Otherwise aPath is a path. If aRoot is a URL, then its path portion
+ * is updated with the result and aRoot is returned. Otherwise the result
+ * is returned.
+ * - If aPath is absolute, the result is aPath.
+ * - Otherwise the two paths are joined with a slash.
+ * - Joining for example 'http://' and 'www.example.com' is also supported.
+ */
+ function join(aRoot, aPath) {
+ if (aRoot === "") {
+ aRoot = ".";
+ }
+ if (aPath === "") {
+ aPath = ".";
+ }
+ var aPathUrl = urlParse(aPath);
+ var aRootUrl = urlParse(aRoot);
+ if (aRootUrl) {
+ aRoot = aRootUrl.path || '/';
+ }
+
+ // `join(foo, '//www.example.org')`
+ if (aPathUrl && !aPathUrl.scheme) {
+ if (aRootUrl) {
+ aPathUrl.scheme = aRootUrl.scheme;
+ }
+ return urlGenerate(aPathUrl);
+ }
+
+ if (aPathUrl || aPath.match(dataUrlRegexp)) {
return aPath;
}
- if (aPath.charAt(0) === '/' && (url = urlParse(aRoot))) {
- url.path = aPath;
- return urlGenerate(url);
+ // `join('http://', 'www.example.com')`
+ if (aRootUrl && !aRootUrl.host && !aRootUrl.path) {
+ aRootUrl.host = aPath;
+ return urlGenerate(aRootUrl);
}
- return aRoot.replace(/\/$/, '') + '/' + aPath;
+ var joined = aPath.charAt(0) === '/'
+ ? aPath
+ : normalize(aRoot.replace(/\/+$/, '') + '/' + aPath);
+
+ if (aRootUrl) {
+ aRootUrl.path = joined;
+ return urlGenerate(aRootUrl);
+ }
+ return joined;
}
exports.join = join;
+ /**
+ * Make a path relative to a URL or another path.
+ *
+ * @param aRoot The root path or URL.
+ * @param aPath The path or URL to be made relative to aRoot.
+ */
+ function relative(aRoot, aPath) {
+ if (aRoot === "") {
+ aRoot = ".";
+ }
+
+ aRoot = aRoot.replace(/\/$/, '');
+
+ // XXX: It is possible to remove this block, and the tests still pass!
+ var url = urlParse(aRoot);
+ if (aPath.charAt(0) == "/" && url && url.path == "/") {
+ return aPath.slice(1);
+ }
+
+ return aPath.indexOf(aRoot + '/') === 0
+ ? aPath.substr(aRoot.length + 1)
+ : aPath;
+ }
+ exports.relative = relative;
+
/**
* Because behavior goes wacky when you set `__proto__` on objects, we
* have to prefix all the strings in our set with an arbitrary character.
@@ -592,20 +726,6 @@ define('source-map/util', ['require', 'exports', 'module' , ], function(require,
}
exports.fromSetString = fromSetString;
- function relative(aRoot, aPath) {
- aRoot = aRoot.replace(/\/$/, '');
-
- var url = urlParse(aRoot);
- if (aPath.charAt(0) == "/" && url && url.path == "/") {
- return aPath.slice(1);
- }
-
- return aPath.indexOf(aRoot + '/') === 0
- ? aPath.substr(aRoot.length + 1)
- : aPath;
- }
- exports.relative = relative;
-
function strcmp(aStr1, aStr2) {
var s1 = aStr1 || "";
var s2 = aStr2 || "";
@@ -980,9 +1100,9 @@ define('source-map/base64-vlq', ['require', 'exports', 'module' , 'source-map/b
/**
* Decodes the next base 64 VLQ value from the given string and returns the
- * value and the rest of the string.
+ * value and the rest of the string via the out parameter.
*/
- exports.decode = function base64VLQ_decode(aStr) {
+ exports.decode = function base64VLQ_decode(aStr, aOutParam) {
var i = 0;
var strLen = aStr.length;
var result = 0;
@@ -1000,10 +1120,8 @@ define('source-map/base64-vlq', ['require', 'exports', 'module' , 'source-map/b
shift += VLQ_BASE_SHIFT;
} while (continuation);
- return {
- value: fromVLQSigned(result),
- rest: aStr.slice(i)
- };
+ aOutParam.value = fromVLQSigned(result);
+ aOutParam.rest = aStr.slice(i);
};
});
@@ -1060,14 +1178,17 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
/**
* An instance of the SourceMapGenerator represents a source map which is
- * being built incrementally. To create a new one, you must pass an object
- * with the following properties:
+ * being built incrementally. You may pass an object with the following
+ * properties:
*
* - file: The filename of the generated source.
- * - sourceRoot: An optional root for all URLs in this source map.
+ * - sourceRoot: A root for all relative URLs in this source map.
*/
function SourceMapGenerator(aArgs) {
- this._file = util.getArg(aArgs, 'file');
+ if (!aArgs) {
+ aArgs = {};
+ }
+ this._file = util.getArg(aArgs, 'file', null);
this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null);
this._sources = new ArraySet();
this._names = new ArraySet();
@@ -1097,9 +1218,9 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
}
};
- if (mapping.source) {
+ if (mapping.source != null) {
newMapping.source = mapping.source;
- if (sourceRoot) {
+ if (sourceRoot != null) {
newMapping.source = util.relative(sourceRoot, newMapping.source);
}
@@ -1108,7 +1229,7 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
column: mapping.originalColumn
};
- if (mapping.name) {
+ if (mapping.name != null) {
newMapping.name = mapping.name;
}
}
@@ -1117,7 +1238,7 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
});
aSourceMapConsumer.sources.forEach(function (sourceFile) {
var content = aSourceMapConsumer.sourceContentFor(sourceFile);
- if (content) {
+ if (content != null) {
generator.setSourceContent(sourceFile, content);
}
});
@@ -1143,11 +1264,11 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
this._validateMapping(generated, original, source, name);
- if (source && !this._sources.has(source)) {
+ if (source != null && !this._sources.has(source)) {
this._sources.add(source);
}
- if (name && !this._names.has(name)) {
+ if (name != null && !this._names.has(name)) {
this._names.add(name);
}
@@ -1167,18 +1288,18 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
SourceMapGenerator.prototype.setSourceContent =
function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) {
var source = aSourceFile;
- if (this._sourceRoot) {
+ if (this._sourceRoot != null) {
source = util.relative(this._sourceRoot, source);
}
- if (aSourceContent !== null) {
+ if (aSourceContent != null) {
// Add the source content to the _sourcesContents map.
// Create a new _sourcesContents map if the property is null.
if (!this._sourcesContents) {
this._sourcesContents = {};
}
this._sourcesContents[util.toSetString(source)] = aSourceContent;
- } else {
+ } else if (this._sourcesContents) {
// Remove the source file from the _sourcesContents map.
// If the _sourcesContents map is empty, set the property to null.
delete this._sourcesContents[util.toSetString(source)];
@@ -1197,55 +1318,68 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
* @param aSourceMapConsumer The source map to be applied.
* @param aSourceFile Optional. The filename of the source file.
* If omitted, SourceMapConsumer's file property will be used.
+ * @param aSourceMapPath Optional. The dirname of the path to the source map
+ * to be applied. If relative, it is relative to the SourceMapConsumer.
+ * This parameter is needed when the two source maps aren't in the same
+ * directory, and the source map to be applied contains relative source
+ * paths. If so, those relative source paths need to be rewritten
+ * relative to the SourceMapGenerator.
*/
SourceMapGenerator.prototype.applySourceMap =
- function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile) {
+ function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile, aSourceMapPath) {
+ var sourceFile = aSourceFile;
// If aSourceFile is omitted, we will use the file property of the SourceMap
- if (!aSourceFile) {
- aSourceFile = aSourceMapConsumer.file;
+ if (aSourceFile == null) {
+ if (aSourceMapConsumer.file == null) {
+ throw new Error(
+ 'SourceMapGenerator.prototype.applySourceMap requires either an explicit source file, ' +
+ 'or the source map\'s "file" property. Both were omitted.'
+ );
+ }
+ sourceFile = aSourceMapConsumer.file;
}
var sourceRoot = this._sourceRoot;
- // Make "aSourceFile" relative if an absolute Url is passed.
- if (sourceRoot) {
- aSourceFile = util.relative(sourceRoot, aSourceFile);
+ // Make "sourceFile" relative if an absolute Url is passed.
+ if (sourceRoot != null) {
+ sourceFile = util.relative(sourceRoot, sourceFile);
}
// Applying the SourceMap can add and remove items from the sources and
// the names array.
var newSources = new ArraySet();
var newNames = new ArraySet();
- // Find mappings for the "aSourceFile"
+ // Find mappings for the "sourceFile"
this._mappings.forEach(function (mapping) {
- if (mapping.source === aSourceFile && mapping.originalLine) {
+ if (mapping.source === sourceFile && mapping.originalLine != null) {
// Check if it can be mapped by the source map, then update the mapping.
var original = aSourceMapConsumer.originalPositionFor({
line: mapping.originalLine,
column: mapping.originalColumn
});
- if (original.source !== null) {
+ if (original.source != null) {
// Copy mapping
- if (sourceRoot) {
- mapping.source = util.relative(sourceRoot, original.source);
- } else {
- mapping.source = original.source;
+ mapping.source = original.source;
+ if (aSourceMapPath != null) {
+ mapping.source = util.join(aSourceMapPath, mapping.source)
+ }
+ if (sourceRoot != null) {
+ mapping.source = util.relative(sourceRoot, mapping.source);
}
mapping.originalLine = original.line;
mapping.originalColumn = original.column;
- if (original.name !== null && mapping.name !== null) {
- // Only use the identifier name if it's an identifier
- // in both SourceMaps
+ if (original.name != null) {
mapping.name = original.name;
}
}
}
var source = mapping.source;
- if (source && !newSources.has(source)) {
+ if (source != null && !newSources.has(source)) {
newSources.add(source);
}
var name = mapping.name;
- if (name && !newNames.has(name)) {
+ if (name != null && !newNames.has(name)) {
newNames.add(name);
}
@@ -1256,8 +1390,11 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
// Copy sourcesContents of applied map.
aSourceMapConsumer.sources.forEach(function (sourceFile) {
var content = aSourceMapConsumer.sourceContentFor(sourceFile);
- if (content) {
- if (sourceRoot) {
+ if (content != null) {
+ if (aSourceMapPath != null) {
+ sourceFile = util.join(aSourceMapPath, sourceFile);
+ }
+ if (sourceRoot != null) {
sourceFile = util.relative(sourceRoot, sourceFile);
}
this.setSourceContent(sourceFile, content);
@@ -1348,7 +1485,7 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
- previousGeneratedColumn);
previousGeneratedColumn = mapping.generatedColumn;
- if (mapping.source) {
+ if (mapping.source != null) {
result += base64VLQ.encode(this._sources.indexOf(mapping.source)
- previousSource);
previousSource = this._sources.indexOf(mapping.source);
@@ -1362,7 +1499,7 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
- previousOriginalColumn);
previousOriginalColumn = mapping.originalColumn;
- if (mapping.name) {
+ if (mapping.name != null) {
result += base64VLQ.encode(this._names.indexOf(mapping.name)
- previousName);
previousName = this._names.indexOf(mapping.name);
@@ -1379,7 +1516,7 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
if (!this._sourcesContents) {
return null;
}
- if (aSourceRoot) {
+ if (aSourceRoot != null) {
source = util.relative(aSourceRoot, source);
}
var key = util.toSetString(source);
@@ -1397,17 +1534,18 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
function SourceMapGenerator_toJSON() {
var map = {
version: this._version,
- file: this._file,
sources: this._sources.toArray(),
names: this._names.toArray(),
mappings: this._serializeMappings()
};
- if (this._sourceRoot) {
+ if (this._file != null) {
+ map.file = this._file;
+ }
+ if (this._sourceRoot != null) {
map.sourceRoot = this._sourceRoot;
}
if (this._sourcesContents) {
- map.sourcesContent = this._generateSourcesContent(map.sources,
- map.sourceRoot || undefined);
+ map.sourcesContent = this._generateSourcesContent(map.sources, map.sourceRoot);
}
return map;
@@ -1435,6 +1573,13 @@ define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/
var SourceMapGenerator = require('source-map/source-map-generator').SourceMapGenerator;
var util = require('source-map/util');
+ // Matches a Windows-style `\r\n` newline or a `\n` newline used by all other
+ // operating systems these days (capturing the result).
+ var REGEX_NEWLINE = /(\r?\n)/;
+
+ // Matches a Windows-style newline, or any character.
+ var REGEX_CHARACTER = /\r\n|[\s\S]/g;
+
/**
* SourceNodes provide a way to abstract over interpolating/concatenating
* snippets of generated JavaScript source code while maintaining the line and
@@ -1450,10 +1595,10 @@ define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/
function SourceNode(aLine, aColumn, aSource, aChunks, aName) {
this.children = [];
this.sourceContents = {};
- this.line = aLine === undefined ? null : aLine;
- this.column = aColumn === undefined ? null : aColumn;
- this.source = aSource === undefined ? null : aSource;
- this.name = aName === undefined ? null : aName;
+ this.line = aLine == null ? null : aLine;
+ this.column = aColumn == null ? null : aColumn;
+ this.source = aSource == null ? null : aSource;
+ this.name = aName == null ? null : aName;
if (aChunks != null) this.add(aChunks);
}
@@ -1462,16 +1607,26 @@ define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/
*
* @param aGeneratedCode The generated code
* @param aSourceMapConsumer The SourceMap for the generated code
+ * @param aRelativePath Optional. The path that relative sources in the
+ * SourceMapConsumer should be relative to.
*/
SourceNode.fromStringWithSourceMap =
- function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer) {
+ function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer, aRelativePath) {
// The SourceNode we want to fill with the generated code
// and the SourceMap
var node = new SourceNode();
- // The generated code
- // Processed fragments are removed from this array.
- var remainingLines = aGeneratedCode.split('\n');
+ // All even indices of this array are one line of the generated code,
+ // while all odd indices are the newlines between two adjacent lines
+ // (since `REGEX_NEWLINE` captures its match).
+ // Processed fragments are removed from this array, by calling `shiftNextLine`.
+ var remainingLines = aGeneratedCode.split(REGEX_NEWLINE);
+ var shiftNextLine = function() {
+ var lineContents = remainingLines.shift();
+ // The last line of a file might not have a newline.
+ var newLine = remainingLines.shift() || "";
+ return lineContents + newLine;
+ };
// We need to remember the position of "remainingLines"
var lastGeneratedLine = 1, lastGeneratedColumn = 0;
@@ -1482,41 +1637,16 @@ define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/
var lastMapping = null;
aSourceMapConsumer.eachMapping(function (mapping) {
- if (lastMapping === null) {
- // We add the generated code until the first mapping
- // to the SourceNode without any mapping.
- // Each line is added as separate string.
- while (lastGeneratedLine < mapping.generatedLine) {
- node.add(remainingLines.shift() + "\n");
- lastGeneratedLine++;
- }
- if (lastGeneratedColumn < mapping.generatedColumn) {
- var nextLine = remainingLines[0];
- node.add(nextLine.substr(0, mapping.generatedColumn));
- remainingLines[0] = nextLine.substr(mapping.generatedColumn);
- lastGeneratedColumn = mapping.generatedColumn;
- }
- } else {
+ if (lastMapping !== null) {
// We add the code from "lastMapping" to "mapping":
// First check if there is a new line in between.
if (lastGeneratedLine < mapping.generatedLine) {
var code = "";
- // Associate full lines with "lastMapping"
- do {
- code += remainingLines.shift() + "\n";
- lastGeneratedLine++;
- lastGeneratedColumn = 0;
- } while (lastGeneratedLine < mapping.generatedLine);
- // When we reached the correct line, we add code until we
- // reach the correct column too.
- if (lastGeneratedColumn < mapping.generatedColumn) {
- var nextLine = remainingLines[0];
- code += nextLine.substr(0, mapping.generatedColumn);
- remainingLines[0] = nextLine.substr(mapping.generatedColumn);
- lastGeneratedColumn = mapping.generatedColumn;
- }
- // Create the SourceNode.
- addMappingWithCode(lastMapping, code);
+ // Associate first line with "lastMapping"
+ addMappingWithCode(lastMapping, shiftNextLine());
+ lastGeneratedLine++;
+ lastGeneratedColumn = 0;
+ // The remaining code is added without mapping
} else {
// There is no new line in between.
// Associate the code between "lastGeneratedColumn" and
@@ -1528,19 +1658,43 @@ define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/
lastGeneratedColumn);
lastGeneratedColumn = mapping.generatedColumn;
addMappingWithCode(lastMapping, code);
+ // No more remaining code, continue
+ lastMapping = mapping;
+ return;
}
}
+ // We add the generated code until the first mapping
+ // to the SourceNode without any mapping.
+ // Each line is added as separate string.
+ while (lastGeneratedLine < mapping.generatedLine) {
+ node.add(shiftNextLine());
+ lastGeneratedLine++;
+ }
+ if (lastGeneratedColumn < mapping.generatedColumn) {
+ var nextLine = remainingLines[0];
+ node.add(nextLine.substr(0, mapping.generatedColumn));
+ remainingLines[0] = nextLine.substr(mapping.generatedColumn);
+ lastGeneratedColumn = mapping.generatedColumn;
+ }
lastMapping = mapping;
}, this);
// We have processed all mappings.
- // Associate the remaining code in the current line with "lastMapping"
- // and add the remaining lines without any mapping
- addMappingWithCode(lastMapping, remainingLines.join("\n"));
+ if (remainingLines.length > 0) {
+ if (lastMapping) {
+ // Associate the remaining code in the current line with "lastMapping"
+ addMappingWithCode(lastMapping, shiftNextLine());
+ }
+ // and add the remaining lines without any mapping
+ node.add(remainingLines.join(""));
+ }
// Copy sourcesContent into SourceNode
aSourceMapConsumer.sources.forEach(function (sourceFile) {
var content = aSourceMapConsumer.sourceContentFor(sourceFile);
- if (content) {
+ if (content != null) {
+ if (aRelativePath != null) {
+ sourceFile = util.join(aRelativePath, sourceFile);
+ }
node.setSourceContent(sourceFile, content);
}
});
@@ -1551,9 +1705,12 @@ define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/
if (mapping === null || mapping.source === undefined) {
node.add(code);
} else {
+ var source = aRelativePath
+ ? util.join(aRelativePath, mapping.source)
+ : mapping.source;
node.add(new SourceNode(mapping.originalLine,
mapping.originalColumn,
- mapping.source,
+ source,
code,
mapping.name));
}
@@ -1773,12 +1930,30 @@ define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/
lastOriginalSource = null;
sourceMappingActive = false;
}
- chunk.split('').forEach(function (ch) {
- if (ch === '\n') {
+ chunk.match(REGEX_CHARACTER).forEach(function (ch, idx, array) {
+ if (REGEX_NEWLINE.test(ch)) {
generated.line++;
generated.column = 0;
+ // Mappings end at eol
+ if (idx + 1 === array.length) {
+ lastOriginalSource = null;
+ sourceMappingActive = false;
+ } else if (sourceMappingActive) {
+ map.addMapping({
+ source: original.source,
+ original: {
+ line: original.line,
+ column: original.column
+ },
+ generated: {
+ line: generated.line,
+ column: generated.column
+ },
+ name: original.name
+ });
+ }
} else {
- generated.column++;
+ generated.column += ch.length;
}
});
});
diff --git a/toolkit/devtools/sourcemap/tests/unit/Utils.jsm b/toolkit/devtools/sourcemap/tests/unit/Utils.jsm
index fa1f0ae89160..5142e5e68a58 100644
--- a/toolkit/devtools/sourcemap/tests/unit/Utils.jsm
+++ b/toolkit/devtools/sourcemap/tests/unit/Utils.jsm
@@ -1,4 +1,4 @@
-/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
+/* -*- Mode: js; js-indent-level: 2; -*- */
/*
* Copyright 2011 Mozilla Foundation and contributors
* Licensed under the New BSD license. See LICENSE or:
@@ -111,6 +111,21 @@ define('test/source-map/util', ['require', 'exports', 'module' , 'lib/source-ma
sourceRoot: '/the/root',
mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
};
+ exports.testMapNoSourceRoot = {
+ version: 3,
+ file: 'min.js',
+ names: ['bar', 'baz', 'n'],
+ sources: ['one.js', 'two.js'],
+ mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
+ };
+ exports.testMapEmptySourceRoot = {
+ version: 3,
+ file: 'min.js',
+ names: ['bar', 'baz', 'n'],
+ sources: ['one.js', 'two.js'],
+ sourceRoot: '',
+ mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
+ };
exports.testMapWithSourcesContent = {
version: 3,
file: 'min.js',
@@ -259,8 +274,8 @@ define('lib/source-map/util', ['require', 'exports', 'module' , ], function(requ
}
exports.getArg = getArg;
- var urlRegexp = /([\w+\-.]+):\/\/((\w+:\w+)@)?([\w.]+)?(:(\d+))?(\S+)?/;
- var dataUrlRegexp = /^data:.+\,.+/;
+ var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/;
+ var dataUrlRegexp = /^data:.+\,.+$/;
function urlParse(aUrl) {
var match = aUrl.match(urlRegexp);
@@ -269,18 +284,22 @@ define('lib/source-map/util', ['require', 'exports', 'module' , ], function(requ
}
return {
scheme: match[1],
- auth: match[3],
- host: match[4],
- port: match[6],
- path: match[7]
+ auth: match[2],
+ host: match[3],
+ port: match[4],
+ path: match[5]
};
}
exports.urlParse = urlParse;
function urlGenerate(aParsedUrl) {
- var url = aParsedUrl.scheme + "://";
+ var url = '';
+ if (aParsedUrl.scheme) {
+ url += aParsedUrl.scheme + ':';
+ }
+ url += '//';
if (aParsedUrl.auth) {
- url += aParsedUrl.auth + "@"
+ url += aParsedUrl.auth + '@';
}
if (aParsedUrl.host) {
url += aParsedUrl.host;
@@ -295,22 +314,146 @@ define('lib/source-map/util', ['require', 'exports', 'module' , ], function(requ
}
exports.urlGenerate = urlGenerate;
- function join(aRoot, aPath) {
- var url;
+ /**
+ * Normalizes a path, or the path portion of a URL:
+ *
+ * - Replaces consequtive slashes with one slash.
+ * - Removes unnecessary '.' parts.
+ * - Removes unnecessary '/..' parts.
+ *
+ * Based on code in the Node.js 'path' core module.
+ *
+ * @param aPath The path or url to normalize.
+ */
+ function normalize(aPath) {
+ var path = aPath;
+ var url = urlParse(aPath);
+ if (url) {
+ if (!url.path) {
+ return aPath;
+ }
+ path = url.path;
+ }
+ var isAbsolute = (path.charAt(0) === '/');
- if (aPath.match(urlRegexp) || aPath.match(dataUrlRegexp)) {
+ var parts = path.split(/\/+/);
+ for (var part, up = 0, i = parts.length - 1; i >= 0; i--) {
+ part = parts[i];
+ if (part === '.') {
+ parts.splice(i, 1);
+ } else if (part === '..') {
+ up++;
+ } else if (up > 0) {
+ if (part === '') {
+ // The first part is blank if the path is absolute. Trying to go
+ // above the root is a no-op. Therefore we can remove all '..' parts
+ // directly after the root.
+ parts.splice(i + 1, up);
+ up = 0;
+ } else {
+ parts.splice(i, 2);
+ up--;
+ }
+ }
+ }
+ path = parts.join('/');
+
+ if (path === '') {
+ path = isAbsolute ? '/' : '.';
+ }
+
+ if (url) {
+ url.path = path;
+ return urlGenerate(url);
+ }
+ return path;
+ }
+ exports.normalize = normalize;
+
+ /**
+ * Joins two paths/URLs.
+ *
+ * @param aRoot The root path or URL.
+ * @param aPath The path or URL to be joined with the root.
+ *
+ * - If aPath is a URL or a data URI, aPath is returned, unless aPath is a
+ * scheme-relative URL: Then the scheme of aRoot, if any, is prepended
+ * first.
+ * - Otherwise aPath is a path. If aRoot is a URL, then its path portion
+ * is updated with the result and aRoot is returned. Otherwise the result
+ * is returned.
+ * - If aPath is absolute, the result is aPath.
+ * - Otherwise the two paths are joined with a slash.
+ * - Joining for example 'http://' and 'www.example.com' is also supported.
+ */
+ function join(aRoot, aPath) {
+ if (aRoot === "") {
+ aRoot = ".";
+ }
+ if (aPath === "") {
+ aPath = ".";
+ }
+ var aPathUrl = urlParse(aPath);
+ var aRootUrl = urlParse(aRoot);
+ if (aRootUrl) {
+ aRoot = aRootUrl.path || '/';
+ }
+
+ // `join(foo, '//www.example.org')`
+ if (aPathUrl && !aPathUrl.scheme) {
+ if (aRootUrl) {
+ aPathUrl.scheme = aRootUrl.scheme;
+ }
+ return urlGenerate(aPathUrl);
+ }
+
+ if (aPathUrl || aPath.match(dataUrlRegexp)) {
return aPath;
}
- if (aPath.charAt(0) === '/' && (url = urlParse(aRoot))) {
- url.path = aPath;
- return urlGenerate(url);
+ // `join('http://', 'www.example.com')`
+ if (aRootUrl && !aRootUrl.host && !aRootUrl.path) {
+ aRootUrl.host = aPath;
+ return urlGenerate(aRootUrl);
}
- return aRoot.replace(/\/$/, '') + '/' + aPath;
+ var joined = aPath.charAt(0) === '/'
+ ? aPath
+ : normalize(aRoot.replace(/\/+$/, '') + '/' + aPath);
+
+ if (aRootUrl) {
+ aRootUrl.path = joined;
+ return urlGenerate(aRootUrl);
+ }
+ return joined;
}
exports.join = join;
+ /**
+ * Make a path relative to a URL or another path.
+ *
+ * @param aRoot The root path or URL.
+ * @param aPath The path or URL to be made relative to aRoot.
+ */
+ function relative(aRoot, aPath) {
+ if (aRoot === "") {
+ aRoot = ".";
+ }
+
+ aRoot = aRoot.replace(/\/$/, '');
+
+ // XXX: It is possible to remove this block, and the tests still pass!
+ var url = urlParse(aRoot);
+ if (aPath.charAt(0) == "/" && url && url.path == "/") {
+ return aPath.slice(1);
+ }
+
+ return aPath.indexOf(aRoot + '/') === 0
+ ? aPath.substr(aRoot.length + 1)
+ : aPath;
+ }
+ exports.relative = relative;
+
/**
* Because behavior goes wacky when you set `__proto__` on objects, we
* have to prefix all the strings in our set with an arbitrary character.
@@ -330,20 +473,6 @@ define('lib/source-map/util', ['require', 'exports', 'module' , ], function(requ
}
exports.fromSetString = fromSetString;
- function relative(aRoot, aPath) {
- aRoot = aRoot.replace(/\/$/, '');
-
- var url = urlParse(aRoot);
- if (aPath.charAt(0) == "/" && url && url.path == "/") {
- return aPath.slice(1);
- }
-
- return aPath.indexOf(aRoot + '/') === 0
- ? aPath.substr(aRoot.length + 1)
- : aPath;
- }
- exports.relative = relative;
-
function strcmp(aStr1, aStr2) {
var s1 = aStr1 || "";
var s2 = aStr2 || "";
diff --git a/toolkit/devtools/sourcemap/tests/unit/test_base64_vlq.js b/toolkit/devtools/sourcemap/tests/unit/test_base64_vlq.js
index 4ca3d2d75477..4b8c52efafe6 100644
--- a/toolkit/devtools/sourcemap/tests/unit/test_base64_vlq.js
+++ b/toolkit/devtools/sourcemap/tests/unit/test_base64_vlq.js
@@ -17,10 +17,9 @@ define("test/source-map/test-base64-vlq", ["require", "exports", "module"], func
var base64VLQ = require('source-map/base64-vlq');
exports['test normal encoding and decoding'] = function (assert, util) {
- var result;
+ var result = {};
for (var i = -255; i < 256; i++) {
- result = base64VLQ.decode(base64VLQ.encode(i));
- assert.ok(result);
+ base64VLQ.decode(base64VLQ.encode(i), result);
assert.equal(result.value, i);
assert.equal(result.rest, "");
}
diff --git a/toolkit/devtools/sourcemap/tests/unit/test_dog_fooding.js b/toolkit/devtools/sourcemap/tests/unit/test_dog_fooding.js
index 8fb24f4c587b..77017063b402 100644
--- a/toolkit/devtools/sourcemap/tests/unit/test_dog_fooding.js
+++ b/toolkit/devtools/sourcemap/tests/unit/test_dog_fooding.js
@@ -47,6 +47,12 @@ define("test/source-map/test-dog-fooding", ["require", "exports", "module"], fun
generated: { line: 5, column: 2 }
});
+ smg.addMapping({
+ source: 'gza.coffee',
+ original: { line: 5, column: 10 },
+ generated: { line: 6, column: 12 }
+ });
+
var smc = new SourceMapConsumer(smg.toString());
// Exact
@@ -54,24 +60,30 @@ define("test/source-map/test-dog-fooding", ["require", "exports", "module"], fun
util.assertMapping(3, 2, '/wu/tang/gza.coffee', 2, 0, null, smc, assert);
util.assertMapping(4, 2, '/wu/tang/gza.coffee', 3, 0, null, smc, assert);
util.assertMapping(5, 2, '/wu/tang/gza.coffee', 4, 0, null, smc, assert);
+ util.assertMapping(6, 12, '/wu/tang/gza.coffee', 5, 10, null, smc, assert);
// Fuzzy
- // Original to generated
+ // Generated to original
util.assertMapping(2, 0, null, null, null, null, smc, assert, true);
util.assertMapping(2, 9, '/wu/tang/gza.coffee', 1, 0, null, smc, assert, true);
- util.assertMapping(3, 0, '/wu/tang/gza.coffee', 1, 0, null, smc, assert, true);
+ util.assertMapping(3, 0, null, null, null, null, smc, assert, true);
util.assertMapping(3, 9, '/wu/tang/gza.coffee', 2, 0, null, smc, assert, true);
- util.assertMapping(4, 0, '/wu/tang/gza.coffee', 2, 0, null, smc, assert, true);
+ util.assertMapping(4, 0, null, null, null, null, smc, assert, true);
util.assertMapping(4, 9, '/wu/tang/gza.coffee', 3, 0, null, smc, assert, true);
- util.assertMapping(5, 0, '/wu/tang/gza.coffee', 3, 0, null, smc, assert, true);
+ util.assertMapping(5, 0, null, null, null, null, smc, assert, true);
util.assertMapping(5, 9, '/wu/tang/gza.coffee', 4, 0, null, smc, assert, true);
+ util.assertMapping(6, 0, null, null, null, null, smc, assert, true);
+ util.assertMapping(6, 9, null, null, null, null, smc, assert, true);
+ util.assertMapping(6, 13, '/wu/tang/gza.coffee', 5, 10, null, smc, assert, true);
- // Generated to original
+ // Original to generated
util.assertMapping(2, 2, '/wu/tang/gza.coffee', 1, 1, null, smc, assert, null, true);
util.assertMapping(3, 2, '/wu/tang/gza.coffee', 2, 3, null, smc, assert, null, true);
util.assertMapping(4, 2, '/wu/tang/gza.coffee', 3, 6, null, smc, assert, null, true);
util.assertMapping(5, 2, '/wu/tang/gza.coffee', 4, 9, null, smc, assert, null, true);
+ util.assertMapping(5, 2, '/wu/tang/gza.coffee', 5, 9, null, smc, assert, null, true);
+ util.assertMapping(6, 12, '/wu/tang/gza.coffee', 6, 19, null, smc, assert, null, true);
};
});
diff --git a/toolkit/devtools/sourcemap/tests/unit/test_source_map_consumer.js b/toolkit/devtools/sourcemap/tests/unit/test_source_map_consumer.js
index d2533ea6663c..42fb2e102601 100644
--- a/toolkit/devtools/sourcemap/tests/unit/test_source_map_consumer.js
+++ b/toolkit/devtools/sourcemap/tests/unit/test_source_map_consumer.js
@@ -17,7 +17,7 @@ define("test/source-map/test-source-map-consumer", ["require", "exports", "modul
var SourceMapConsumer = require('source-map/source-map-consumer').SourceMapConsumer;
var SourceMapGenerator = require('source-map/source-map-generator').SourceMapGenerator;
- exports['test that we can instantiate with a string or an objects'] = function (assert, util) {
+ exports['test that we can instantiate with a string or an object'] = function (assert, util) {
assert.doesNotThrow(function () {
var map = new SourceMapConsumer(util.testMap);
});
@@ -27,18 +27,34 @@ define("test/source-map/test-source-map-consumer", ["require", "exports", "modul
};
exports['test that the `sources` field has the original sources'] = function (assert, util) {
- var map = new SourceMapConsumer(util.testMap);
- var sources = map.sources;
+ var map;
+ var sources;
+ map = new SourceMapConsumer(util.testMap);
+ sources = map.sources;
assert.equal(sources[0], '/the/root/one.js');
assert.equal(sources[1], '/the/root/two.js');
assert.equal(sources.length, 2);
+
+ map = new SourceMapConsumer(util.testMapNoSourceRoot);
+ sources = map.sources;
+ assert.equal(sources[0], 'one.js');
+ assert.equal(sources[1], 'two.js');
+ assert.equal(sources.length, 2);
+
+ map = new SourceMapConsumer(util.testMapEmptySourceRoot);
+ sources = map.sources;
+ assert.equal(sources[0], 'one.js');
+ assert.equal(sources[1], 'two.js');
+ assert.equal(sources.length, 2);
};
exports['test that the source root is reflected in a mapping\'s source field'] = function (assert, util) {
- var map = new SourceMapConsumer(util.testMap);
+ var map;
var mapping;
+ map = new SourceMapConsumer(util.testMap);
+
mapping = map.originalPositionFor({
line: 2,
column: 1
@@ -50,6 +66,36 @@ define("test/source-map/test-source-map-consumer", ["require", "exports", "modul
column: 1
});
assert.equal(mapping.source, '/the/root/one.js');
+
+
+ map = new SourceMapConsumer(util.testMapNoSourceRoot);
+
+ mapping = map.originalPositionFor({
+ line: 2,
+ column: 1
+ });
+ assert.equal(mapping.source, 'two.js');
+
+ mapping = map.originalPositionFor({
+ line: 1,
+ column: 1
+ });
+ assert.equal(mapping.source, 'one.js');
+
+
+ map = new SourceMapConsumer(util.testMapEmptySourceRoot);
+
+ mapping = map.originalPositionFor({
+ line: 2,
+ column: 1
+ });
+ assert.equal(mapping.source, 'two.js');
+
+ mapping = map.originalPositionFor({
+ line: 1,
+ column: 1
+ });
+ assert.equal(mapping.source, 'one.js');
};
exports['test mapping tokens back exactly'] = function (assert, util) {
@@ -85,6 +131,30 @@ define("test/source-map/test-source-map-consumer", ["require", "exports", "modul
util.assertMapping(2, 9, '/the/root/two.js', 1, 16, null, map, assert, null, true);
};
+ exports['test mappings and end of lines'] = function (assert, util) {
+ var smg = new SourceMapGenerator({
+ file: 'foo.js'
+ });
+ smg.addMapping({
+ original: { line: 1, column: 1 },
+ generated: { line: 1, column: 1 },
+ source: 'bar.js'
+ });
+ smg.addMapping({
+ original: { line: 2, column: 2 },
+ generated: { line: 2, column: 2 },
+ source: 'bar.js'
+ });
+
+ var map = SourceMapConsumer.fromSourceMap(smg);
+
+ // When finding original positions, mappings end at the end of the line.
+ util.assertMapping(2, 1, null, null, null, null, map, assert, true)
+
+ // When finding generated positions, mappings do not end at the end of the line.
+ util.assertMapping(1, 1, 'bar.js', 2, 1, null, map, assert, null, true);
+ };
+
exports['test creating source map consumers with )]}\' prefix'] = function (assert, util) {
assert.doesNotThrow(function () {
var map = new SourceMapConsumer(")]}'" + JSON.stringify(util.testMap));
@@ -92,15 +162,15 @@ define("test/source-map/test-source-map-consumer", ["require", "exports", "modul
};
exports['test eachMapping'] = function (assert, util) {
- var map = new SourceMapConsumer(util.testMap);
+ var map;
+
+ map = new SourceMapConsumer(util.testMap);
var previousLine = -Infinity;
var previousColumn = -Infinity;
map.eachMapping(function (mapping) {
assert.ok(mapping.generatedLine >= previousLine);
- if (mapping.source) {
- assert.equal(mapping.source.indexOf(util.testMap.sourceRoot), 0);
- }
+ assert.ok(mapping.source === '/the/root/one.js' || mapping.source === '/the/root/two.js');
if (mapping.generatedLine === previousLine) {
assert.ok(mapping.generatedColumn >= previousColumn);
@@ -111,6 +181,16 @@ define("test/source-map/test-source-map-consumer", ["require", "exports", "modul
previousColumn = -Infinity;
}
});
+
+ map = new SourceMapConsumer(util.testMapNoSourceRoot);
+ map.eachMapping(function (mapping) {
+ assert.ok(mapping.source === 'one.js' || mapping.source === 'two.js');
+ });
+
+ map = new SourceMapConsumer(util.testMapEmptySourceRoot);
+ map.eachMapping(function (mapping) {
+ assert.ok(mapping.source === 'one.js' || mapping.source === 'two.js');
+ });
};
exports['test iterating over mappings in a different order'] = function (assert, util) {
diff --git a/toolkit/devtools/sourcemap/tests/unit/test_source_map_generator.js b/toolkit/devtools/sourcemap/tests/unit/test_source_map_generator.js
index b49682b0d907..58582d9966e0 100644
--- a/toolkit/devtools/sourcemap/tests/unit/test_source_map_generator.js
+++ b/toolkit/devtools/sourcemap/tests/unit/test_source_map_generator.js
@@ -25,6 +25,10 @@ define("test/source-map/test-source-map-generator", ["require", "exports", "modu
sourceRoot: '.'
});
assert.ok(true);
+
+ var map = new SourceMapGenerator().toJSON();
+ assert.ok(!('file' in map));
+ assert.ok(!('sourceRoot' in map));
};
exports['test JSON serialization'] = function (assert, util) {
@@ -182,6 +186,24 @@ define("test/source-map/test-source-map-generator", ["require", "exports", "modu
util.assertEqualMaps(assert, map, util.testMap);
};
+ exports['test that adding a mapping with an empty string name does not break generation'] = function (assert, util) {
+ var map = new SourceMapGenerator({
+ file: 'generated-foo.js',
+ sourceRoot: '.'
+ });
+
+ map.addMapping({
+ generated: { line: 1, column: 1 },
+ source: 'bar.js',
+ original: { line: 1, column: 1 },
+ name: ''
+ });
+
+ assert.doesNotThrow(function () {
+ JSON.parse(map.toString());
+ });
+ };
+
exports['test that source content can be set'] = function (assert, util) {
var map = new SourceMapGenerator({
file: 'min.js',
@@ -274,6 +296,218 @@ define("test/source-map/test-source-map-generator", ["require", "exports", "modu
util.assertEqualMaps(assert, actualMap, expectedMap);
};
+ exports['test applySourceMap throws when file is missing'] = function (assert, util) {
+ var map = new SourceMapGenerator({
+ file: 'test.js'
+ });
+ var map2 = new SourceMapGenerator();
+ assert.throws(function() {
+ map.applySourceMap(new SourceMapConsumer(map2.toJSON()));
+ });
+ };
+
+ exports['test the two additional parameters of applySourceMap'] = function (assert, util) {
+ // Assume the following directory structure:
+ //
+ // http://foo.org/
+ // bar.coffee
+ // app/
+ // coffee/
+ // foo.coffee
+ // temp/
+ // bundle.js
+ // temp_maps/
+ // bundle.js.map
+ // public/
+ // bundle.min.js
+ // bundle.min.js.map
+ //
+ // http://www.example.com/
+ // baz.coffee
+
+ var bundleMap = new SourceMapGenerator({
+ file: 'bundle.js'
+ });
+ bundleMap.addMapping({
+ generated: { line: 3, column: 3 },
+ original: { line: 2, column: 2 },
+ source: '../../coffee/foo.coffee'
+ });
+ bundleMap.setSourceContent('../../coffee/foo.coffee', 'foo coffee');
+ bundleMap.addMapping({
+ generated: { line: 13, column: 13 },
+ original: { line: 12, column: 12 },
+ source: '/bar.coffee'
+ });
+ bundleMap.setSourceContent('/bar.coffee', 'bar coffee');
+ bundleMap.addMapping({
+ generated: { line: 23, column: 23 },
+ original: { line: 22, column: 22 },
+ source: 'http://www.example.com/baz.coffee'
+ });
+ bundleMap.setSourceContent(
+ 'http://www.example.com/baz.coffee',
+ 'baz coffee'
+ );
+ bundleMap = new SourceMapConsumer(bundleMap.toJSON());
+
+ var minifiedMap = new SourceMapGenerator({
+ file: 'bundle.min.js',
+ sourceRoot: '..'
+ });
+ minifiedMap.addMapping({
+ generated: { line: 1, column: 1 },
+ original: { line: 3, column: 3 },
+ source: 'temp/bundle.js'
+ });
+ minifiedMap.addMapping({
+ generated: { line: 11, column: 11 },
+ original: { line: 13, column: 13 },
+ source: 'temp/bundle.js'
+ });
+ minifiedMap.addMapping({
+ generated: { line: 21, column: 21 },
+ original: { line: 23, column: 23 },
+ source: 'temp/bundle.js'
+ });
+ minifiedMap = new SourceMapConsumer(minifiedMap.toJSON());
+
+ var expectedMap = function (sources) {
+ var map = new SourceMapGenerator({
+ file: 'bundle.min.js',
+ sourceRoot: '..'
+ });
+ map.addMapping({
+ generated: { line: 1, column: 1 },
+ original: { line: 2, column: 2 },
+ source: sources[0]
+ });
+ map.setSourceContent(sources[0], 'foo coffee');
+ map.addMapping({
+ generated: { line: 11, column: 11 },
+ original: { line: 12, column: 12 },
+ source: sources[1]
+ });
+ map.setSourceContent(sources[1], 'bar coffee');
+ map.addMapping({
+ generated: { line: 21, column: 21 },
+ original: { line: 22, column: 22 },
+ source: sources[2]
+ });
+ map.setSourceContent(sources[2], 'baz coffee');
+ return map.toJSON();
+ }
+
+ var actualMap = function (aSourceMapPath) {
+ var map = SourceMapGenerator.fromSourceMap(minifiedMap);
+ // Note that relying on `bundleMap.file` (which is simply 'bundle.js')
+ // instead of supplying the second parameter wouldn't work here.
+ map.applySourceMap(bundleMap, '../temp/bundle.js', aSourceMapPath);
+ return map.toJSON();
+ }
+
+ util.assertEqualMaps(assert, actualMap('../temp/temp_maps'), expectedMap([
+ 'coffee/foo.coffee',
+ '/bar.coffee',
+ 'http://www.example.com/baz.coffee'
+ ]));
+
+ util.assertEqualMaps(assert, actualMap('/app/temp/temp_maps'), expectedMap([
+ '/app/coffee/foo.coffee',
+ '/bar.coffee',
+ 'http://www.example.com/baz.coffee'
+ ]));
+
+ util.assertEqualMaps(assert, actualMap('http://foo.org/app/temp/temp_maps'), expectedMap([
+ 'http://foo.org/app/coffee/foo.coffee',
+ 'http://foo.org/bar.coffee',
+ 'http://www.example.com/baz.coffee'
+ ]));
+
+ // If the third parameter is omitted or set to the current working
+ // directory we get incorrect source paths:
+
+ util.assertEqualMaps(assert, actualMap(), expectedMap([
+ '../coffee/foo.coffee',
+ '/bar.coffee',
+ 'http://www.example.com/baz.coffee'
+ ]));
+
+ util.assertEqualMaps(assert, actualMap(''), expectedMap([
+ '../coffee/foo.coffee',
+ '/bar.coffee',
+ 'http://www.example.com/baz.coffee'
+ ]));
+
+ util.assertEqualMaps(assert, actualMap('.'), expectedMap([
+ '../coffee/foo.coffee',
+ '/bar.coffee',
+ 'http://www.example.com/baz.coffee'
+ ]));
+
+ util.assertEqualMaps(assert, actualMap('./'), expectedMap([
+ '../coffee/foo.coffee',
+ '/bar.coffee',
+ 'http://www.example.com/baz.coffee'
+ ]));
+ };
+
+ exports['test applySourceMap name handling'] = function (assert, util) {
+ // Imagine some CoffeeScript code being compiled into JavaScript and then
+ // minified.
+
+ var assertName = function(coffeeName, jsName, expectedName) {
+ var minifiedMap = new SourceMapGenerator({
+ file: 'test.js.min'
+ });
+ minifiedMap.addMapping({
+ generated: { line: 1, column: 4 },
+ original: { line: 1, column: 4 },
+ source: 'test.js',
+ name: jsName
+ });
+
+ var coffeeMap = new SourceMapGenerator({
+ file: 'test.js'
+ });
+ coffeeMap.addMapping({
+ generated: { line: 1, column: 4 },
+ original: { line: 1, column: 0 },
+ source: 'test.coffee',
+ name: coffeeName
+ });
+
+ minifiedMap.applySourceMap(new SourceMapConsumer(coffeeMap.toJSON()));
+
+ new SourceMapConsumer(minifiedMap.toJSON()).eachMapping(function(mapping) {
+ assert.equal(mapping.name, expectedName);
+ });
+ };
+
+ // `foo = 1` -> `var foo = 1;` -> `var a=1`
+ // CoffeeScript doesn’t rename variables, so there’s no need for it to
+ // provide names in its source maps. Minifiers do rename variables and
+ // therefore do provide names in their source maps. So that name should be
+ // retained if the original map lacks names.
+ assertName(null, 'foo', 'foo');
+
+ // `foo = 1` -> `var coffee$foo = 1;` -> `var a=1`
+ // Imagine that CoffeeScript prefixed all variables with `coffee$`. Even
+ // though the minifier then also provides a name, the original name is
+ // what corresponds to the source.
+ assertName('foo', 'coffee$foo', 'foo');
+
+ // `foo = 1` -> `var coffee$foo = 1;` -> `var coffee$foo=1`
+ // Minifiers can turn off variable mangling. Then there’s no need to
+ // provide names in the source map, but the names from the original map are
+ // still needed.
+ assertName('foo', null, 'foo');
+
+ // `foo = 1` -> `var foo = 1;` -> `var foo=1`
+ // No renaming at all.
+ assertName(null, null, null);
+ };
+
exports['test sorting with duplicate generated mappings'] = function (assert, util) {
var map = new SourceMapGenerator({
file: 'test.js'
@@ -419,6 +653,13 @@ define("test/source-map/test-source-map-generator", ["require", "exports", "modu
});
};
+ exports['test setting sourcesContent to null when already null'] = function (assert, util) {
+ var smg = new SourceMapGenerator({ file: "foo.js" });
+ assert.doesNotThrow(function() {
+ smg.setSourceContent("bar.js", null);
+ });
+ };
+
});
function run_test() {
runSourceMapTests('test/source-map/test-source-map-generator', do_throw);
diff --git a/toolkit/devtools/sourcemap/tests/unit/test_source_node.js b/toolkit/devtools/sourcemap/tests/unit/test_source_node.js
index fa1639562027..d3037010e8e7 100644
--- a/toolkit/devtools/sourcemap/tests/unit/test_source_node.js
+++ b/toolkit/devtools/sourcemap/tests/unit/test_source_node.js
@@ -18,6 +18,12 @@ define("test/source-map/test-source-node", ["require", "exports", "module"], fun
var SourceMapConsumer = require('source-map/source-map-consumer').SourceMapConsumer;
var SourceNode = require('source-map/source-node').SourceNode;
+ function forEachNewline(fn) {
+ return function (assert, util) {
+ ['\n', '\r\n'].forEach(fn.bind(null, assert, util));
+ }
+ }
+
exports['test .add()'] = function (assert, util) {
var node = new SourceNode(null, null, null);
@@ -133,20 +139,35 @@ define("test/source-map/test-source-node", ["require", "exports", "module"], fun
assert.equal(node.toString(), 'hey sexy mama, want to watch Futurama?');
};
- exports['test .toStringWithSourceMap()'] = function (assert, util) {
+ exports['test .toStringWithSourceMap()'] = forEachNewline(function (assert, util, nl) {
var node = new SourceNode(null, null, null,
- ['(function () {\n',
+ ['(function () {' + nl,
' ',
new SourceNode(1, 0, 'a.js', 'someCall', 'originalCall'),
new SourceNode(1, 8, 'a.js', '()'),
- ';\n',
- ' ', new SourceNode(2, 0, 'b.js', ['if (foo) bar()']), ';\n',
+ ';' + nl,
+ ' ', new SourceNode(2, 0, 'b.js', ['if (foo) bar()']), ';' + nl,
'}());']);
- var map = node.toStringWithSourceMap({
+ var result = node.toStringWithSourceMap({
file: 'foo.js'
- }).map;
+ });
+
+ assert.equal(result.code, [
+ '(function () {',
+ ' someCall();',
+ ' if (foo) bar();',
+ '}());'
+ ].join(nl));
+
+ var map = result.map;
+ var mapWithoutOptions = node.toStringWithSourceMap().map;
assert.ok(map instanceof SourceMapGenerator, 'map instanceof SourceMapGenerator');
+ assert.ok(mapWithoutOptions instanceof SourceMapGenerator, 'mapWithoutOptions instanceof SourceMapGenerator');
+ assert.ok(!('file' in mapWithoutOptions));
+ mapWithoutOptions._file = 'foo.js';
+ util.assertEqualMaps(assert, map.toJSON(), mapWithoutOptions.toJSON());
+
map = new SourceMapConsumer(map.toString());
var actual;
@@ -191,11 +212,12 @@ define("test/source-map/test-source-node", ["require", "exports", "module"], fun
assert.equal(actual.source, null);
assert.equal(actual.line, null);
assert.equal(actual.column, null);
- };
+ });
- exports['test .fromStringWithSourceMap()'] = function (assert, util) {
+ exports['test .fromStringWithSourceMap()'] = forEachNewline(function (assert, util, nl) {
+ var testCode = util.testGeneratedCode.replace(/\n/g, nl);
var node = SourceNode.fromStringWithSourceMap(
- util.testGeneratedCode,
+ testCode,
new SourceMapConsumer(util.testMap));
var result = node.toStringWithSourceMap({
@@ -204,17 +226,17 @@ define("test/source-map/test-source-node", ["require", "exports", "module"], fun
var map = result.map;
var code = result.code;
- assert.equal(code, util.testGeneratedCode);
+ assert.equal(code, testCode);
assert.ok(map instanceof SourceMapGenerator, 'map instanceof SourceMapGenerator');
map = map.toJSON();
assert.equal(map.version, util.testMap.version);
assert.equal(map.file, util.testMap.file);
assert.equal(map.mappings, util.testMap.mappings);
- };
+ });
- exports['test .fromStringWithSourceMap() empty map'] = function (assert, util) {
+ exports['test .fromStringWithSourceMap() empty map'] = forEachNewline(function (assert, util, nl) {
var node = SourceNode.fromStringWithSourceMap(
- util.testGeneratedCode,
+ util.testGeneratedCode.replace(/\n/g, nl),
new SourceMapConsumer(util.emptyMap));
var result = node.toStringWithSourceMap({
file: 'min.js'
@@ -222,22 +244,22 @@ define("test/source-map/test-source-node", ["require", "exports", "module"], fun
var map = result.map;
var code = result.code;
- assert.equal(code, util.testGeneratedCode);
+ assert.equal(code, util.testGeneratedCode.replace(/\n/g, nl));
assert.ok(map instanceof SourceMapGenerator, 'map instanceof SourceMapGenerator');
map = map.toJSON();
assert.equal(map.version, util.emptyMap.version);
assert.equal(map.file, util.emptyMap.file);
assert.equal(map.mappings.length, util.emptyMap.mappings.length);
assert.equal(map.mappings, util.emptyMap.mappings);
- };
+ });
- exports['test .fromStringWithSourceMap() complex version'] = function (assert, util) {
+ exports['test .fromStringWithSourceMap() complex version'] = forEachNewline(function (assert, util, nl) {
var input = new SourceNode(null, null, null, [
- "(function() {\n",
- " var Test = {};\n",
- " ", new SourceNode(1, 0, "a.js", "Test.A = { value: 1234 };\n"),
- " ", new SourceNode(2, 0, "a.js", "Test.A.x = 'xyz';"), "\n",
- "}());\n",
+ "(function() {" + nl,
+ " var Test = {};" + nl,
+ " ", new SourceNode(1, 0, "a.js", "Test.A = { value: 1234 };" + nl),
+ " ", new SourceNode(2, 0, "a.js", "Test.A.x = 'xyz';"), nl,
+ "}());" + nl,
"/* Generated Source */"]);
input = input.toStringWithSourceMap({
file: 'foo.js'
@@ -258,25 +280,123 @@ define("test/source-map/test-source-node", ["require", "exports", "module"], fun
map = map.toJSON();
var inputMap = input.map.toJSON();
util.assertEqualMaps(assert, map, inputMap);
+ });
+
+ exports['test .fromStringWithSourceMap() third argument'] = function (assert, util) {
+ // Assume the following directory structure:
+ //
+ // http://foo.org/
+ // bar.coffee
+ // app/
+ // coffee/
+ // foo.coffee
+ // coffeeBundle.js # Made from {foo,bar,baz}.coffee
+ // maps/
+ // coffeeBundle.js.map
+ // js/
+ // foo.js
+ // public/
+ // app.js # Made from {foo,coffeeBundle}.js
+ // app.js.map
+ //
+ // http://www.example.com/
+ // baz.coffee
+
+ var coffeeBundle = new SourceNode(1, 0, 'foo.coffee', 'foo(coffee);\n');
+ coffeeBundle.setSourceContent('foo.coffee', 'foo coffee');
+ coffeeBundle.add(new SourceNode(2, 0, '/bar.coffee', 'bar(coffee);\n'));
+ coffeeBundle.add(new SourceNode(3, 0, 'http://www.example.com/baz.coffee', 'baz(coffee);'));
+ coffeeBundle = coffeeBundle.toStringWithSourceMap({
+ file: 'foo.js',
+ sourceRoot: '..'
+ });
+
+ var foo = new SourceNode(1, 0, 'foo.js', 'foo(js);');
+
+ var test = function(relativePath, expectedSources) {
+ var app = new SourceNode();
+ app.add(SourceNode.fromStringWithSourceMap(
+ coffeeBundle.code,
+ new SourceMapConsumer(coffeeBundle.map.toString()),
+ relativePath));
+ app.add(foo);
+ var i = 0;
+ app.walk(function (chunk, loc) {
+ assert.equal(loc.source, expectedSources[i]);
+ i++;
+ });
+ app.walkSourceContents(function (sourceFile, sourceContent) {
+ assert.equal(sourceFile, expectedSources[0]);
+ assert.equal(sourceContent, 'foo coffee');
+ })
+ };
+
+ test('../coffee/maps', [
+ '../coffee/foo.coffee',
+ '/bar.coffee',
+ 'http://www.example.com/baz.coffee',
+ 'foo.js'
+ ]);
+
+ // If the third parameter is omitted or set to the current working
+ // directory we get incorrect source paths:
+
+ test(undefined, [
+ '../foo.coffee',
+ '/bar.coffee',
+ 'http://www.example.com/baz.coffee',
+ 'foo.js'
+ ]);
+
+ test('', [
+ '../foo.coffee',
+ '/bar.coffee',
+ 'http://www.example.com/baz.coffee',
+ 'foo.js'
+ ]);
+
+ test('.', [
+ '../foo.coffee',
+ '/bar.coffee',
+ 'http://www.example.com/baz.coffee',
+ 'foo.js'
+ ]);
+
+ test('./', [
+ '../foo.coffee',
+ '/bar.coffee',
+ 'http://www.example.com/baz.coffee',
+ 'foo.js'
+ ]);
};
- exports['test .fromStringWithSourceMap() merging duplicate mappings'] = function (assert, util) {
+ exports['test .toStringWithSourceMap() merging duplicate mappings'] = forEachNewline(function (assert, util, nl) {
var input = new SourceNode(null, null, null, [
new SourceNode(1, 0, "a.js", "(function"),
- new SourceNode(1, 0, "a.js", "() {\n"),
+ new SourceNode(1, 0, "a.js", "() {" + nl),
" ",
new SourceNode(1, 0, "a.js", "var Test = "),
- new SourceNode(1, 0, "b.js", "{};\n"),
+ new SourceNode(1, 0, "b.js", "{};" + nl),
new SourceNode(2, 0, "b.js", "Test"),
new SourceNode(2, 0, "b.js", ".A", "A"),
- new SourceNode(2, 20, "b.js", " = { value: 1234 };\n", "A"),
- "}());\n",
+ new SourceNode(2, 20, "b.js", " = { value: ", "A"),
+ "1234",
+ new SourceNode(2, 40, "b.js", " };" + nl, "A"),
+ "}());" + nl,
"/* Generated Source */"
]);
input = input.toStringWithSourceMap({
file: 'foo.js'
});
+ assert.equal(input.code, [
+ "(function() {",
+ " var Test = {};",
+ "Test.A = { value: 1234 };",
+ "}());",
+ "/* Generated Source */"
+ ].join(nl))
+
var correctMap = new SourceMapGenerator({
file: 'foo.js'
});
@@ -285,9 +405,8 @@ define("test/source-map/test-source-node", ["require", "exports", "module"], fun
source: 'a.js',
original: { line: 1, column: 0 }
});
- correctMap.addMapping({
- generated: { line: 2, column: 0 }
- });
+ // Here is no need for a empty mapping,
+ // because mappings ends at eol
correctMap.addMapping({
generated: { line: 2, column: 2 },
source: 'a.js',
@@ -315,15 +434,143 @@ define("test/source-map/test-source-node", ["require", "exports", "module"], fun
name: 'A',
original: { line: 2, column: 20 }
});
+ // This empty mapping is required,
+ // because there is a hole in the middle of the line
correctMap.addMapping({
- generated: { line: 4, column: 0 }
+ generated: { line: 3, column: 18 }
+ });
+ correctMap.addMapping({
+ generated: { line: 3, column: 22 },
+ source: 'b.js',
+ name: 'A',
+ original: { line: 2, column: 40 }
+ });
+ // Here is no need for a empty mapping,
+ // because mappings ends at eol
+
+ var inputMap = input.map.toJSON();
+ correctMap = correctMap.toJSON();
+ util.assertEqualMaps(assert, inputMap, correctMap);
+ });
+
+ exports['test .toStringWithSourceMap() multi-line SourceNodes'] = forEachNewline(function (assert, util, nl) {
+ var input = new SourceNode(null, null, null, [
+ new SourceNode(1, 0, "a.js", "(function() {" + nl + "var nextLine = 1;" + nl + "anotherLine();" + nl),
+ new SourceNode(2, 2, "b.js", "Test.call(this, 123);" + nl),
+ new SourceNode(2, 2, "b.js", "this['stuff'] = 'v';" + nl),
+ new SourceNode(2, 2, "b.js", "anotherLine();" + nl),
+ "/*" + nl + "Generated" + nl + "Source" + nl + "*/" + nl,
+ new SourceNode(3, 4, "c.js", "anotherLine();" + nl),
+ "/*" + nl + "Generated" + nl + "Source" + nl + "*/"
+ ]);
+ input = input.toStringWithSourceMap({
+ file: 'foo.js'
+ });
+
+ assert.equal(input.code, [
+ "(function() {",
+ "var nextLine = 1;",
+ "anotherLine();",
+ "Test.call(this, 123);",
+ "this['stuff'] = 'v';",
+ "anotherLine();",
+ "/*",
+ "Generated",
+ "Source",
+ "*/",
+ "anotherLine();",
+ "/*",
+ "Generated",
+ "Source",
+ "*/"
+ ].join(nl));
+
+ var correctMap = new SourceMapGenerator({
+ file: 'foo.js'
+ });
+ correctMap.addMapping({
+ generated: { line: 1, column: 0 },
+ source: 'a.js',
+ original: { line: 1, column: 0 }
+ });
+ correctMap.addMapping({
+ generated: { line: 2, column: 0 },
+ source: 'a.js',
+ original: { line: 1, column: 0 }
+ });
+ correctMap.addMapping({
+ generated: { line: 3, column: 0 },
+ source: 'a.js',
+ original: { line: 1, column: 0 }
+ });
+ correctMap.addMapping({
+ generated: { line: 4, column: 0 },
+ source: 'b.js',
+ original: { line: 2, column: 2 }
+ });
+ correctMap.addMapping({
+ generated: { line: 5, column: 0 },
+ source: 'b.js',
+ original: { line: 2, column: 2 }
+ });
+ correctMap.addMapping({
+ generated: { line: 6, column: 0 },
+ source: 'b.js',
+ original: { line: 2, column: 2 }
+ });
+ correctMap.addMapping({
+ generated: { line: 11, column: 0 },
+ source: 'c.js',
+ original: { line: 3, column: 4 }
});
var inputMap = input.map.toJSON();
correctMap = correctMap.toJSON();
- util.assertEqualMaps(assert, correctMap, inputMap);
+ util.assertEqualMaps(assert, inputMap, correctMap);
+ });
+
+ exports['test .toStringWithSourceMap() with empty string'] = function (assert, util) {
+ var node = new SourceNode(1, 0, 'empty.js', '');
+ var result = node.toStringWithSourceMap();
+ assert.equal(result.code, '');
};
+ exports['test .toStringWithSourceMap() with consecutive newlines'] = forEachNewline(function (assert, util, nl) {
+ var input = new SourceNode(null, null, null, [
+ "/***/" + nl + nl,
+ new SourceNode(1, 0, "a.js", "'use strict';" + nl),
+ new SourceNode(2, 0, "a.js", "a();"),
+ ]);
+ input = input.toStringWithSourceMap({
+ file: 'foo.js'
+ });
+
+ assert.equal(input.code, [
+ "/***/",
+ "",
+ "'use strict';",
+ "a();",
+ ].join(nl));
+
+ var correctMap = new SourceMapGenerator({
+ file: 'foo.js'
+ });
+ correctMap.addMapping({
+ generated: { line: 3, column: 0 },
+ source: 'a.js',
+ original: { line: 1, column: 0 }
+ });
+ correctMap.addMapping({
+ generated: { line: 4, column: 0 },
+ source: 'a.js',
+ original: { line: 2, column: 0 }
+ });
+
+ var inputMap = input.map.toJSON();
+ correctMap = correctMap.toJSON();
+ util.assertEqualMaps(assert, inputMap, correctMap);
+ });
+
exports['test setSourceContent with toStringWithSourceMap'] = function (assert, util) {
var aNode = new SourceNode(1, 1, 'a.js', 'a');
aNode.setSourceContent('a.js', 'someContent');
diff --git a/toolkit/devtools/sourcemap/tests/unit/test_util.js b/toolkit/devtools/sourcemap/tests/unit/test_util.js
new file mode 100644
index 000000000000..6419a21e0ef2
--- /dev/null
+++ b/toolkit/devtools/sourcemap/tests/unit/test_util.js
@@ -0,0 +1,224 @@
+/*
+ * WARNING!
+ *
+ * Do not edit this file directly, it is built from the sources at
+ * https://github.com/mozilla/source-map/
+ */
+
+Components.utils.import('resource://test/Utils.jsm');
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2014 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+define("test/source-map/test-util", ["require", "exports", "module"], function (require, exports, module) {
+
+ var libUtil = require('source-map/util');
+
+ exports['test urls'] = function (assert, util) {
+ var assertUrl = function (url) {
+ assert.equal(url, libUtil.urlGenerate(libUtil.urlParse(url)));
+ };
+ assertUrl('http://');
+ assertUrl('http://www.example.com');
+ assertUrl('http://user:pass@www.example.com');
+ assertUrl('http://www.example.com:80');
+ assertUrl('http://www.example.com/');
+ assertUrl('http://www.example.com/foo/bar');
+ assertUrl('http://www.example.com/foo/bar/');
+ assertUrl('http://user:pass@www.example.com:80/foo/bar/');
+
+ assertUrl('//');
+ assertUrl('//www.example.com');
+ assertUrl('file:///www.example.com');
+
+ assert.equal(libUtil.urlParse(''), null);
+ assert.equal(libUtil.urlParse('.'), null);
+ assert.equal(libUtil.urlParse('..'), null);
+ assert.equal(libUtil.urlParse('a'), null);
+ assert.equal(libUtil.urlParse('a/b'), null);
+ assert.equal(libUtil.urlParse('a//b'), null);
+ assert.equal(libUtil.urlParse('/a'), null);
+ assert.equal(libUtil.urlParse('data:foo,bar'), null);
+ };
+
+ exports['test normalize()'] = function (assert, util) {
+ assert.equal(libUtil.normalize('/..'), '/');
+ assert.equal(libUtil.normalize('/../'), '/');
+ assert.equal(libUtil.normalize('/../../../..'), '/');
+ assert.equal(libUtil.normalize('/../../../../a/b/c'), '/a/b/c');
+ assert.equal(libUtil.normalize('/a/b/c/../../../d/../../e'), '/e');
+
+ assert.equal(libUtil.normalize('..'), '..');
+ assert.equal(libUtil.normalize('../'), '../');
+ assert.equal(libUtil.normalize('../../a/'), '../../a/');
+ assert.equal(libUtil.normalize('a/..'), '.');
+ assert.equal(libUtil.normalize('a/../../..'), '../..');
+
+ assert.equal(libUtil.normalize('/.'), '/');
+ assert.equal(libUtil.normalize('/./'), '/');
+ assert.equal(libUtil.normalize('/./././.'), '/');
+ assert.equal(libUtil.normalize('/././././a/b/c'), '/a/b/c');
+ assert.equal(libUtil.normalize('/a/b/c/./././d/././e'), '/a/b/c/d/e');
+
+ assert.equal(libUtil.normalize(''), '.');
+ assert.equal(libUtil.normalize('.'), '.');
+ assert.equal(libUtil.normalize('./'), '.');
+ assert.equal(libUtil.normalize('././a'), 'a');
+ assert.equal(libUtil.normalize('a/./'), 'a/');
+ assert.equal(libUtil.normalize('a/././.'), 'a');
+
+ assert.equal(libUtil.normalize('/a/b//c////d/////'), '/a/b/c/d/');
+ assert.equal(libUtil.normalize('///a/b//c////d/////'), '///a/b/c/d/');
+ assert.equal(libUtil.normalize('a/b//c////d'), 'a/b/c/d');
+
+ assert.equal(libUtil.normalize('.///.././../a/b//./..'), '../../a')
+
+ assert.equal(libUtil.normalize('http://www.example.com'), 'http://www.example.com');
+ assert.equal(libUtil.normalize('http://www.example.com/'), 'http://www.example.com/');
+ assert.equal(libUtil.normalize('http://www.example.com/./..//a/b/c/.././d//'), 'http://www.example.com/a/b/d/');
+ };
+
+ exports['test join()'] = function (assert, util) {
+ assert.equal(libUtil.join('a', 'b'), 'a/b');
+ assert.equal(libUtil.join('a/', 'b'), 'a/b');
+ assert.equal(libUtil.join('a//', 'b'), 'a/b');
+ assert.equal(libUtil.join('a', 'b/'), 'a/b/');
+ assert.equal(libUtil.join('a', 'b//'), 'a/b/');
+ assert.equal(libUtil.join('a/', '/b'), '/b');
+ assert.equal(libUtil.join('a//', '//b'), '//b');
+
+ assert.equal(libUtil.join('a', '..'), '.');
+ assert.equal(libUtil.join('a', '../b'), 'b');
+ assert.equal(libUtil.join('a/b', '../c'), 'a/c');
+
+ assert.equal(libUtil.join('a', '.'), 'a');
+ assert.equal(libUtil.join('a', './b'), 'a/b');
+ assert.equal(libUtil.join('a/b', './c'), 'a/b/c');
+
+ assert.equal(libUtil.join('a', 'http://www.example.com'), 'http://www.example.com');
+ assert.equal(libUtil.join('a', 'data:foo,bar'), 'data:foo,bar');
+
+
+ assert.equal(libUtil.join('', 'b'), 'b');
+ assert.equal(libUtil.join('.', 'b'), 'b');
+ assert.equal(libUtil.join('', 'b/'), 'b/');
+ assert.equal(libUtil.join('.', 'b/'), 'b/');
+ assert.equal(libUtil.join('', 'b//'), 'b/');
+ assert.equal(libUtil.join('.', 'b//'), 'b/');
+
+ assert.equal(libUtil.join('', '..'), '..');
+ assert.equal(libUtil.join('.', '..'), '..');
+ assert.equal(libUtil.join('', '../b'), '../b');
+ assert.equal(libUtil.join('.', '../b'), '../b');
+
+ assert.equal(libUtil.join('', '.'), '.');
+ assert.equal(libUtil.join('.', '.'), '.');
+ assert.equal(libUtil.join('', './b'), 'b');
+ assert.equal(libUtil.join('.', './b'), 'b');
+
+ assert.equal(libUtil.join('', 'http://www.example.com'), 'http://www.example.com');
+ assert.equal(libUtil.join('.', 'http://www.example.com'), 'http://www.example.com');
+ assert.equal(libUtil.join('', 'data:foo,bar'), 'data:foo,bar');
+ assert.equal(libUtil.join('.', 'data:foo,bar'), 'data:foo,bar');
+
+
+ assert.equal(libUtil.join('..', 'b'), '../b');
+ assert.equal(libUtil.join('..', 'b/'), '../b/');
+ assert.equal(libUtil.join('..', 'b//'), '../b/');
+
+ assert.equal(libUtil.join('..', '..'), '../..');
+ assert.equal(libUtil.join('..', '../b'), '../../b');
+
+ assert.equal(libUtil.join('..', '.'), '..');
+ assert.equal(libUtil.join('..', './b'), '../b');
+
+ assert.equal(libUtil.join('..', 'http://www.example.com'), 'http://www.example.com');
+ assert.equal(libUtil.join('..', 'data:foo,bar'), 'data:foo,bar');
+
+
+ assert.equal(libUtil.join('a', ''), 'a');
+ assert.equal(libUtil.join('a', '.'), 'a');
+ assert.equal(libUtil.join('a/', ''), 'a');
+ assert.equal(libUtil.join('a/', '.'), 'a');
+ assert.equal(libUtil.join('a//', ''), 'a');
+ assert.equal(libUtil.join('a//', '.'), 'a');
+ assert.equal(libUtil.join('/a', ''), '/a');
+ assert.equal(libUtil.join('/a', '.'), '/a');
+ assert.equal(libUtil.join('', ''), '.');
+ assert.equal(libUtil.join('.', ''), '.');
+ assert.equal(libUtil.join('.', ''), '.');
+ assert.equal(libUtil.join('.', '.'), '.');
+ assert.equal(libUtil.join('..', ''), '..');
+ assert.equal(libUtil.join('..', '.'), '..');
+ assert.equal(libUtil.join('http://foo.org/a', ''), 'http://foo.org/a');
+ assert.equal(libUtil.join('http://foo.org/a', '.'), 'http://foo.org/a');
+ assert.equal(libUtil.join('http://foo.org/a/', ''), 'http://foo.org/a');
+ assert.equal(libUtil.join('http://foo.org/a/', '.'), 'http://foo.org/a');
+ assert.equal(libUtil.join('http://foo.org/a//', ''), 'http://foo.org/a');
+ assert.equal(libUtil.join('http://foo.org/a//', '.'), 'http://foo.org/a');
+ assert.equal(libUtil.join('http://foo.org', ''), 'http://foo.org/');
+ assert.equal(libUtil.join('http://foo.org', '.'), 'http://foo.org/');
+ assert.equal(libUtil.join('http://foo.org/', ''), 'http://foo.org/');
+ assert.equal(libUtil.join('http://foo.org/', '.'), 'http://foo.org/');
+ assert.equal(libUtil.join('http://foo.org//', ''), 'http://foo.org/');
+ assert.equal(libUtil.join('http://foo.org//', '.'), 'http://foo.org/');
+ assert.equal(libUtil.join('//www.example.com', ''), '//www.example.com/');
+ assert.equal(libUtil.join('//www.example.com', '.'), '//www.example.com/');
+
+
+ assert.equal(libUtil.join('http://foo.org/a', 'b'), 'http://foo.org/a/b');
+ assert.equal(libUtil.join('http://foo.org/a/', 'b'), 'http://foo.org/a/b');
+ assert.equal(libUtil.join('http://foo.org/a//', 'b'), 'http://foo.org/a/b');
+ assert.equal(libUtil.join('http://foo.org/a', 'b/'), 'http://foo.org/a/b/');
+ assert.equal(libUtil.join('http://foo.org/a', 'b//'), 'http://foo.org/a/b/');
+ assert.equal(libUtil.join('http://foo.org/a/', '/b'), 'http://foo.org/b');
+ assert.equal(libUtil.join('http://foo.org/a//', '//b'), 'http://b');
+
+ assert.equal(libUtil.join('http://foo.org/a', '..'), 'http://foo.org/');
+ assert.equal(libUtil.join('http://foo.org/a', '../b'), 'http://foo.org/b');
+ assert.equal(libUtil.join('http://foo.org/a/b', '../c'), 'http://foo.org/a/c');
+
+ assert.equal(libUtil.join('http://foo.org/a', '.'), 'http://foo.org/a');
+ assert.equal(libUtil.join('http://foo.org/a', './b'), 'http://foo.org/a/b');
+ assert.equal(libUtil.join('http://foo.org/a/b', './c'), 'http://foo.org/a/b/c');
+
+ assert.equal(libUtil.join('http://foo.org/a', 'http://www.example.com'), 'http://www.example.com');
+ assert.equal(libUtil.join('http://foo.org/a', 'data:foo,bar'), 'data:foo,bar');
+
+
+ assert.equal(libUtil.join('http://foo.org', 'a'), 'http://foo.org/a');
+ assert.equal(libUtil.join('http://foo.org/', 'a'), 'http://foo.org/a');
+ assert.equal(libUtil.join('http://foo.org//', 'a'), 'http://foo.org/a');
+ assert.equal(libUtil.join('http://foo.org', '/a'), 'http://foo.org/a');
+ assert.equal(libUtil.join('http://foo.org/', '/a'), 'http://foo.org/a');
+ assert.equal(libUtil.join('http://foo.org//', '/a'), 'http://foo.org/a');
+
+
+ assert.equal(libUtil.join('http://', 'www.example.com'), 'http://www.example.com');
+ assert.equal(libUtil.join('file:///', 'www.example.com'), 'file:///www.example.com');
+ assert.equal(libUtil.join('http://', 'ftp://example.com'), 'ftp://example.com');
+
+ assert.equal(libUtil.join('http://www.example.com', '//foo.org/bar'), 'http://foo.org/bar');
+ assert.equal(libUtil.join('//www.example.com', '//foo.org/bar'), '//foo.org/bar');
+ };
+
+ // TODO Issue #128: Define and test this function properly.
+ exports['test relative()'] = function (assert, util) {
+ assert.equal(libUtil.relative('/the/root', '/the/root/one.js'), 'one.js');
+ assert.equal(libUtil.relative('/the/root', '/the/rootone.js'), '/the/rootone.js');
+
+ assert.equal(libUtil.relative('', '/the/root/one.js'), '/the/root/one.js');
+ assert.equal(libUtil.relative('.', '/the/root/one.js'), '/the/root/one.js');
+ assert.equal(libUtil.relative('', 'the/root/one.js'), 'the/root/one.js');
+ assert.equal(libUtil.relative('.', 'the/root/one.js'), 'the/root/one.js');
+
+ assert.equal(libUtil.relative('/', '/the/root/one.js'), 'the/root/one.js');
+ assert.equal(libUtil.relative('/', 'the/root/one.js'), 'the/root/one.js');
+ };
+
+});
+function run_test() {
+ runSourceMapTests('test/source-map/test-util', do_throw);
+}