Bug 882986 - A source map's sourcesContent doesn't work when the original file path is absolute; r=dcamp

This commit is contained in:
Nick Fitzgerald 2013-06-13 18:21:06 -07:00
parent d35f461d62
commit f9b181c2d5
6 changed files with 273 additions and 13 deletions

View File

@ -0,0 +1,52 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Regression test for bug 882986 regarding sourcesContent and absolute source
* URLs.
*/
var gDebuggee;
var gClient;
var gThreadClient;
Components.utils.import("resource:///modules/devtools/SourceMap.jsm");
function run_test()
{
initTestDebuggerServer();
gDebuggee = addTestGlobal("test-source-map");
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect(function() {
attachTestTabAndResume(gClient, "test-source-map", function(aResponse, aTabClient, aThreadClient) {
gThreadClient = aThreadClient;
test_source_maps();
});
});
do_test_pending();
}
function test_source_maps()
{
gClient.addOneTimeListener("newSource", function (aEvent, aPacket) {
let sourceClient = gThreadClient.source(aPacket.source);
sourceClient.source(function ({error, source}) {
do_check_true(!error, "should be able to grab the source");
do_check_eq(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"]
});
Components.utils.evalInSandbox(code, gDebuggee, "1.8",
"http://example.com/foo.js", 1);
}

View File

@ -96,6 +96,7 @@ reason = bug 820380
[test_sourcemaps-07.js]
skip-if = toolkit == "gonk"
reason = bug 820380
[test_sourcemaps-08.js]
[test_objectgrips-01.js]
[test_objectgrips-02.js]
[test_objectgrips-03.js]

View File

@ -326,17 +326,21 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou
}
if (this.sourceRoot) {
// Try to remove the sourceRoot
var relativeUrl = util.relative(this.sourceRoot, aSource);
if (this._sources.has(relativeUrl)) {
return this.sourcesContent[this._sources.indexOf(relativeUrl)];
}
aSource = util.relative(this.sourceRoot, aSource);
}
if (this._sources.has(aSource)) {
return this.sourcesContent[this._sources.indexOf(aSource)];
}
var url;
if (this.sourceRoot
&& (url = util.urlParse(this.sourceRoot))
&& (!url.path || url.path == "/")
&& this._sources.has("/" + aSource)) {
return this.sourcesContent[this._sources.indexOf("/" + aSource)];
}
throw new Error('"' + aSource + '" is not in the SourceMap.');
};
@ -485,6 +489,25 @@ define('source-map/util', ['require', 'exports', 'module' , ], function(require,
path: match[7]
};
}
exports.urlParse = urlParse;
function urlGenerate(aParsedUrl) {
var url = aParsedUrl.scheme + "://";
if (aParsedUrl.auth) {
url += aParsedUrl.auth + "@"
}
if (aParsedUrl.host) {
url += aParsedUrl.host;
}
if (aParsedUrl.port) {
url += ":" + aParsedUrl.port
}
if (aParsedUrl.path) {
url += aParsedUrl.path;
}
return url;
}
exports.urlGenerate = urlGenerate;
function join(aRoot, aPath) {
var url;
@ -494,7 +517,8 @@ define('source-map/util', ['require', 'exports', 'module' , ], function(require,
}
if (aPath.charAt(0) === '/' && (url = urlParse(aRoot))) {
return aRoot.replace(url.path, '') + aPath;
url.path = aPath;
return urlGenerate(url);
}
return aRoot.replace(/\/$/, '') + '/' + aPath;
@ -522,6 +546,12 @@ define('source-map/util', ['require', 'exports', 'module' , ], function(require,
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;
@ -1130,6 +1160,24 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
}
};
function cmpLocation(loc1, loc2) {
var cmp = (loc1 && loc1.line) - (loc2 && loc2.line);
return cmp ? cmp : (loc1 && loc1.column) - (loc2 && loc2.column);
}
function strcmp(str1, str2) {
str1 = str1 || '';
str2 = str2 || '';
return (str1 > str2) - (str1 < str2);
}
function cmpMapping(mappingA, mappingB) {
return cmpLocation(mappingA.generated, mappingB.generated) ||
cmpLocation(mappingA.original, mappingB.original) ||
strcmp(mappingA.source, mappingB.source) ||
strcmp(mappingA.name, mappingB.name);
}
/**
* Serialize the accumulated mappings in to the stream of base 64 VLQs
* specified by the source map format.
@ -1150,12 +1198,7 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
// via the ';' separators) will be all messed up. Note: it might be more
// performant to maintain the sorting as we insert them, rather than as we
// serialize them, but the big O is the same either way.
this._mappings.sort(function (mappingA, mappingB) {
var cmp = mappingA.generated.line - mappingB.generated.line;
return cmp === 0
? mappingA.generated.column - mappingB.generated.column
: cmp;
});
this._mappings.sort(cmpMapping);
for (var i = 0, len = this._mappings.length; i < len; i++) {
mapping = this._mappings[i];
@ -1169,6 +1212,9 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
}
else {
if (i > 0) {
if (!cmpMapping(mapping, this._mappings[i - 1])) {
continue;
}
result += ',';
}
}

View File

@ -265,6 +265,25 @@ define('lib/source-map/util', ['require', 'exports', 'module' , ], function(requ
path: match[7]
};
}
exports.urlParse = urlParse;
function urlGenerate(aParsedUrl) {
var url = aParsedUrl.scheme + "://";
if (aParsedUrl.auth) {
url += aParsedUrl.auth + "@"
}
if (aParsedUrl.host) {
url += aParsedUrl.host;
}
if (aParsedUrl.port) {
url += ":" + aParsedUrl.port
}
if (aParsedUrl.path) {
url += aParsedUrl.path;
}
return url;
}
exports.urlGenerate = urlGenerate;
function join(aRoot, aPath) {
var url;
@ -274,7 +293,8 @@ define('lib/source-map/util', ['require', 'exports', 'module' , ], function(requ
}
if (aPath.charAt(0) === '/' && (url = urlParse(aRoot))) {
return aRoot.replace(url.path, '') + aPath;
url.path = aPath;
return urlGenerate(url);
}
return aRoot.replace(/\/$/, '') + '/' + aPath;
@ -302,6 +322,12 @@ define('lib/source-map/util', ['require', 'exports', 'module' , ], function(requ
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;

View File

@ -293,6 +293,21 @@ define("test/source-map/test-source-map-consumer", ["require", "exports", "modul
'Source should be relative the host of the source root.');
};
exports['test github issue #64'] = function (assert, util) {
var map = new SourceMapConsumer({
"version": 3,
"file": "foo.js",
"sourceRoot": "http://example.com/",
"sources": ["/a"],
"names": [],
"mappings": "AACA",
"sourcesContent": ["foo"]
});
assert.equal(map.sourceContentFor("a"), "foo");
assert.equal(map.sourceContentFor("/a"), "foo");
};
});
function run_test() {
runSourceMapTests('test/source-map/test-source-map-consumer', do_throw);

View File

@ -273,6 +273,126 @@ define("test/source-map/test-source-map-generator", ["require", "exports", "modu
util.assertEqualMaps(assert, actualMap, expectedMap);
};
exports['test sorting with duplicate generated mappings'] = function (assert, util) {
var map = new SourceMapGenerator({
file: 'test.js'
});
map.addMapping({
generated: { line: 3, column: 0 },
original: { line: 2, column: 0 },
source: 'a.js'
});
map.addMapping({
generated: { line: 2, column: 0 }
});
map.addMapping({
generated: { line: 2, column: 0 }
});
map.addMapping({
generated: { line: 1, column: 0 },
original: { line: 1, column: 0 },
source: 'a.js'
});
util.assertEqualMaps(assert, map.toJSON(), {
version: 3,
file: 'test.js',
sources: ['a.js'],
names: [],
mappings: 'AAAA;A;AACA'
});
};
exports['test ignore duplicate mappings.'] = function (assert, util) {
var init = { file: 'min.js', sourceRoot: '/the/root' };
var map1, map2;
// null original source location
var nullMapping1 = {
generated: { line: 1, column: 0 }
};
var nullMapping2 = {
generated: { line: 2, column: 2 }
};
map1 = new SourceMapGenerator(init);
map2 = new SourceMapGenerator(init);
map1.addMapping(nullMapping1);
map1.addMapping(nullMapping1);
map2.addMapping(nullMapping1);
util.assertEqualMaps(assert, map1.toJSON(), map2.toJSON());
map1.addMapping(nullMapping2);
map1.addMapping(nullMapping1);
map2.addMapping(nullMapping2);
util.assertEqualMaps(assert, map1.toJSON(), map2.toJSON());
// original source location
var srcMapping1 = {
generated: { line: 1, column: 0 },
original: { line: 11, column: 0 },
source: 'srcMapping1.js'
};
var srcMapping2 = {
generated: { line: 2, column: 2 },
original: { line: 11, column: 0 },
source: 'srcMapping2.js'
};
map1 = new SourceMapGenerator(init);
map2 = new SourceMapGenerator(init);
map1.addMapping(srcMapping1);
map1.addMapping(srcMapping1);
map2.addMapping(srcMapping1);
util.assertEqualMaps(assert, map1.toJSON(), map2.toJSON());
map1.addMapping(srcMapping2);
map1.addMapping(srcMapping1);
map2.addMapping(srcMapping2);
util.assertEqualMaps(assert, map1.toJSON(), map2.toJSON());
// full original source and name information
var fullMapping1 = {
generated: { line: 1, column: 0 },
original: { line: 11, column: 0 },
source: 'fullMapping1.js',
name: 'fullMapping1'
};
var fullMapping2 = {
generated: { line: 2, column: 2 },
original: { line: 11, column: 0 },
source: 'fullMapping2.js',
name: 'fullMapping2'
};
map1 = new SourceMapGenerator(init);
map2 = new SourceMapGenerator(init);
map1.addMapping(fullMapping1);
map1.addMapping(fullMapping1);
map2.addMapping(fullMapping1);
util.assertEqualMaps(assert, map1.toJSON(), map2.toJSON());
map1.addMapping(fullMapping2);
map1.addMapping(fullMapping1);
map2.addMapping(fullMapping2);
util.assertEqualMaps(assert, map1.toJSON(), map2.toJSON());
};
});
function run_test() {
runSourceMapTests('test/source-map/test-source-map-generator', do_throw);