[Pkg-javascript-commits] [node-source-map] 01/09: New upstream version 0.6.0+dfsg

Julien Puydt julien.puydt at laposte.net
Thu Sep 28 05:55:58 UTC 2017


This is an automated email from the git hooks/post-receive script.

jpuydt-guest pushed a commit to branch master
in repository node-source-map.

commit 050af2959edb26ba74e2179e91632a83e4d5d37a
Author: Julien Puydt <julien.puydt at laposte.net>
Date:   Thu Sep 28 07:44:52 2017 +0200

    New upstream version 0.6.0+dfsg
---
 .gitignore                             |   3 +
 README.md                              |  41 ++-
 dist/source-map.debug.js               | 356 ++++++++++++++++-------
 dist/source-map.js                     | 354 ++++++++++++++++-------
 dist/source-map.min.js                 |   2 +-
 dist/source-map.min.js.map             |   2 +-
 dist/test/test_api.js                  | 360 ++++++++++++++++-------
 dist/test/test_array_set.js            | 136 +++++++--
 dist/test/test_base64.js               |  10 +-
 dist/test/test_base64_vlq.js           |  14 +-
 dist/test/test_binary_search.js        |  10 +-
 dist/test/test_dog_fooding.js          | 384 +++++++++++++++++++------
 dist/test/test_quick_sort.js           |  10 +-
 dist/test/test_source_map_consumer.js  | 509 ++++++++++++++++++++++++++-------
 dist/test/test_source_map_generator.js | 436 +++++++++++++++++++++-------
 dist/test/test_source_node.js          | 443 ++++++++++++++++++++--------
 dist/test/test_util.js                 | 139 ++++++++-
 lib/source-map-consumer.js             | 143 +++++----
 lib/source-map-generator.js            |   9 +
 lib/source-node.js                     |   4 +-
 lib/util.js                            |  83 +++++-
 package.json                           |   3 +-
 test/test-source-map-consumer.js       | 125 +++++++-
 test/test-source-map-generator.js      |  24 ++
 test/test-source-node.js               |  31 +-
 test/test-util.js                      |  44 +++
 test/util.js                           |  52 ++++
 27 files changed, 2880 insertions(+), 847 deletions(-)

diff --git a/.gitignore b/.gitignore
index 8d87b1d..ab5c793 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,4 @@
+*.iml
+*.log
+.idea
 node_modules/*
diff --git a/README.md b/README.md
index 3281339..fea4beb 100644
--- a/README.md
+++ b/README.md
@@ -248,9 +248,13 @@ Returns the original source, line, and column information for the generated
 source's line and column positions provided. The only argument is an object with
 the following properties:
 
-* `line`: The line number in the generated source.
+* `line`: The line number in the generated source.  Line numbers in
+  this library are 1-based (note that the underlying source map
+  specification uses 0-based line numbers -- this library handles the
+  translation).
 
-* `column`: The column number in the generated source.
+* `column`: The column number in the generated source.  Column numbers
+  in this library are 0-based.
 
 * `bias`: Either `SourceMapConsumer.GREATEST_LOWER_BOUND` or
   `SourceMapConsumer.LEAST_UPPER_BOUND`. Specifies whether to return the closest
@@ -264,10 +268,10 @@ and an object is returned with the following properties:
   available.
 
 * `line`: The line number in the original source, or null if this information is
-  not available.
+  not available.  The line number is 1-based.
 
 * `column`: The column number in the original source, or null if this
-  information is not available.
+  information is not available.  The column number is 0-based.
 
 * `name`: The original identifier, or null if this information is not available.
 
@@ -293,15 +297,19 @@ the following properties:
 
 * `source`: The filename of the original source.
 
-* `line`: The line number in the original source.
+* `line`: The line number in the original source.  The line number is
+  1-based.
 
-* `column`: The column number in the original source.
+* `column`: The column number in the original source.  The column
+  number is 0-based.
 
 and an object is returned with the following properties:
 
-* `line`: The line number in the generated source, or null.
+* `line`: The line number in the generated source, or null.  The line
+  number is 1-based.
 
-* `column`: The column number in the generated source, or null.
+* `column`: The column number in the generated source, or null.  The
+  column number is 0-based.
 
 ```js
 consumer.generatedPositionFor({ source: "example.js", line: 2, column: 10 })
@@ -322,15 +330,19 @@ The only argument is an object with the following properties:
 
 * `source`: The filename of the original source.
 
-* `line`: The line number in the original source.
+* `line`: The line number in the original source.  The line number is
+  1-based.
 
-* `column`: Optional. The column number in the original source.
+* `column`: Optional. The column number in the original source.  The
+  column number is 0-based.
 
 and an array of objects is returned, each with the following properties:
 
-* `line`: The line number in the generated source, or null.
+* `line`: The line number in the generated source, or null.  The line
+  number is 1-based.
 
-* `column`: The column number in the generated source, or null.
+* `column`: The column number in the generated source, or null.  The
+  column number is 0-based.
 
 ```js
 consumer.allGeneratedpositionsfor({ line: 2, source: "foo.coffee" })
@@ -534,10 +546,11 @@ use before outputting the generated JS and source map.
 #### new SourceNode([line, column, source[, chunk[, name]]])
 
 * `line`: The original line number associated with this source node, or null if
-  it isn't associated with an original line.
+  it isn't associated with an original line.  The line number is 1-based.
 
 * `column`: The original column number associated with this source node, or null
-  if it isn't associated with an original column.
+  if it isn't associated with an original column.  The column number
+  is 0-based.
 
 * `source`: The original source's filename; null if no filename is provided.
 
diff --git a/dist/source-map.debug.js b/dist/source-map.debug.js
index a3bcda1..4308614 100644
--- a/dist/source-map.debug.js
+++ b/dist/source-map.debug.js
@@ -52,7 +52,7 @@ return /******/ (function(modules) { // webpackBootstrap
 /************************************************************************/
 /******/ ([
 /* 0 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/*
 	 * Copyright 2009-2011 Mozilla Foundation and contributors
@@ -64,9 +64,9 @@ return /******/ (function(modules) { // webpackBootstrap
 	exports.SourceNode = __webpack_require__(10).SourceNode;
 
 
-/***/ },
+/***/ }),
 /* 1 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -142,6 +142,15 @@ return /******/ (function(modules) { // webpackBootstrap
 	      generator.addMapping(newMapping);
 	    });
 	    aSourceMapConsumer.sources.forEach(function (sourceFile) {
+	      var sourceRelative = sourceFile;
+	      if (sourceRoot !== null) {
+	        sourceRelative = util.relative(sourceRoot, sourceFile);
+	      }
+	
+	      if (!generator._sources.has(sourceRelative)) {
+	        generator._sources.add(sourceRelative);
+	      }
+	
 	      var content = aSourceMapConsumer.sourceContentFor(sourceFile);
 	      if (content != null) {
 	        generator.setSourceContent(sourceFile, content);
@@ -329,6 +338,18 @@ return /******/ (function(modules) { // webpackBootstrap
 	SourceMapGenerator.prototype._validateMapping =
 	  function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource,
 	                                              aName) {
+	    // When aOriginal is truthy but has empty values for .line and .column,
+	    // it is most likely a programmer error. In this case we throw a very
+	    // specific error message to try to guide them the right way.
+	    // For example: https://github.com/Polymer/polymer-bundler/pull/519
+	    if (aOriginal && typeof aOriginal.line !== 'number' && typeof aOriginal.column !== 'number') {
+	        throw new Error(
+	            'original.line and original.column are not numbers -- you probably meant to omit ' +
+	            'the original mapping entirely and only map the generated position. If so, pass ' +
+	            'null for the original mapping instead of an object with empty or null values.'
+	        );
+	    }
+	
 	    if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
 	        && aGenerated.line > 0 && aGenerated.column >= 0
 	        && !aOriginal && !aSource && !aName) {
@@ -474,9 +495,9 @@ return /******/ (function(modules) { // webpackBootstrap
 	exports.SourceMapGenerator = SourceMapGenerator;
 
 
-/***/ },
+/***/ }),
 /* 2 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -620,9 +641,9 @@ return /******/ (function(modules) { // webpackBootstrap
 	};
 
 
-/***/ },
+/***/ }),
 /* 3 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -693,9 +714,9 @@ return /******/ (function(modules) { // webpackBootstrap
 	};
 
 
-/***/ },
+/***/ }),
 /* 4 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -725,7 +746,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	}
 	exports.getArg = getArg;
 	
-	var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/;
+	var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/;
 	var dataUrlRegexp = /^data:.+\,.+$/;
 	
 	function urlParse(aUrl) {
@@ -768,7 +789,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	/**
 	 * Normalizes a path, or the path portion of a URL:
 	 *
-	 * - Replaces consequtive slashes with one slash.
+	 * - Replaces consecutive slashes with one slash.
 	 * - Removes unnecessary '.' parts.
 	 * - Removes unnecessary '<dir>/..' parts.
 	 *
@@ -881,7 +902,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	exports.join = join;
 	
 	exports.isAbsolute = function (aPath) {
-	  return aPath.charAt(0) === '/' || !!aPath.match(urlRegexp);
+	  return aPath.charAt(0) === '/' || urlRegexp.test(aPath);
 	};
 	
 	/**
@@ -1001,7 +1022,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	 * stubbed out mapping.
 	 */
 	function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
-	  var cmp = mappingA.source - mappingB.source;
+	  var cmp = strcmp(mappingA.source, mappingB.source);
 	  if (cmp !== 0) {
 	    return cmp;
 	  }
@@ -1026,7 +1047,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	    return cmp;
 	  }
 	
-	  return mappingA.name - mappingB.name;
+	  return strcmp(mappingA.name, mappingB.name);
 	}
 	exports.compareByOriginalPositions = compareByOriginalPositions;
 	
@@ -1050,7 +1071,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	    return cmp;
 	  }
 	
-	  cmp = mappingA.source - mappingB.source;
+	  cmp = strcmp(mappingA.source, mappingB.source);
 	  if (cmp !== 0) {
 	    return cmp;
 	  }
@@ -1065,7 +1086,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	    return cmp;
 	  }
 	
-	  return mappingA.name - mappingB.name;
+	  return strcmp(mappingA.name, mappingB.name);
 	}
 	exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated;
 	
@@ -1074,6 +1095,14 @@ return /******/ (function(modules) { // webpackBootstrap
 	    return 0;
 	  }
 	
+	  if (aStr1 === null) {
+	    return 1; // aStr2 !== null
+	  }
+	
+	  if (aStr2 === null) {
+	    return -1; // aStr1 !== null
+	  }
+	
 	  if (aStr1 > aStr2) {
 	    return 1;
 	  }
@@ -1114,11 +1143,74 @@ return /******/ (function(modules) { // webpackBootstrap
 	  return strcmp(mappingA.name, mappingB.name);
 	}
 	exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated;
+	
+	/**
+	 * Strip any JSON XSSI avoidance prefix from the string (as documented
+	 * in the source maps specification), and then parse the string as
+	 * JSON.
+	 */
+	function parseSourceMapInput(str) {
+	  return JSON.parse(str.replace(/^\)]}'[^\n]*\n/, ''));
+	}
+	exports.parseSourceMapInput = parseSourceMapInput;
+	
+	/**
+	 * Compute the URL of a source given the the source root, the source's
+	 * URL, and the source map's URL.
+	 */
+	function computeSourceURL(sourceRoot, sourceURL, sourceMapURL) {
+	  sourceURL = sourceURL || '';
+	
+	  if (sourceRoot) {
+	    // This follows what Chrome does.
+	    if (sourceRoot[sourceRoot.length - 1] !== '/' && sourceURL[0] !== '/') {
+	      sourceRoot += '/';
+	    }
+	    // The spec says:
+	    //   Line 4: An optional source root, useful for relocating source
+	    //   files on a server or removing repeated values in the
+	    //   “sources” entry.  This value is prepended to the individual
+	    //   entries in the “source” field.
+	    sourceURL = sourceRoot + sourceURL;
+	  }
+	
+	  // Historically, SourceMapConsumer did not take the sourceMapURL as
+	  // a parameter.  This mode is still somewhat supported, which is why
+	  // this code block is conditional.  However, it's preferable to pass
+	  // the source map URL to SourceMapConsumer, so that this function
+	  // can implement the source URL resolution algorithm as outlined in
+	  // the spec.  This block is basically the equivalent of:
+	  //    new URL(sourceURL, sourceMapURL).toString()
+	  // ... except it avoids using URL, which wasn't available in the
+	  // older releases of node still supported by this library.
+	  //
+	  // The spec says:
+	  //   If the sources are not absolute URLs after prepending of the
+	  //   “sourceRoot”, the sources are resolved relative to the
+	  //   SourceMap (like resolving script src in a html document).
+	  if (sourceMapURL) {
+	    var parsed = urlParse(sourceMapURL);
+	    if (!parsed) {
+	      throw new Error("sourceMapURL could not be parsed");
+	    }
+	    if (parsed.path) {
+	      // Strip the last path component, but keep the "/".
+	      var index = parsed.path.lastIndexOf('/');
+	      if (index >= 0) {
+	        parsed.path = parsed.path.substring(0, index + 1);
+	      }
+	    }
+	    sourceURL = join(urlGenerate(parsed), sourceURL);
+	  }
+	
+	  return normalize(sourceURL);
+	}
+	exports.computeSourceURL = computeSourceURL;
 
 
-/***/ },
+/***/ }),
 /* 5 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -1129,6 +1221,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	
 	var util = __webpack_require__(4);
 	var has = Object.prototype.hasOwnProperty;
+	var hasNativeMap = typeof Map !== "undefined";
 	
 	/**
 	 * A data structure which is a combination of an array and a set. Adding a new
@@ -1138,7 +1231,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	 */
 	function ArraySet() {
 	  this._array = [];
-	  this._set = Object.create(null);
+	  this._set = hasNativeMap ? new Map() : Object.create(null);
 	}
 	
 	/**
@@ -1159,7 +1252,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	 * @returns Number
 	 */
 	ArraySet.prototype.size = function ArraySet_size() {
-	  return Object.getOwnPropertyNames(this._set).length;
+	  return hasNativeMap ? this._set.size : Object.getOwnPropertyNames(this._set).length;
 	};
 	
 	/**
@@ -1168,14 +1261,18 @@ return /******/ (function(modules) { // webpackBootstrap
 	 * @param String aStr
 	 */
 	ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) {
-	  var sStr = util.toSetString(aStr);
-	  var isDuplicate = has.call(this._set, sStr);
+	  var sStr = hasNativeMap ? aStr : util.toSetString(aStr);
+	  var isDuplicate = hasNativeMap ? this.has(aStr) : has.call(this._set, sStr);
 	  var idx = this._array.length;
 	  if (!isDuplicate || aAllowDuplicates) {
 	    this._array.push(aStr);
 	  }
 	  if (!isDuplicate) {
-	    this._set[sStr] = idx;
+	    if (hasNativeMap) {
+	      this._set.set(aStr, idx);
+	    } else {
+	      this._set[sStr] = idx;
+	    }
 	  }
 	};
 	
@@ -1185,8 +1282,12 @@ return /******/ (function(modules) { // webpackBootstrap
 	 * @param String aStr
 	 */
 	ArraySet.prototype.has = function ArraySet_has(aStr) {
-	  var sStr = util.toSetString(aStr);
-	  return has.call(this._set, sStr);
+	  if (hasNativeMap) {
+	    return this._set.has(aStr);
+	  } else {
+	    var sStr = util.toSetString(aStr);
+	    return has.call(this._set, sStr);
+	  }
 	};
 	
 	/**
@@ -1195,10 +1296,18 @@ return /******/ (function(modules) { // webpackBootstrap
 	 * @param String aStr
 	 */
 	ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) {
-	  var sStr = util.toSetString(aStr);
-	  if (has.call(this._set, sStr)) {
-	    return this._set[sStr];
+	  if (hasNativeMap) {
+	    var idx = this._set.get(aStr);
+	    if (idx >= 0) {
+	        return idx;
+	    }
+	  } else {
+	    var sStr = util.toSetString(aStr);
+	    if (has.call(this._set, sStr)) {
+	      return this._set[sStr];
+	    }
 	  }
+	
 	  throw new Error('"' + aStr + '" is not in the set.');
 	};
 	
@@ -1226,9 +1335,9 @@ return /******/ (function(modules) { // webpackBootstrap
 	exports.ArraySet = ArraySet;
 
 
-/***/ },
+/***/ }),
 /* 6 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -1311,9 +1420,9 @@ return /******/ (function(modules) { // webpackBootstrap
 	exports.MappingList = MappingList;
 
 
-/***/ },
+/***/ }),
 /* 7 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -1328,15 +1437,15 @@ return /******/ (function(modules) { // webpackBootstrap
 	var base64VLQ = __webpack_require__(2);
 	var quickSort = __webpack_require__(9).quickSort;
 	
-	function SourceMapConsumer(aSourceMap) {
+	function SourceMapConsumer(aSourceMap, aSourceMapURL) {
 	  var sourceMap = aSourceMap;
 	  if (typeof aSourceMap === 'string') {
-	    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+	    sourceMap = util.parseSourceMapInput(aSourceMap);
 	  }
 	
 	  return sourceMap.sections != null
-	    ? new IndexedSourceMapConsumer(sourceMap)
-	    : new BasicSourceMapConsumer(sourceMap);
+	    ? new IndexedSourceMapConsumer(sourceMap, aSourceMapURL)
+	    : new BasicSourceMapConsumer(sourceMap, aSourceMapURL);
 	}
 	
 	SourceMapConsumer.fromSourceMap = function(aSourceMap) {
@@ -1380,6 +1489,8 @@ return /******/ (function(modules) { // webpackBootstrap
 	
 	SourceMapConsumer.prototype.__generatedMappings = null;
 	Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', {
+	  configurable: true,
+	  enumerable: true,
 	  get: function () {
 	    if (!this.__generatedMappings) {
 	      this._parseMappings(this._mappings, this.sourceRoot);
@@ -1391,6 +1502,8 @@ return /******/ (function(modules) { // webpackBootstrap
 	
 	SourceMapConsumer.prototype.__originalMappings = null;
 	Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', {
+	  configurable: true,
+	  enumerable: true,
 	  get: function () {
 	    if (!this.__originalMappings) {
 	      this._parseMappings(this._mappings, this.sourceRoot);
@@ -1458,9 +1571,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	    var sourceRoot = this.sourceRoot;
 	    mappings.map(function (mapping) {
 	      var source = mapping.source === null ? null : this._sources.at(mapping.source);
-	      if (source != null && sourceRoot != null) {
-	        source = util.join(sourceRoot, source);
-	      }
+	      source = util.computeSourceURL(sourceRoot, source, this._sourceMapURL);
 	      return {
 	        source: source,
 	        generatedLine: mapping.generatedLine,
@@ -1483,13 +1594,16 @@ return /******/ (function(modules) { // webpackBootstrap
 	 * The only argument is an object with the following properties:
 	 *
 	 *   - source: The filename of the original source.
-	 *   - line: The line number in the original source.
+	 *   - line: The line number in the original source.  The line number is 1-based.
 	 *   - column: Optional. the column number in the original source.
+	 *    The column number is 0-based.
 	 *
 	 * and an array of objects is returned, each with the following properties:
 	 *
-	 *   - line: The line number in the generated source, or null.
+	 *   - line: The line number in the generated source, or null.  The
+	 *    line number is 1-based.
 	 *   - column: The column number in the generated source, or null.
+	 *    The column number is 0-based.
 	 */
 	SourceMapConsumer.prototype.allGeneratedPositionsFor =
 	  function SourceMapConsumer_allGeneratedPositionsFor(aArgs) {
@@ -1571,7 +1685,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	 * query for information about the original file positions by giving it a file
 	 * position in the generated source.
 	 *
-	 * The only parameter is the raw source map (either as a JSON string, or
+	 * The first parameter is the raw source map (either as a JSON string, or
 	 * already parsed to an object). According to the spec, source maps have the
 	 * following attributes:
 	 *
@@ -1594,12 +1708,16 @@ return /******/ (function(modules) { // webpackBootstrap
 	 *       mappings: "AA,AB;;ABCDE;"
 	 *     }
 	 *
+	 * The second parameter, if given, is a string whose value is the URL
+	 * at which the source map was found.  This URL is used to compute the
+	 * sources array.
+	 *
 	 * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#
 	 */
-	function BasicSourceMapConsumer(aSourceMap) {
+	function BasicSourceMapConsumer(aSourceMap, aSourceMapURL) {
 	  var sourceMap = aSourceMap;
 	  if (typeof aSourceMap === 'string') {
-	    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+	    sourceMap = util.parseSourceMapInput(aSourceMap);
 	  }
 	
 	  var version = util.getArg(sourceMap, 'version');
@@ -1618,6 +1736,10 @@ return /******/ (function(modules) { // webpackBootstrap
 	    throw new Error('Unsupported version: ' + version);
 	  }
 	
+	  if (sourceRoot) {
+	    sourceRoot = util.normalize(sourceRoot);
+	  }
+	
 	  sources = sources
 	    .map(String)
 	    // Some source maps produce relative source paths like "./foo.js" instead of
@@ -1644,6 +1766,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	  this.sourceRoot = sourceRoot;
 	  this.sourcesContent = sourcesContent;
 	  this._mappings = mappings;
+	  this._sourceMapURL = aSourceMapURL;
 	  this.file = file;
 	}
 	
@@ -1655,10 +1778,12 @@ return /******/ (function(modules) { // webpackBootstrap
 	 *
 	 * @param SourceMapGenerator aSourceMap
 	 *        The source map that will be consumed.
+	 * @param String aSourceMapURL
+	 *        The URL at which the source map can be found (optional)
 	 * @returns BasicSourceMapConsumer
 	 */
 	BasicSourceMapConsumer.fromSourceMap =
-	  function SourceMapConsumer_fromSourceMap(aSourceMap) {
+	  function SourceMapConsumer_fromSourceMap(aSourceMap, aSourceMapURL) {
 	    var smc = Object.create(BasicSourceMapConsumer.prototype);
 	
 	    var names = smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);
@@ -1667,6 +1792,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	    smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(),
 	                                                            smc.sourceRoot);
 	    smc.file = aSourceMap._file;
+	    smc._sourceMapURL = aSourceMapURL;
 	
 	    // Because we are modifying the entries (by converting string sources and
 	    // names to indices into the sources and names ArraySets), we have to make
@@ -1714,7 +1840,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	Object.defineProperty(BasicSourceMapConsumer.prototype, 'sources', {
 	  get: function () {
 	    return this._sources.toArray().map(function (s) {
-	      return this.sourceRoot != null ? util.join(this.sourceRoot, s) : s;
+	      return util.computeSourceURL(this.sourceRoot, s, this._sourceMapURL);
 	    }, this);
 	  }
 	});
@@ -1896,8 +2022,10 @@ return /******/ (function(modules) { // webpackBootstrap
 	 * source's line and column positions provided. The only argument is an object
 	 * with the following properties:
 	 *
-	 *   - line: The line number in the generated source.
-	 *   - column: The column number in the generated source.
+	 *   - line: The line number in the generated source.  The line number
+	 *     is 1-based.
+	 *   - column: The column number in the generated source.  The column
+	 *     number is 0-based.
 	 *   - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or
 	 *     'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the
 	 *     closest element that is smaller than or greater than the one we are
@@ -1907,8 +2035,10 @@ return /******/ (function(modules) { // webpackBootstrap
 	 * and an object is returned with the following properties:
 	 *
 	 *   - source: The original source file, or null.
-	 *   - line: The line number in the original source, or null.
-	 *   - column: The column number in the original source, or null.
+	 *   - line: The line number in the original source, or null.  The
+	 *     line number is 1-based.
+	 *   - column: The column number in the original source, or null.  The
+	 *     column number is 0-based.
 	 *   - name: The original identifier, or null.
 	 */
 	BasicSourceMapConsumer.prototype.originalPositionFor =
@@ -1934,9 +2064,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	        var source = util.getArg(mapping, 'source', null);
 	        if (source !== null) {
 	          source = this._sources.at(source);
-	          if (this.sourceRoot != null) {
-	            source = util.join(this.sourceRoot, source);
-	          }
+	          source = util.computeSourceURL(this.sourceRoot, source, this._sourceMapURL);
 	        }
 	        var name = util.getArg(mapping, 'name', null);
 	        if (name !== null) {
@@ -1983,12 +2111,23 @@ return /******/ (function(modules) { // webpackBootstrap
 	      return null;
 	    }
 	
+	    var relativeSource = aSource;
 	    if (this.sourceRoot != null) {
-	      aSource = util.relative(this.sourceRoot, aSource);
+	      relativeSource = util.relative(this.sourceRoot, relativeSource);
 	    }
 	
-	    if (this._sources.has(aSource)) {
-	      return this.sourcesContent[this._sources.indexOf(aSource)];
+	    if (this._sources.has(relativeSource)) {
+	      return this.sourcesContent[this._sources.indexOf(relativeSource)];
+	    }
+	
+	    // Maybe aSource is an absolute URL as returned by |sources|.  In
+	    // this case we can't simply undo the transform.
+	    var sourceArray = this.sources;
+	    var i;
+	    for (i = 0; i < sourceArray.length; ++i) {
+	      if (sourceArray[i] == aSource) {
+	        return this.sourcesContent[i];
+	      }
 	    }
 	
 	    var url;
@@ -1998,15 +2137,15 @@ return /******/ (function(modules) { // webpackBootstrap
 	      // many users. We can help them out when they expect file:// URIs to
 	      // behave like it would if they were running a local HTTP server. See
 	      // https://bugzilla.mozilla.org/show_bug.cgi?id=885597.
-	      var fileUriAbsPath = aSource.replace(/^file:\/\//, "");
+	      var fileUriAbsPath = relativeSource.replace(/^file:\/\//, "");
 	      if (url.scheme == "file"
 	          && this._sources.has(fileUriAbsPath)) {
 	        return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)]
 	      }
 	
 	      if ((!url.path || url.path == "/")
-	          && this._sources.has("/" + aSource)) {
-	        return this.sourcesContent[this._sources.indexOf("/" + aSource)];
+	          && this._sources.has("/" + relativeSource)) {
+	        return this.sourcesContent[this._sources.indexOf("/" + relativeSource)];
 	      }
 	    }
 	
@@ -2018,7 +2157,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	      return null;
 	    }
 	    else {
-	      throw new Error('"' + aSource + '" is not in the SourceMap.');
+	      throw new Error('"' + relativeSource + '" is not in the SourceMap.');
 	    }
 	  };
 	
@@ -2028,8 +2167,10 @@ return /******/ (function(modules) { // webpackBootstrap
 	 * the following properties:
 	 *
 	 *   - source: The filename of the original source.
-	 *   - line: The line number in the original source.
-	 *   - column: The column number in the original source.
+	 *   - line: The line number in the original source.  The line number
+	 *     is 1-based.
+	 *   - column: The column number in the original source.  The column
+	 *     number is 0-based.
 	 *   - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or
 	 *     'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the
 	 *     closest element that is smaller than or greater than the one we are
@@ -2038,8 +2179,10 @@ return /******/ (function(modules) { // webpackBootstrap
 	 *
 	 * and an object is returned with the following properties:
 	 *
-	 *   - line: The line number in the generated source, or null.
+	 *   - line: The line number in the generated source, or null.  The
+	 *     line number is 1-based.
 	 *   - column: The column number in the generated source, or null.
+	 *     The column number is 0-based.
 	 */
 	BasicSourceMapConsumer.prototype.generatedPositionFor =
 	  function SourceMapConsumer_generatedPositionFor(aArgs) {
@@ -2098,7 +2241,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	 * that it takes "indexed" source maps (i.e. ones with a "sections" field) as
 	 * input.
 	 *
-	 * The only parameter is a raw source map (either as a JSON string, or already
+	 * The first parameter is a raw source map (either as a JSON string, or already
 	 * parsed to an object). According to the spec for indexed source maps, they
 	 * have the following attributes:
 	 *
@@ -2135,12 +2278,16 @@ return /******/ (function(modules) { // webpackBootstrap
 	 *    }],
 	 *  }
 	 *
+	 * The second parameter, if given, is a string whose value is the URL
+	 * at which the source map was found.  This URL is used to compute the
+	 * sources array.
+	 *
 	 * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.535es3xeprgt
 	 */
-	function IndexedSourceMapConsumer(aSourceMap) {
+	function IndexedSourceMapConsumer(aSourceMap, aSourceMapURL) {
 	  var sourceMap = aSourceMap;
 	  if (typeof aSourceMap === 'string') {
-	    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+	    sourceMap = util.parseSourceMapInput(aSourceMap);
 	  }
 	
 	  var version = util.getArg(sourceMap, 'version');
@@ -2180,7 +2327,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	        generatedLine: offsetLine + 1,
 	        generatedColumn: offsetColumn + 1
 	      },
-	      consumer: new SourceMapConsumer(util.getArg(s, 'map'))
+	      consumer: new SourceMapConsumer(util.getArg(s, 'map'), aSourceMapURL)
 	    }
 	  });
 	}
@@ -2213,14 +2360,18 @@ return /******/ (function(modules) { // webpackBootstrap
 	 * source's line and column positions provided. The only argument is an object
 	 * with the following properties:
 	 *
-	 *   - line: The line number in the generated source.
-	 *   - column: The column number in the generated source.
+	 *   - line: The line number in the generated source.  The line number
+	 *     is 1-based.
+	 *   - column: The column number in the generated source.  The column
+	 *     number is 0-based.
 	 *
 	 * and an object is returned with the following properties:
 	 *
 	 *   - source: The original source file, or null.
-	 *   - line: The line number in the original source, or null.
-	 *   - column: The column number in the original source, or null.
+	 *   - line: The line number in the original source, or null.  The
+	 *     line number is 1-based.
+	 *   - column: The column number in the original source, or null.  The
+	 *     column number is 0-based.
 	 *   - name: The original identifier, or null.
 	 */
 	IndexedSourceMapConsumer.prototype.originalPositionFor =
@@ -2304,13 +2455,17 @@ return /******/ (function(modules) { // webpackBootstrap
 	 * the following properties:
 	 *
 	 *   - source: The filename of the original source.
-	 *   - line: The line number in the original source.
-	 *   - column: The column number in the original source.
+	 *   - line: The line number in the original source.  The line number
+	 *     is 1-based.
+	 *   - column: The column number in the original source.  The column
+	 *     number is 0-based.
 	 *
 	 * and an object is returned with the following properties:
 	 *
-	 *   - line: The line number in the generated source, or null.
+	 *   - line: The line number in the generated source, or null.  The
+	 *     line number is 1-based. 
 	 *   - column: The column number in the generated source, or null.
+	 *     The column number is 0-based.
 	 */
 	IndexedSourceMapConsumer.prototype.generatedPositionFor =
 	  function IndexedSourceMapConsumer_generatedPositionFor(aArgs) {
@@ -2358,15 +2513,16 @@ return /******/ (function(modules) { // webpackBootstrap
 	        var mapping = sectionMappings[j];
 	
 	        var source = section.consumer._sources.at(mapping.source);
-	        if (section.consumer.sourceRoot !== null) {
-	          source = util.join(section.consumer.sourceRoot, source);
-	        }
+	        source = util.computeSourceURL(section.consumer.sourceRoot, source, this._sourceMapURL);
 	        this._sources.add(source);
 	        source = this._sources.indexOf(source);
 	
-	        var name = section.consumer._names.at(mapping.name);
-	        this._names.add(name);
-	        name = this._names.indexOf(name);
+	        var name = null;
+	        if (mapping.name) {
+	          name = section.consumer._names.at(mapping.name);
+	          this._names.add(name);
+	          name = this._names.indexOf(name);
+	        }
 	
 	        // The mappings coming from the consumer for the section have
 	        // generated positions relative to the start of the section, so we
@@ -2399,9 +2555,9 @@ return /******/ (function(modules) { // webpackBootstrap
 	exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer;
 
 
-/***/ },
+/***/ }),
 /* 8 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -2516,9 +2672,9 @@ return /******/ (function(modules) { // webpackBootstrap
 	};
 
 
-/***/ },
+/***/ }),
 /* 9 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -2636,9 +2792,9 @@ return /******/ (function(modules) { // webpackBootstrap
 	};
 
 
-/***/ },
+/***/ }),
 /* 10 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -2702,13 +2858,19 @@ return /******/ (function(modules) { // webpackBootstrap
 	    // 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`.
+	    // Processed fragments are accessed by calling `shiftNextLine`.
 	    var remainingLines = aGeneratedCode.split(REGEX_NEWLINE);
+	    var remainingLinesIndex = 0;
 	    var shiftNextLine = function() {
-	      var lineContents = remainingLines.shift();
+	      var lineContents = getNextLine();
 	      // The last line of a file might not have a newline.
-	      var newLine = remainingLines.shift() || "";
+	      var newLine = getNextLine() || "";
 	      return lineContents + newLine;
+	
+	      function getNextLine() {
+	        return remainingLinesIndex < remainingLines.length ?
+	            remainingLines[remainingLinesIndex++] : undefined;
+	      }
 	    };
 	
 	    // We need to remember the position of "remainingLines"
@@ -2733,10 +2895,10 @@ return /******/ (function(modules) { // webpackBootstrap
 	          // There is no new line in between.
 	          // Associate the code between "lastGeneratedColumn" and
 	          // "mapping.generatedColumn" with "lastMapping"
-	          var nextLine = remainingLines[0];
+	          var nextLine = remainingLines[remainingLinesIndex] || '';
 	          var code = nextLine.substr(0, mapping.generatedColumn -
 	                                        lastGeneratedColumn);
-	          remainingLines[0] = nextLine.substr(mapping.generatedColumn -
+	          remainingLines[remainingLinesIndex] = nextLine.substr(mapping.generatedColumn -
 	                                              lastGeneratedColumn);
 	          lastGeneratedColumn = mapping.generatedColumn;
 	          addMappingWithCode(lastMapping, code);
@@ -2753,21 +2915,21 @@ return /******/ (function(modules) { // webpackBootstrap
 	        lastGeneratedLine++;
 	      }
 	      if (lastGeneratedColumn < mapping.generatedColumn) {
-	        var nextLine = remainingLines[0];
+	        var nextLine = remainingLines[remainingLinesIndex] || '';
 	        node.add(nextLine.substr(0, mapping.generatedColumn));
-	        remainingLines[0] = nextLine.substr(mapping.generatedColumn);
+	        remainingLines[remainingLinesIndex] = nextLine.substr(mapping.generatedColumn);
 	        lastGeneratedColumn = mapping.generatedColumn;
 	      }
 	      lastMapping = mapping;
 	    }, this);
 	    // We have processed all mappings.
-	    if (remainingLines.length > 0) {
+	    if (remainingLinesIndex < remainingLines.length) {
 	      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(""));
+	      node.add(remainingLines.splice(remainingLinesIndex).join(""));
 	    }
 	
 	    // Copy sourcesContent into SourceNode
@@ -3049,8 +3211,8 @@ return /******/ (function(modules) { // webpackBootstrap
 	exports.SourceNode = SourceNode;
 
 
-/***/ }
+/***/ })
 /******/ ])
 });
 ;
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay91bml2ZXJzYWxNb2R1bGVEZWZpbml0aW9uIiwid2VicGFjazovLy93ZWJwYWNrL2Jvb3RzdHJhcCBlMTM0NGZmMjJhYzA2Zjk0NWVlNCIsIndlYnBhY2s6Ly8vLi9zb3VyY2UtbWFwLmpzIiwid2VicGFjazovLy8uL2xpYi9zb3VyY2UtbWFwLWdlbmVyYXRvci5qcyIsIndlYnBhY2s6Ly8vLi9saWIvYmFzZTY0LXZscS5qcyIsIndlYnBhY2s6Ly8vLi9saWIvYmFzZTY0LmpzIiwid2VicGFjazovLy8uL2xpYi91dGlsLmpzIiwid2VicGFjazovLy8uL2xpYi9hcnJheS1zZXQuanMiLCJ3ZWJwYWNrOi8vLy4vbG [...]
\ No newline at end of file
+//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay91bml2ZXJzYWxNb2R1bGVEZWZpbml0aW9uIiwid2VicGFjazovLy93ZWJwYWNrL2Jvb3RzdHJhcCA3MmMxZTZhODQ5ZWNiOTIwODM1MCIsIndlYnBhY2s6Ly8vLi9zb3VyY2UtbWFwLmpzIiwid2VicGFjazovLy8uL2xpYi9zb3VyY2UtbWFwLWdlbmVyYXRvci5qcyIsIndlYnBhY2s6Ly8vLi9saWIvYmFzZTY0LXZscS5qcyIsIndlYnBhY2s6Ly8vLi9saWIvYmFzZTY0LmpzIiwid2VicGFjazovLy8uL2xpYi91dGlsLmpzIiwid2VicGFjazovLy8uL2xpYi9hcnJheS1zZXQuanMiLCJ3ZWJw [...]
\ No newline at end of file
diff --git a/dist/source-map.js b/dist/source-map.js
index 61c0d8d..6022d2a 100644
--- a/dist/source-map.js
+++ b/dist/source-map.js
@@ -52,7 +52,7 @@ return /******/ (function(modules) { // webpackBootstrap
 /************************************************************************/
 /******/ ([
 /* 0 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/*
 	 * Copyright 2009-2011 Mozilla Foundation and contributors
@@ -64,9 +64,9 @@ return /******/ (function(modules) { // webpackBootstrap
 	exports.SourceNode = __webpack_require__(10).SourceNode;
 
 
-/***/ },
+/***/ }),
 /* 1 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -142,6 +142,15 @@ return /******/ (function(modules) { // webpackBootstrap
 	      generator.addMapping(newMapping);
 	    });
 	    aSourceMapConsumer.sources.forEach(function (sourceFile) {
+	      var sourceRelative = sourceFile;
+	      if (sourceRoot !== null) {
+	        sourceRelative = util.relative(sourceRoot, sourceFile);
+	      }
+
+	      if (!generator._sources.has(sourceRelative)) {
+	        generator._sources.add(sourceRelative);
+	      }
+
 	      var content = aSourceMapConsumer.sourceContentFor(sourceFile);
 	      if (content != null) {
 	        generator.setSourceContent(sourceFile, content);
@@ -329,6 +338,18 @@ return /******/ (function(modules) { // webpackBootstrap
 	SourceMapGenerator.prototype._validateMapping =
 	  function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource,
 	                                              aName) {
+	    // When aOriginal is truthy but has empty values for .line and .column,
+	    // it is most likely a programmer error. In this case we throw a very
+	    // specific error message to try to guide them the right way.
+	    // For example: https://github.com/Polymer/polymer-bundler/pull/519
+	    if (aOriginal && typeof aOriginal.line !== 'number' && typeof aOriginal.column !== 'number') {
+	        throw new Error(
+	            'original.line and original.column are not numbers -- you probably meant to omit ' +
+	            'the original mapping entirely and only map the generated position. If so, pass ' +
+	            'null for the original mapping instead of an object with empty or null values.'
+	        );
+	    }
+
 	    if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
 	        && aGenerated.line > 0 && aGenerated.column >= 0
 	        && !aOriginal && !aSource && !aName) {
@@ -474,9 +495,9 @@ return /******/ (function(modules) { // webpackBootstrap
 	exports.SourceMapGenerator = SourceMapGenerator;
 
 
-/***/ },
+/***/ }),
 /* 2 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -620,9 +641,9 @@ return /******/ (function(modules) { // webpackBootstrap
 	};
 
 
-/***/ },
+/***/ }),
 /* 3 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -693,9 +714,9 @@ return /******/ (function(modules) { // webpackBootstrap
 	};
 
 
-/***/ },
+/***/ }),
 /* 4 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -725,7 +746,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	}
 	exports.getArg = getArg;
 
-	var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/;
+	var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/;
 	var dataUrlRegexp = /^data:.+\,.+$/;
 
 	function urlParse(aUrl) {
@@ -768,7 +789,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	/**
 	 * Normalizes a path, or the path portion of a URL:
 	 *
-	 * - Replaces consequtive slashes with one slash.
+	 * - Replaces consecutive slashes with one slash.
 	 * - Removes unnecessary '.' parts.
 	 * - Removes unnecessary '<dir>/..' parts.
 	 *
@@ -881,7 +902,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	exports.join = join;
 
 	exports.isAbsolute = function (aPath) {
-	  return aPath.charAt(0) === '/' || !!aPath.match(urlRegexp);
+	  return aPath.charAt(0) === '/' || urlRegexp.test(aPath);
 	};
 
 	/**
@@ -1001,7 +1022,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	 * stubbed out mapping.
 	 */
 	function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
-	  var cmp = mappingA.source - mappingB.source;
+	  var cmp = strcmp(mappingA.source, mappingB.source);
 	  if (cmp !== 0) {
 	    return cmp;
 	  }
@@ -1026,7 +1047,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	    return cmp;
 	  }
 
-	  return mappingA.name - mappingB.name;
+	  return strcmp(mappingA.name, mappingB.name);
 	}
 	exports.compareByOriginalPositions = compareByOriginalPositions;
 
@@ -1050,7 +1071,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	    return cmp;
 	  }
 
-	  cmp = mappingA.source - mappingB.source;
+	  cmp = strcmp(mappingA.source, mappingB.source);
 	  if (cmp !== 0) {
 	    return cmp;
 	  }
@@ -1065,7 +1086,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	    return cmp;
 	  }
 
-	  return mappingA.name - mappingB.name;
+	  return strcmp(mappingA.name, mappingB.name);
 	}
 	exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated;
 
@@ -1074,6 +1095,14 @@ return /******/ (function(modules) { // webpackBootstrap
 	    return 0;
 	  }
 
+	  if (aStr1 === null) {
+	    return 1; // aStr2 !== null
+	  }
+
+	  if (aStr2 === null) {
+	    return -1; // aStr1 !== null
+	  }
+
 	  if (aStr1 > aStr2) {
 	    return 1;
 	  }
@@ -1115,10 +1144,73 @@ return /******/ (function(modules) { // webpackBootstrap
 	}
 	exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated;
 
+	/**
+	 * Strip any JSON XSSI avoidance prefix from the string (as documented
+	 * in the source maps specification), and then parse the string as
+	 * JSON.
+	 */
+	function parseSourceMapInput(str) {
+	  return JSON.parse(str.replace(/^\)]}'[^\n]*\n/, ''));
+	}
+	exports.parseSourceMapInput = parseSourceMapInput;
+
+	/**
+	 * Compute the URL of a source given the the source root, the source's
+	 * URL, and the source map's URL.
+	 */
+	function computeSourceURL(sourceRoot, sourceURL, sourceMapURL) {
+	  sourceURL = sourceURL || '';
+
+	  if (sourceRoot) {
+	    // This follows what Chrome does.
+	    if (sourceRoot[sourceRoot.length - 1] !== '/' && sourceURL[0] !== '/') {
+	      sourceRoot += '/';
+	    }
+	    // The spec says:
+	    //   Line 4: An optional source root, useful for relocating source
+	    //   files on a server or removing repeated values in the
+	    //   “sources” entry.  This value is prepended to the individual
+	    //   entries in the “source” field.
+	    sourceURL = sourceRoot + sourceURL;
+	  }
 
-/***/ },
+	  // Historically, SourceMapConsumer did not take the sourceMapURL as
+	  // a parameter.  This mode is still somewhat supported, which is why
+	  // this code block is conditional.  However, it's preferable to pass
+	  // the source map URL to SourceMapConsumer, so that this function
+	  // can implement the source URL resolution algorithm as outlined in
+	  // the spec.  This block is basically the equivalent of:
+	  //    new URL(sourceURL, sourceMapURL).toString()
+	  // ... except it avoids using URL, which wasn't available in the
+	  // older releases of node still supported by this library.
+	  //
+	  // The spec says:
+	  //   If the sources are not absolute URLs after prepending of the
+	  //   “sourceRoot”, the sources are resolved relative to the
+	  //   SourceMap (like resolving script src in a html document).
+	  if (sourceMapURL) {
+	    var parsed = urlParse(sourceMapURL);
+	    if (!parsed) {
+	      throw new Error("sourceMapURL could not be parsed");
+	    }
+	    if (parsed.path) {
+	      // Strip the last path component, but keep the "/".
+	      var index = parsed.path.lastIndexOf('/');
+	      if (index >= 0) {
+	        parsed.path = parsed.path.substring(0, index + 1);
+	      }
+	    }
+	    sourceURL = join(urlGenerate(parsed), sourceURL);
+	  }
+
+	  return normalize(sourceURL);
+	}
+	exports.computeSourceURL = computeSourceURL;
+
+
+/***/ }),
 /* 5 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -1129,6 +1221,7 @@ return /******/ (function(modules) { // webpackBootstrap
 
 	var util = __webpack_require__(4);
 	var has = Object.prototype.hasOwnProperty;
+	var hasNativeMap = typeof Map !== "undefined";
 
 	/**
 	 * A data structure which is a combination of an array and a set. Adding a new
@@ -1138,7 +1231,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	 */
 	function ArraySet() {
 	  this._array = [];
-	  this._set = Object.create(null);
+	  this._set = hasNativeMap ? new Map() : Object.create(null);
 	}
 
 	/**
@@ -1159,7 +1252,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	 * @returns Number
 	 */
 	ArraySet.prototype.size = function ArraySet_size() {
-	  return Object.getOwnPropertyNames(this._set).length;
+	  return hasNativeMap ? this._set.size : Object.getOwnPropertyNames(this._set).length;
 	};
 
 	/**
@@ -1168,14 +1261,18 @@ return /******/ (function(modules) { // webpackBootstrap
 	 * @param String aStr
 	 */
 	ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) {
-	  var sStr = util.toSetString(aStr);
-	  var isDuplicate = has.call(this._set, sStr);
+	  var sStr = hasNativeMap ? aStr : util.toSetString(aStr);
+	  var isDuplicate = hasNativeMap ? this.has(aStr) : has.call(this._set, sStr);
 	  var idx = this._array.length;
 	  if (!isDuplicate || aAllowDuplicates) {
 	    this._array.push(aStr);
 	  }
 	  if (!isDuplicate) {
-	    this._set[sStr] = idx;
+	    if (hasNativeMap) {
+	      this._set.set(aStr, idx);
+	    } else {
+	      this._set[sStr] = idx;
+	    }
 	  }
 	};
 
@@ -1185,8 +1282,12 @@ return /******/ (function(modules) { // webpackBootstrap
 	 * @param String aStr
 	 */
 	ArraySet.prototype.has = function ArraySet_has(aStr) {
-	  var sStr = util.toSetString(aStr);
-	  return has.call(this._set, sStr);
+	  if (hasNativeMap) {
+	    return this._set.has(aStr);
+	  } else {
+	    var sStr = util.toSetString(aStr);
+	    return has.call(this._set, sStr);
+	  }
 	};
 
 	/**
@@ -1195,10 +1296,18 @@ return /******/ (function(modules) { // webpackBootstrap
 	 * @param String aStr
 	 */
 	ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) {
-	  var sStr = util.toSetString(aStr);
-	  if (has.call(this._set, sStr)) {
-	    return this._set[sStr];
+	  if (hasNativeMap) {
+	    var idx = this._set.get(aStr);
+	    if (idx >= 0) {
+	        return idx;
+	    }
+	  } else {
+	    var sStr = util.toSetString(aStr);
+	    if (has.call(this._set, sStr)) {
+	      return this._set[sStr];
+	    }
 	  }
+
 	  throw new Error('"' + aStr + '" is not in the set.');
 	};
 
@@ -1226,9 +1335,9 @@ return /******/ (function(modules) { // webpackBootstrap
 	exports.ArraySet = ArraySet;
 
 
-/***/ },
+/***/ }),
 /* 6 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -1311,9 +1420,9 @@ return /******/ (function(modules) { // webpackBootstrap
 	exports.MappingList = MappingList;
 
 
-/***/ },
+/***/ }),
 /* 7 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -1328,15 +1437,15 @@ return /******/ (function(modules) { // webpackBootstrap
 	var base64VLQ = __webpack_require__(2);
 	var quickSort = __webpack_require__(9).quickSort;
 
-	function SourceMapConsumer(aSourceMap) {
+	function SourceMapConsumer(aSourceMap, aSourceMapURL) {
 	  var sourceMap = aSourceMap;
 	  if (typeof aSourceMap === 'string') {
-	    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+	    sourceMap = util.parseSourceMapInput(aSourceMap);
 	  }
 
 	  return sourceMap.sections != null
-	    ? new IndexedSourceMapConsumer(sourceMap)
-	    : new BasicSourceMapConsumer(sourceMap);
+	    ? new IndexedSourceMapConsumer(sourceMap, aSourceMapURL)
+	    : new BasicSourceMapConsumer(sourceMap, aSourceMapURL);
 	}
 
 	SourceMapConsumer.fromSourceMap = function(aSourceMap) {
@@ -1380,6 +1489,8 @@ return /******/ (function(modules) { // webpackBootstrap
 
 	SourceMapConsumer.prototype.__generatedMappings = null;
 	Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', {
+	  configurable: true,
+	  enumerable: true,
 	  get: function () {
 	    if (!this.__generatedMappings) {
 	      this._parseMappings(this._mappings, this.sourceRoot);
@@ -1391,6 +1502,8 @@ return /******/ (function(modules) { // webpackBootstrap
 
 	SourceMapConsumer.prototype.__originalMappings = null;
 	Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', {
+	  configurable: true,
+	  enumerable: true,
 	  get: function () {
 	    if (!this.__originalMappings) {
 	      this._parseMappings(this._mappings, this.sourceRoot);
@@ -1458,9 +1571,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	    var sourceRoot = this.sourceRoot;
 	    mappings.map(function (mapping) {
 	      var source = mapping.source === null ? null : this._sources.at(mapping.source);
-	      if (source != null && sourceRoot != null) {
-	        source = util.join(sourceRoot, source);
-	      }
+	      source = util.computeSourceURL(sourceRoot, source, this._sourceMapURL);
 	      return {
 	        source: source,
 	        generatedLine: mapping.generatedLine,
@@ -1483,13 +1594,16 @@ return /******/ (function(modules) { // webpackBootstrap
 	 * The only argument is an object with the following properties:
 	 *
 	 *   - source: The filename of the original source.
-	 *   - line: The line number in the original source.
+	 *   - line: The line number in the original source.  The line number is 1-based.
 	 *   - column: Optional. the column number in the original source.
+	 *    The column number is 0-based.
 	 *
 	 * and an array of objects is returned, each with the following properties:
 	 *
-	 *   - line: The line number in the generated source, or null.
+	 *   - line: The line number in the generated source, or null.  The
+	 *    line number is 1-based.
 	 *   - column: The column number in the generated source, or null.
+	 *    The column number is 0-based.
 	 */
 	SourceMapConsumer.prototype.allGeneratedPositionsFor =
 	  function SourceMapConsumer_allGeneratedPositionsFor(aArgs) {
@@ -1571,7 +1685,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	 * query for information about the original file positions by giving it a file
 	 * position in the generated source.
 	 *
-	 * The only parameter is the raw source map (either as a JSON string, or
+	 * The first parameter is the raw source map (either as a JSON string, or
 	 * already parsed to an object). According to the spec, source maps have the
 	 * following attributes:
 	 *
@@ -1594,12 +1708,16 @@ return /******/ (function(modules) { // webpackBootstrap
 	 *       mappings: "AA,AB;;ABCDE;"
 	 *     }
 	 *
+	 * The second parameter, if given, is a string whose value is the URL
+	 * at which the source map was found.  This URL is used to compute the
+	 * sources array.
+	 *
 	 * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#
 	 */
-	function BasicSourceMapConsumer(aSourceMap) {
+	function BasicSourceMapConsumer(aSourceMap, aSourceMapURL) {
 	  var sourceMap = aSourceMap;
 	  if (typeof aSourceMap === 'string') {
-	    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+	    sourceMap = util.parseSourceMapInput(aSourceMap);
 	  }
 
 	  var version = util.getArg(sourceMap, 'version');
@@ -1618,6 +1736,10 @@ return /******/ (function(modules) { // webpackBootstrap
 	    throw new Error('Unsupported version: ' + version);
 	  }
 
+	  if (sourceRoot) {
+	    sourceRoot = util.normalize(sourceRoot);
+	  }
+
 	  sources = sources
 	    .map(String)
 	    // Some source maps produce relative source paths like "./foo.js" instead of
@@ -1644,6 +1766,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	  this.sourceRoot = sourceRoot;
 	  this.sourcesContent = sourcesContent;
 	  this._mappings = mappings;
+	  this._sourceMapURL = aSourceMapURL;
 	  this.file = file;
 	}
 
@@ -1655,10 +1778,12 @@ return /******/ (function(modules) { // webpackBootstrap
 	 *
 	 * @param SourceMapGenerator aSourceMap
 	 *        The source map that will be consumed.
+	 * @param String aSourceMapURL
+	 *        The URL at which the source map can be found (optional)
 	 * @returns BasicSourceMapConsumer
 	 */
 	BasicSourceMapConsumer.fromSourceMap =
-	  function SourceMapConsumer_fromSourceMap(aSourceMap) {
+	  function SourceMapConsumer_fromSourceMap(aSourceMap, aSourceMapURL) {
 	    var smc = Object.create(BasicSourceMapConsumer.prototype);
 
 	    var names = smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);
@@ -1667,6 +1792,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	    smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(),
 	                                                            smc.sourceRoot);
 	    smc.file = aSourceMap._file;
+	    smc._sourceMapURL = aSourceMapURL;
 
 	    // Because we are modifying the entries (by converting string sources and
 	    // names to indices into the sources and names ArraySets), we have to make
@@ -1714,7 +1840,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	Object.defineProperty(BasicSourceMapConsumer.prototype, 'sources', {
 	  get: function () {
 	    return this._sources.toArray().map(function (s) {
-	      return this.sourceRoot != null ? util.join(this.sourceRoot, s) : s;
+	      return util.computeSourceURL(this.sourceRoot, s, this._sourceMapURL);
 	    }, this);
 	  }
 	});
@@ -1896,8 +2022,10 @@ return /******/ (function(modules) { // webpackBootstrap
 	 * source's line and column positions provided. The only argument is an object
 	 * with the following properties:
 	 *
-	 *   - line: The line number in the generated source.
-	 *   - column: The column number in the generated source.
+	 *   - line: The line number in the generated source.  The line number
+	 *     is 1-based.
+	 *   - column: The column number in the generated source.  The column
+	 *     number is 0-based.
 	 *   - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or
 	 *     'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the
 	 *     closest element that is smaller than or greater than the one we are
@@ -1907,8 +2035,10 @@ return /******/ (function(modules) { // webpackBootstrap
 	 * and an object is returned with the following properties:
 	 *
 	 *   - source: The original source file, or null.
-	 *   - line: The line number in the original source, or null.
-	 *   - column: The column number in the original source, or null.
+	 *   - line: The line number in the original source, or null.  The
+	 *     line number is 1-based.
+	 *   - column: The column number in the original source, or null.  The
+	 *     column number is 0-based.
 	 *   - name: The original identifier, or null.
 	 */
 	BasicSourceMapConsumer.prototype.originalPositionFor =
@@ -1934,9 +2064,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	        var source = util.getArg(mapping, 'source', null);
 	        if (source !== null) {
 	          source = this._sources.at(source);
-	          if (this.sourceRoot != null) {
-	            source = util.join(this.sourceRoot, source);
-	          }
+	          source = util.computeSourceURL(this.sourceRoot, source, this._sourceMapURL);
 	        }
 	        var name = util.getArg(mapping, 'name', null);
 	        if (name !== null) {
@@ -1983,12 +2111,23 @@ return /******/ (function(modules) { // webpackBootstrap
 	      return null;
 	    }
 
+	    var relativeSource = aSource;
 	    if (this.sourceRoot != null) {
-	      aSource = util.relative(this.sourceRoot, aSource);
+	      relativeSource = util.relative(this.sourceRoot, relativeSource);
 	    }
 
-	    if (this._sources.has(aSource)) {
-	      return this.sourcesContent[this._sources.indexOf(aSource)];
+	    if (this._sources.has(relativeSource)) {
+	      return this.sourcesContent[this._sources.indexOf(relativeSource)];
+	    }
+
+	    // Maybe aSource is an absolute URL as returned by |sources|.  In
+	    // this case we can't simply undo the transform.
+	    var sourceArray = this.sources;
+	    var i;
+	    for (i = 0; i < sourceArray.length; ++i) {
+	      if (sourceArray[i] == aSource) {
+	        return this.sourcesContent[i];
+	      }
 	    }
 
 	    var url;
@@ -1998,15 +2137,15 @@ return /******/ (function(modules) { // webpackBootstrap
 	      // many users. We can help them out when they expect file:// URIs to
 	      // behave like it would if they were running a local HTTP server. See
 	      // https://bugzilla.mozilla.org/show_bug.cgi?id=885597.
-	      var fileUriAbsPath = aSource.replace(/^file:\/\//, "");
+	      var fileUriAbsPath = relativeSource.replace(/^file:\/\//, "");
 	      if (url.scheme == "file"
 	          && this._sources.has(fileUriAbsPath)) {
 	        return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)]
 	      }
 
 	      if ((!url.path || url.path == "/")
-	          && this._sources.has("/" + aSource)) {
-	        return this.sourcesContent[this._sources.indexOf("/" + aSource)];
+	          && this._sources.has("/" + relativeSource)) {
+	        return this.sourcesContent[this._sources.indexOf("/" + relativeSource)];
 	      }
 	    }
 
@@ -2018,7 +2157,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	      return null;
 	    }
 	    else {
-	      throw new Error('"' + aSource + '" is not in the SourceMap.');
+	      throw new Error('"' + relativeSource + '" is not in the SourceMap.');
 	    }
 	  };
 
@@ -2028,8 +2167,10 @@ return /******/ (function(modules) { // webpackBootstrap
 	 * the following properties:
 	 *
 	 *   - source: The filename of the original source.
-	 *   - line: The line number in the original source.
-	 *   - column: The column number in the original source.
+	 *   - line: The line number in the original source.  The line number
+	 *     is 1-based.
+	 *   - column: The column number in the original source.  The column
+	 *     number is 0-based.
 	 *   - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or
 	 *     'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the
 	 *     closest element that is smaller than or greater than the one we are
@@ -2038,8 +2179,10 @@ return /******/ (function(modules) { // webpackBootstrap
 	 *
 	 * and an object is returned with the following properties:
 	 *
-	 *   - line: The line number in the generated source, or null.
+	 *   - line: The line number in the generated source, or null.  The
+	 *     line number is 1-based.
 	 *   - column: The column number in the generated source, or null.
+	 *     The column number is 0-based.
 	 */
 	BasicSourceMapConsumer.prototype.generatedPositionFor =
 	  function SourceMapConsumer_generatedPositionFor(aArgs) {
@@ -2098,7 +2241,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	 * that it takes "indexed" source maps (i.e. ones with a "sections" field) as
 	 * input.
 	 *
-	 * The only parameter is a raw source map (either as a JSON string, or already
+	 * The first parameter is a raw source map (either as a JSON string, or already
 	 * parsed to an object). According to the spec for indexed source maps, they
 	 * have the following attributes:
 	 *
@@ -2135,12 +2278,16 @@ return /******/ (function(modules) { // webpackBootstrap
 	 *    }],
 	 *  }
 	 *
+	 * The second parameter, if given, is a string whose value is the URL
+	 * at which the source map was found.  This URL is used to compute the
+	 * sources array.
+	 *
 	 * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.535es3xeprgt
 	 */
-	function IndexedSourceMapConsumer(aSourceMap) {
+	function IndexedSourceMapConsumer(aSourceMap, aSourceMapURL) {
 	  var sourceMap = aSourceMap;
 	  if (typeof aSourceMap === 'string') {
-	    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+	    sourceMap = util.parseSourceMapInput(aSourceMap);
 	  }
 
 	  var version = util.getArg(sourceMap, 'version');
@@ -2180,7 +2327,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	        generatedLine: offsetLine + 1,
 	        generatedColumn: offsetColumn + 1
 	      },
-	      consumer: new SourceMapConsumer(util.getArg(s, 'map'))
+	      consumer: new SourceMapConsumer(util.getArg(s, 'map'), aSourceMapURL)
 	    }
 	  });
 	}
@@ -2213,14 +2360,18 @@ return /******/ (function(modules) { // webpackBootstrap
 	 * source's line and column positions provided. The only argument is an object
 	 * with the following properties:
 	 *
-	 *   - line: The line number in the generated source.
-	 *   - column: The column number in the generated source.
+	 *   - line: The line number in the generated source.  The line number
+	 *     is 1-based.
+	 *   - column: The column number in the generated source.  The column
+	 *     number is 0-based.
 	 *
 	 * and an object is returned with the following properties:
 	 *
 	 *   - source: The original source file, or null.
-	 *   - line: The line number in the original source, or null.
-	 *   - column: The column number in the original source, or null.
+	 *   - line: The line number in the original source, or null.  The
+	 *     line number is 1-based.
+	 *   - column: The column number in the original source, or null.  The
+	 *     column number is 0-based.
 	 *   - name: The original identifier, or null.
 	 */
 	IndexedSourceMapConsumer.prototype.originalPositionFor =
@@ -2304,13 +2455,17 @@ return /******/ (function(modules) { // webpackBootstrap
 	 * the following properties:
 	 *
 	 *   - source: The filename of the original source.
-	 *   - line: The line number in the original source.
-	 *   - column: The column number in the original source.
+	 *   - line: The line number in the original source.  The line number
+	 *     is 1-based.
+	 *   - column: The column number in the original source.  The column
+	 *     number is 0-based.
 	 *
 	 * and an object is returned with the following properties:
 	 *
-	 *   - line: The line number in the generated source, or null.
+	 *   - line: The line number in the generated source, or null.  The
+	 *     line number is 1-based. 
 	 *   - column: The column number in the generated source, or null.
+	 *     The column number is 0-based.
 	 */
 	IndexedSourceMapConsumer.prototype.generatedPositionFor =
 	  function IndexedSourceMapConsumer_generatedPositionFor(aArgs) {
@@ -2358,15 +2513,16 @@ return /******/ (function(modules) { // webpackBootstrap
 	        var mapping = sectionMappings[j];
 
 	        var source = section.consumer._sources.at(mapping.source);
-	        if (section.consumer.sourceRoot !== null) {
-	          source = util.join(section.consumer.sourceRoot, source);
-	        }
+	        source = util.computeSourceURL(section.consumer.sourceRoot, source, this._sourceMapURL);
 	        this._sources.add(source);
 	        source = this._sources.indexOf(source);
 
-	        var name = section.consumer._names.at(mapping.name);
-	        this._names.add(name);
-	        name = this._names.indexOf(name);
+	        var name = null;
+	        if (mapping.name) {
+	          name = section.consumer._names.at(mapping.name);
+	          this._names.add(name);
+	          name = this._names.indexOf(name);
+	        }
 
 	        // The mappings coming from the consumer for the section have
 	        // generated positions relative to the start of the section, so we
@@ -2399,9 +2555,9 @@ return /******/ (function(modules) { // webpackBootstrap
 	exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer;
 
 
-/***/ },
+/***/ }),
 /* 8 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -2516,9 +2672,9 @@ return /******/ (function(modules) { // webpackBootstrap
 	};
 
 
-/***/ },
+/***/ }),
 /* 9 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -2636,9 +2792,9 @@ return /******/ (function(modules) { // webpackBootstrap
 	};
 
 
-/***/ },
+/***/ }),
 /* 10 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -2702,13 +2858,19 @@ return /******/ (function(modules) { // webpackBootstrap
 	    // 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`.
+	    // Processed fragments are accessed by calling `shiftNextLine`.
 	    var remainingLines = aGeneratedCode.split(REGEX_NEWLINE);
+	    var remainingLinesIndex = 0;
 	    var shiftNextLine = function() {
-	      var lineContents = remainingLines.shift();
+	      var lineContents = getNextLine();
 	      // The last line of a file might not have a newline.
-	      var newLine = remainingLines.shift() || "";
+	      var newLine = getNextLine() || "";
 	      return lineContents + newLine;
+
+	      function getNextLine() {
+	        return remainingLinesIndex < remainingLines.length ?
+	            remainingLines[remainingLinesIndex++] : undefined;
+	      }
 	    };
 
 	    // We need to remember the position of "remainingLines"
@@ -2733,10 +2895,10 @@ return /******/ (function(modules) { // webpackBootstrap
 	          // There is no new line in between.
 	          // Associate the code between "lastGeneratedColumn" and
 	          // "mapping.generatedColumn" with "lastMapping"
-	          var nextLine = remainingLines[0];
+	          var nextLine = remainingLines[remainingLinesIndex] || '';
 	          var code = nextLine.substr(0, mapping.generatedColumn -
 	                                        lastGeneratedColumn);
-	          remainingLines[0] = nextLine.substr(mapping.generatedColumn -
+	          remainingLines[remainingLinesIndex] = nextLine.substr(mapping.generatedColumn -
 	                                              lastGeneratedColumn);
 	          lastGeneratedColumn = mapping.generatedColumn;
 	          addMappingWithCode(lastMapping, code);
@@ -2753,21 +2915,21 @@ return /******/ (function(modules) { // webpackBootstrap
 	        lastGeneratedLine++;
 	      }
 	      if (lastGeneratedColumn < mapping.generatedColumn) {
-	        var nextLine = remainingLines[0];
+	        var nextLine = remainingLines[remainingLinesIndex] || '';
 	        node.add(nextLine.substr(0, mapping.generatedColumn));
-	        remainingLines[0] = nextLine.substr(mapping.generatedColumn);
+	        remainingLines[remainingLinesIndex] = nextLine.substr(mapping.generatedColumn);
 	        lastGeneratedColumn = mapping.generatedColumn;
 	      }
 	      lastMapping = mapping;
 	    }, this);
 	    // We have processed all mappings.
-	    if (remainingLines.length > 0) {
+	    if (remainingLinesIndex < remainingLines.length) {
 	      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(""));
+	      node.add(remainingLines.splice(remainingLinesIndex).join(""));
 	    }
 
 	    // Copy sourcesContent into SourceNode
@@ -3049,7 +3211,7 @@ return /******/ (function(modules) { // webpackBootstrap
 	exports.SourceNode = SourceNode;
 
 
-/***/ }
+/***/ })
 /******/ ])
 });
 ;
\ No newline at end of file
diff --git a/dist/source-map.min.js b/dist/source-map.min.js
index e03b6d5..2150560 100644
--- a/dist/source-map.min.js
+++ b/dist/source-map.min.js
@@ -1,2 +1,2 @@
-!function(e,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?exports.sourceMap=n():e.sourceMap=n()}(this,function(){return function(e){function n(t){if(r[t])return r[t].exports;var o=r[t]={exports:{},id:t,loaded:!1};return e[t].call(o.exports,o,o.exports,n),o.loaded=!0,o.exports}var r={};return n.m=e,n.c=r,n.p="",n(0)}([function(e,n,r){n.SourceMapGenerator=r(1).SourceMapGenerator,n.SourceMa [...]
+!function(e,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?exports.sourceMap=n():e.sourceMap=n()}(this,function(){return function(e){function n(t){if(r[t])return r[t].exports;var o=r[t]={exports:{},id:t,loaded:!1};return e[t].call(o.exports,o,o.exports,n),o.loaded=!0,o.exports}var r={};return n.m=e,n.c=r,n.p="",n(0)}([function(e,n,r){n.SourceMapGenerator=r(1).SourceMapGenerator,n.SourceMa [...]
 //# sourceMappingURL=source-map.min.js.map
\ No newline at end of file
diff --git a/dist/source-map.min.js.map b/dist/source-map.min.js.map
index 3275a32..57477f7 100644
--- a/dist/source-map.min.js.map
+++ b/dist/source-map.min.js.map
@@ -1 +1 @@
-{"version":3,"sources":["webpack:///webpack/universalModuleDefinition","webpack:///source-map.min.js","webpack:///webpack/bootstrap 5d9a9f4df3bd553ed2f9","webpack:///./source-map.js","webpack:///./lib/source-map-generator.js","webpack:///./lib/base64-vlq.js","webpack:///./lib/base64.js","webpack:///./lib/util.js","webpack:///./lib/array-set.js","webpack:///./lib/mapping-list.js","webpack:///./lib/source-map-consumer.js","webpack:///./lib/binary-search.js","webpack:///./lib/quick-sort.js" [...]
\ No newline at end of file
+{"version":3,"sources":["webpack:///webpack/universalModuleDefinition","webpack:///source-map.min.js","webpack:///webpack/bootstrap 3093113f7ca9ad8efffd","webpack:///./source-map.js","webpack:///./lib/source-map-generator.js","webpack:///./lib/base64-vlq.js","webpack:///./lib/base64.js","webpack:///./lib/util.js","webpack:///./lib/array-set.js","webpack:///./lib/mapping-list.js","webpack:///./lib/source-map-consumer.js","webpack:///./lib/binary-search.js","webpack:///./lib/quick-sort.js" [...]
\ No newline at end of file
diff --git a/dist/test/test_api.js b/dist/test/test_api.js
index 27491f5..68bda9b 100644
--- a/dist/test/test_api.js
+++ b/dist/test/test_api.js
@@ -52,7 +52,7 @@ var SOURCE_MAP_TEST_MODULE =
 /************************************************************************/
 /******/ ([
 /* 0 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -70,9 +70,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 1 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/*
 	 * Copyright 2009-2011 Mozilla Foundation and contributors
@@ -84,9 +84,9 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.SourceNode = __webpack_require__(11).SourceNode;
 
 
-/***/ },
+/***/ }),
 /* 2 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -162,6 +162,15 @@ var SOURCE_MAP_TEST_MODULE =
 	      generator.addMapping(newMapping);
 	    });
 	    aSourceMapConsumer.sources.forEach(function (sourceFile) {
+	      var sourceRelative = sourceFile;
+	      if (sourceRoot !== null) {
+	        sourceRelative = util.relative(sourceRoot, sourceFile);
+	      }
+	
+	      if (!generator._sources.has(sourceRelative)) {
+	        generator._sources.add(sourceRelative);
+	      }
+	
 	      var content = aSourceMapConsumer.sourceContentFor(sourceFile);
 	      if (content != null) {
 	        generator.setSourceContent(sourceFile, content);
@@ -349,6 +358,18 @@ var SOURCE_MAP_TEST_MODULE =
 	SourceMapGenerator.prototype._validateMapping =
 	  function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource,
 	                                              aName) {
+	    // When aOriginal is truthy but has empty values for .line and .column,
+	    // it is most likely a programmer error. In this case we throw a very
+	    // specific error message to try to guide them the right way.
+	    // For example: https://github.com/Polymer/polymer-bundler/pull/519
+	    if (aOriginal && typeof aOriginal.line !== 'number' && typeof aOriginal.column !== 'number') {
+	        throw new Error(
+	            'original.line and original.column are not numbers -- you probably meant to omit ' +
+	            'the original mapping entirely and only map the generated position. If so, pass ' +
+	            'null for the original mapping instead of an object with empty or null values.'
+	        );
+	    }
+	
 	    if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
 	        && aGenerated.line > 0 && aGenerated.column >= 0
 	        && !aOriginal && !aSource && !aName) {
@@ -494,9 +515,9 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.SourceMapGenerator = SourceMapGenerator;
 
 
-/***/ },
+/***/ }),
 /* 3 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -640,9 +661,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 4 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -713,9 +734,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 5 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -745,7 +766,7 @@ var SOURCE_MAP_TEST_MODULE =
 	}
 	exports.getArg = getArg;
 	
-	var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/;
+	var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/;
 	var dataUrlRegexp = /^data:.+\,.+$/;
 	
 	function urlParse(aUrl) {
@@ -788,7 +809,7 @@ var SOURCE_MAP_TEST_MODULE =
 	/**
 	 * Normalizes a path, or the path portion of a URL:
 	 *
-	 * - Replaces consequtive slashes with one slash.
+	 * - Replaces consecutive slashes with one slash.
 	 * - Removes unnecessary '.' parts.
 	 * - Removes unnecessary '<dir>/..' parts.
 	 *
@@ -901,7 +922,7 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.join = join;
 	
 	exports.isAbsolute = function (aPath) {
-	  return aPath.charAt(0) === '/' || !!aPath.match(urlRegexp);
+	  return aPath.charAt(0) === '/' || urlRegexp.test(aPath);
 	};
 	
 	/**
@@ -1021,7 +1042,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 * stubbed out mapping.
 	 */
 	function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
-	  var cmp = mappingA.source - mappingB.source;
+	  var cmp = strcmp(mappingA.source, mappingB.source);
 	  if (cmp !== 0) {
 	    return cmp;
 	  }
@@ -1046,7 +1067,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    return cmp;
 	  }
 	
-	  return mappingA.name - mappingB.name;
+	  return strcmp(mappingA.name, mappingB.name);
 	}
 	exports.compareByOriginalPositions = compareByOriginalPositions;
 	
@@ -1070,7 +1091,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    return cmp;
 	  }
 	
-	  cmp = mappingA.source - mappingB.source;
+	  cmp = strcmp(mappingA.source, mappingB.source);
 	  if (cmp !== 0) {
 	    return cmp;
 	  }
@@ -1085,7 +1106,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    return cmp;
 	  }
 	
-	  return mappingA.name - mappingB.name;
+	  return strcmp(mappingA.name, mappingB.name);
 	}
 	exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated;
 	
@@ -1094,6 +1115,14 @@ var SOURCE_MAP_TEST_MODULE =
 	    return 0;
 	  }
 	
+	  if (aStr1 === null) {
+	    return 1; // aStr2 !== null
+	  }
+	
+	  if (aStr2 === null) {
+	    return -1; // aStr1 !== null
+	  }
+	
 	  if (aStr1 > aStr2) {
 	    return 1;
 	  }
@@ -1134,11 +1163,74 @@ var SOURCE_MAP_TEST_MODULE =
 	  return strcmp(mappingA.name, mappingB.name);
 	}
 	exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated;
+	
+	/**
+	 * Strip any JSON XSSI avoidance prefix from the string (as documented
+	 * in the source maps specification), and then parse the string as
+	 * JSON.
+	 */
+	function parseSourceMapInput(str) {
+	  return JSON.parse(str.replace(/^\)]}'[^\n]*\n/, ''));
+	}
+	exports.parseSourceMapInput = parseSourceMapInput;
+	
+	/**
+	 * Compute the URL of a source given the the source root, the source's
+	 * URL, and the source map's URL.
+	 */
+	function computeSourceURL(sourceRoot, sourceURL, sourceMapURL) {
+	  sourceURL = sourceURL || '';
+	
+	  if (sourceRoot) {
+	    // This follows what Chrome does.
+	    if (sourceRoot[sourceRoot.length - 1] !== '/' && sourceURL[0] !== '/') {
+	      sourceRoot += '/';
+	    }
+	    // The spec says:
+	    //   Line 4: An optional source root, useful for relocating source
+	    //   files on a server or removing repeated values in the
+	    //   “sources” entry.  This value is prepended to the individual
+	    //   entries in the “source” field.
+	    sourceURL = sourceRoot + sourceURL;
+	  }
+	
+	  // Historically, SourceMapConsumer did not take the sourceMapURL as
+	  // a parameter.  This mode is still somewhat supported, which is why
+	  // this code block is conditional.  However, it's preferable to pass
+	  // the source map URL to SourceMapConsumer, so that this function
+	  // can implement the source URL resolution algorithm as outlined in
+	  // the spec.  This block is basically the equivalent of:
+	  //    new URL(sourceURL, sourceMapURL).toString()
+	  // ... except it avoids using URL, which wasn't available in the
+	  // older releases of node still supported by this library.
+	  //
+	  // The spec says:
+	  //   If the sources are not absolute URLs after prepending of the
+	  //   “sourceRoot”, the sources are resolved relative to the
+	  //   SourceMap (like resolving script src in a html document).
+	  if (sourceMapURL) {
+	    var parsed = urlParse(sourceMapURL);
+	    if (!parsed) {
+	      throw new Error("sourceMapURL could not be parsed");
+	    }
+	    if (parsed.path) {
+	      // Strip the last path component, but keep the "/".
+	      var index = parsed.path.lastIndexOf('/');
+	      if (index >= 0) {
+	        parsed.path = parsed.path.substring(0, index + 1);
+	      }
+	    }
+	    sourceURL = join(urlGenerate(parsed), sourceURL);
+	  }
+	
+	  return normalize(sourceURL);
+	}
+	exports.computeSourceURL = computeSourceURL;
 
 
-/***/ },
+/***/ }),
 /* 6 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -1149,6 +1241,7 @@ var SOURCE_MAP_TEST_MODULE =
 	
 	var util = __webpack_require__(5);
 	var has = Object.prototype.hasOwnProperty;
+	var hasNativeMap = typeof Map !== "undefined";
 	
 	/**
 	 * A data structure which is a combination of an array and a set. Adding a new
@@ -1158,7 +1251,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 */
 	function ArraySet() {
 	  this._array = [];
-	  this._set = Object.create(null);
+	  this._set = hasNativeMap ? new Map() : Object.create(null);
 	}
 	
 	/**
@@ -1179,7 +1272,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 * @returns Number
 	 */
 	ArraySet.prototype.size = function ArraySet_size() {
-	  return Object.getOwnPropertyNames(this._set).length;
+	  return hasNativeMap ? this._set.size : Object.getOwnPropertyNames(this._set).length;
 	};
 	
 	/**
@@ -1188,14 +1281,18 @@ var SOURCE_MAP_TEST_MODULE =
 	 * @param String aStr
 	 */
 	ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) {
-	  var sStr = util.toSetString(aStr);
-	  var isDuplicate = has.call(this._set, sStr);
+	  var sStr = hasNativeMap ? aStr : util.toSetString(aStr);
+	  var isDuplicate = hasNativeMap ? this.has(aStr) : has.call(this._set, sStr);
 	  var idx = this._array.length;
 	  if (!isDuplicate || aAllowDuplicates) {
 	    this._array.push(aStr);
 	  }
 	  if (!isDuplicate) {
-	    this._set[sStr] = idx;
+	    if (hasNativeMap) {
+	      this._set.set(aStr, idx);
+	    } else {
+	      this._set[sStr] = idx;
+	    }
 	  }
 	};
 	
@@ -1205,8 +1302,12 @@ var SOURCE_MAP_TEST_MODULE =
 	 * @param String aStr
 	 */
 	ArraySet.prototype.has = function ArraySet_has(aStr) {
-	  var sStr = util.toSetString(aStr);
-	  return has.call(this._set, sStr);
+	  if (hasNativeMap) {
+	    return this._set.has(aStr);
+	  } else {
+	    var sStr = util.toSetString(aStr);
+	    return has.call(this._set, sStr);
+	  }
 	};
 	
 	/**
@@ -1215,10 +1316,18 @@ var SOURCE_MAP_TEST_MODULE =
 	 * @param String aStr
 	 */
 	ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) {
-	  var sStr = util.toSetString(aStr);
-	  if (has.call(this._set, sStr)) {
-	    return this._set[sStr];
+	  if (hasNativeMap) {
+	    var idx = this._set.get(aStr);
+	    if (idx >= 0) {
+	        return idx;
+	    }
+	  } else {
+	    var sStr = util.toSetString(aStr);
+	    if (has.call(this._set, sStr)) {
+	      return this._set[sStr];
+	    }
 	  }
+	
 	  throw new Error('"' + aStr + '" is not in the set.');
 	};
 	
@@ -1246,9 +1355,9 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.ArraySet = ArraySet;
 
 
-/***/ },
+/***/ }),
 /* 7 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -1331,9 +1440,9 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.MappingList = MappingList;
 
 
-/***/ },
+/***/ }),
 /* 8 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -1348,15 +1457,15 @@ var SOURCE_MAP_TEST_MODULE =
 	var base64VLQ = __webpack_require__(3);
 	var quickSort = __webpack_require__(10).quickSort;
 	
-	function SourceMapConsumer(aSourceMap) {
+	function SourceMapConsumer(aSourceMap, aSourceMapURL) {
 	  var sourceMap = aSourceMap;
 	  if (typeof aSourceMap === 'string') {
-	    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+	    sourceMap = util.parseSourceMapInput(aSourceMap);
 	  }
 	
 	  return sourceMap.sections != null
-	    ? new IndexedSourceMapConsumer(sourceMap)
-	    : new BasicSourceMapConsumer(sourceMap);
+	    ? new IndexedSourceMapConsumer(sourceMap, aSourceMapURL)
+	    : new BasicSourceMapConsumer(sourceMap, aSourceMapURL);
 	}
 	
 	SourceMapConsumer.fromSourceMap = function(aSourceMap) {
@@ -1400,6 +1509,8 @@ var SOURCE_MAP_TEST_MODULE =
 	
 	SourceMapConsumer.prototype.__generatedMappings = null;
 	Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', {
+	  configurable: true,
+	  enumerable: true,
 	  get: function () {
 	    if (!this.__generatedMappings) {
 	      this._parseMappings(this._mappings, this.sourceRoot);
@@ -1411,6 +1522,8 @@ var SOURCE_MAP_TEST_MODULE =
 	
 	SourceMapConsumer.prototype.__originalMappings = null;
 	Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', {
+	  configurable: true,
+	  enumerable: true,
 	  get: function () {
 	    if (!this.__originalMappings) {
 	      this._parseMappings(this._mappings, this.sourceRoot);
@@ -1478,9 +1591,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    var sourceRoot = this.sourceRoot;
 	    mappings.map(function (mapping) {
 	      var source = mapping.source === null ? null : this._sources.at(mapping.source);
-	      if (source != null && sourceRoot != null) {
-	        source = util.join(sourceRoot, source);
-	      }
+	      source = util.computeSourceURL(sourceRoot, source, this._sourceMapURL);
 	      return {
 	        source: source,
 	        generatedLine: mapping.generatedLine,
@@ -1503,13 +1614,16 @@ var SOURCE_MAP_TEST_MODULE =
 	 * The only argument is an object with the following properties:
 	 *
 	 *   - source: The filename of the original source.
-	 *   - line: The line number in the original source.
+	 *   - line: The line number in the original source.  The line number is 1-based.
 	 *   - column: Optional. the column number in the original source.
+	 *    The column number is 0-based.
 	 *
 	 * and an array of objects is returned, each with the following properties:
 	 *
-	 *   - line: The line number in the generated source, or null.
+	 *   - line: The line number in the generated source, or null.  The
+	 *    line number is 1-based.
 	 *   - column: The column number in the generated source, or null.
+	 *    The column number is 0-based.
 	 */
 	SourceMapConsumer.prototype.allGeneratedPositionsFor =
 	  function SourceMapConsumer_allGeneratedPositionsFor(aArgs) {
@@ -1591,7 +1705,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 * query for information about the original file positions by giving it a file
 	 * position in the generated source.
 	 *
-	 * The only parameter is the raw source map (either as a JSON string, or
+	 * The first parameter is the raw source map (either as a JSON string, or
 	 * already parsed to an object). According to the spec, source maps have the
 	 * following attributes:
 	 *
@@ -1614,12 +1728,16 @@ var SOURCE_MAP_TEST_MODULE =
 	 *       mappings: "AA,AB;;ABCDE;"
 	 *     }
 	 *
+	 * The second parameter, if given, is a string whose value is the URL
+	 * at which the source map was found.  This URL is used to compute the
+	 * sources array.
+	 *
 	 * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#
 	 */
-	function BasicSourceMapConsumer(aSourceMap) {
+	function BasicSourceMapConsumer(aSourceMap, aSourceMapURL) {
 	  var sourceMap = aSourceMap;
 	  if (typeof aSourceMap === 'string') {
-	    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+	    sourceMap = util.parseSourceMapInput(aSourceMap);
 	  }
 	
 	  var version = util.getArg(sourceMap, 'version');
@@ -1638,6 +1756,10 @@ var SOURCE_MAP_TEST_MODULE =
 	    throw new Error('Unsupported version: ' + version);
 	  }
 	
+	  if (sourceRoot) {
+	    sourceRoot = util.normalize(sourceRoot);
+	  }
+	
 	  sources = sources
 	    .map(String)
 	    // Some source maps produce relative source paths like "./foo.js" instead of
@@ -1664,6 +1786,7 @@ var SOURCE_MAP_TEST_MODULE =
 	  this.sourceRoot = sourceRoot;
 	  this.sourcesContent = sourcesContent;
 	  this._mappings = mappings;
+	  this._sourceMapURL = aSourceMapURL;
 	  this.file = file;
 	}
 	
@@ -1675,10 +1798,12 @@ var SOURCE_MAP_TEST_MODULE =
 	 *
 	 * @param SourceMapGenerator aSourceMap
 	 *        The source map that will be consumed.
+	 * @param String aSourceMapURL
+	 *        The URL at which the source map can be found (optional)
 	 * @returns BasicSourceMapConsumer
 	 */
 	BasicSourceMapConsumer.fromSourceMap =
-	  function SourceMapConsumer_fromSourceMap(aSourceMap) {
+	  function SourceMapConsumer_fromSourceMap(aSourceMap, aSourceMapURL) {
 	    var smc = Object.create(BasicSourceMapConsumer.prototype);
 	
 	    var names = smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);
@@ -1687,6 +1812,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(),
 	                                                            smc.sourceRoot);
 	    smc.file = aSourceMap._file;
+	    smc._sourceMapURL = aSourceMapURL;
 	
 	    // Because we are modifying the entries (by converting string sources and
 	    // names to indices into the sources and names ArraySets), we have to make
@@ -1734,7 +1860,7 @@ var SOURCE_MAP_TEST_MODULE =
 	Object.defineProperty(BasicSourceMapConsumer.prototype, 'sources', {
 	  get: function () {
 	    return this._sources.toArray().map(function (s) {
-	      return this.sourceRoot != null ? util.join(this.sourceRoot, s) : s;
+	      return util.computeSourceURL(this.sourceRoot, s, this._sourceMapURL);
 	    }, this);
 	  }
 	});
@@ -1916,8 +2042,10 @@ var SOURCE_MAP_TEST_MODULE =
 	 * source's line and column positions provided. The only argument is an object
 	 * with the following properties:
 	 *
-	 *   - line: The line number in the generated source.
-	 *   - column: The column number in the generated source.
+	 *   - line: The line number in the generated source.  The line number
+	 *     is 1-based.
+	 *   - column: The column number in the generated source.  The column
+	 *     number is 0-based.
 	 *   - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or
 	 *     'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the
 	 *     closest element that is smaller than or greater than the one we are
@@ -1927,8 +2055,10 @@ var SOURCE_MAP_TEST_MODULE =
 	 * and an object is returned with the following properties:
 	 *
 	 *   - source: The original source file, or null.
-	 *   - line: The line number in the original source, or null.
-	 *   - column: The column number in the original source, or null.
+	 *   - line: The line number in the original source, or null.  The
+	 *     line number is 1-based.
+	 *   - column: The column number in the original source, or null.  The
+	 *     column number is 0-based.
 	 *   - name: The original identifier, or null.
 	 */
 	BasicSourceMapConsumer.prototype.originalPositionFor =
@@ -1954,9 +2084,7 @@ var SOURCE_MAP_TEST_MODULE =
 	        var source = util.getArg(mapping, 'source', null);
 	        if (source !== null) {
 	          source = this._sources.at(source);
-	          if (this.sourceRoot != null) {
-	            source = util.join(this.sourceRoot, source);
-	          }
+	          source = util.computeSourceURL(this.sourceRoot, source, this._sourceMapURL);
 	        }
 	        var name = util.getArg(mapping, 'name', null);
 	        if (name !== null) {
@@ -2003,12 +2131,23 @@ var SOURCE_MAP_TEST_MODULE =
 	      return null;
 	    }
 	
+	    var relativeSource = aSource;
 	    if (this.sourceRoot != null) {
-	      aSource = util.relative(this.sourceRoot, aSource);
+	      relativeSource = util.relative(this.sourceRoot, relativeSource);
 	    }
 	
-	    if (this._sources.has(aSource)) {
-	      return this.sourcesContent[this._sources.indexOf(aSource)];
+	    if (this._sources.has(relativeSource)) {
+	      return this.sourcesContent[this._sources.indexOf(relativeSource)];
+	    }
+	
+	    // Maybe aSource is an absolute URL as returned by |sources|.  In
+	    // this case we can't simply undo the transform.
+	    var sourceArray = this.sources;
+	    var i;
+	    for (i = 0; i < sourceArray.length; ++i) {
+	      if (sourceArray[i] == aSource) {
+	        return this.sourcesContent[i];
+	      }
 	    }
 	
 	    var url;
@@ -2018,15 +2157,15 @@ var SOURCE_MAP_TEST_MODULE =
 	      // many users. We can help them out when they expect file:// URIs to
 	      // behave like it would if they were running a local HTTP server. See
 	      // https://bugzilla.mozilla.org/show_bug.cgi?id=885597.
-	      var fileUriAbsPath = aSource.replace(/^file:\/\//, "");
+	      var fileUriAbsPath = relativeSource.replace(/^file:\/\//, "");
 	      if (url.scheme == "file"
 	          && this._sources.has(fileUriAbsPath)) {
 	        return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)]
 	      }
 	
 	      if ((!url.path || url.path == "/")
-	          && this._sources.has("/" + aSource)) {
-	        return this.sourcesContent[this._sources.indexOf("/" + aSource)];
+	          && this._sources.has("/" + relativeSource)) {
+	        return this.sourcesContent[this._sources.indexOf("/" + relativeSource)];
 	      }
 	    }
 	
@@ -2038,7 +2177,7 @@ var SOURCE_MAP_TEST_MODULE =
 	      return null;
 	    }
 	    else {
-	      throw new Error('"' + aSource + '" is not in the SourceMap.');
+	      throw new Error('"' + relativeSource + '" is not in the SourceMap.');
 	    }
 	  };
 	
@@ -2048,8 +2187,10 @@ var SOURCE_MAP_TEST_MODULE =
 	 * the following properties:
 	 *
 	 *   - source: The filename of the original source.
-	 *   - line: The line number in the original source.
-	 *   - column: The column number in the original source.
+	 *   - line: The line number in the original source.  The line number
+	 *     is 1-based.
+	 *   - column: The column number in the original source.  The column
+	 *     number is 0-based.
 	 *   - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or
 	 *     'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the
 	 *     closest element that is smaller than or greater than the one we are
@@ -2058,8 +2199,10 @@ var SOURCE_MAP_TEST_MODULE =
 	 *
 	 * and an object is returned with the following properties:
 	 *
-	 *   - line: The line number in the generated source, or null.
+	 *   - line: The line number in the generated source, or null.  The
+	 *     line number is 1-based.
 	 *   - column: The column number in the generated source, or null.
+	 *     The column number is 0-based.
 	 */
 	BasicSourceMapConsumer.prototype.generatedPositionFor =
 	  function SourceMapConsumer_generatedPositionFor(aArgs) {
@@ -2118,7 +2261,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 * that it takes "indexed" source maps (i.e. ones with a "sections" field) as
 	 * input.
 	 *
-	 * The only parameter is a raw source map (either as a JSON string, or already
+	 * The first parameter is a raw source map (either as a JSON string, or already
 	 * parsed to an object). According to the spec for indexed source maps, they
 	 * have the following attributes:
 	 *
@@ -2155,12 +2298,16 @@ var SOURCE_MAP_TEST_MODULE =
 	 *    }],
 	 *  }
 	 *
+	 * The second parameter, if given, is a string whose value is the URL
+	 * at which the source map was found.  This URL is used to compute the
+	 * sources array.
+	 *
 	 * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.535es3xeprgt
 	 */
-	function IndexedSourceMapConsumer(aSourceMap) {
+	function IndexedSourceMapConsumer(aSourceMap, aSourceMapURL) {
 	  var sourceMap = aSourceMap;
 	  if (typeof aSourceMap === 'string') {
-	    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+	    sourceMap = util.parseSourceMapInput(aSourceMap);
 	  }
 	
 	  var version = util.getArg(sourceMap, 'version');
@@ -2200,7 +2347,7 @@ var SOURCE_MAP_TEST_MODULE =
 	        generatedLine: offsetLine + 1,
 	        generatedColumn: offsetColumn + 1
 	      },
-	      consumer: new SourceMapConsumer(util.getArg(s, 'map'))
+	      consumer: new SourceMapConsumer(util.getArg(s, 'map'), aSourceMapURL)
 	    }
 	  });
 	}
@@ -2233,14 +2380,18 @@ var SOURCE_MAP_TEST_MODULE =
 	 * source's line and column positions provided. The only argument is an object
 	 * with the following properties:
 	 *
-	 *   - line: The line number in the generated source.
-	 *   - column: The column number in the generated source.
+	 *   - line: The line number in the generated source.  The line number
+	 *     is 1-based.
+	 *   - column: The column number in the generated source.  The column
+	 *     number is 0-based.
 	 *
 	 * and an object is returned with the following properties:
 	 *
 	 *   - source: The original source file, or null.
-	 *   - line: The line number in the original source, or null.
-	 *   - column: The column number in the original source, or null.
+	 *   - line: The line number in the original source, or null.  The
+	 *     line number is 1-based.
+	 *   - column: The column number in the original source, or null.  The
+	 *     column number is 0-based.
 	 *   - name: The original identifier, or null.
 	 */
 	IndexedSourceMapConsumer.prototype.originalPositionFor =
@@ -2324,13 +2475,17 @@ var SOURCE_MAP_TEST_MODULE =
 	 * the following properties:
 	 *
 	 *   - source: The filename of the original source.
-	 *   - line: The line number in the original source.
-	 *   - column: The column number in the original source.
+	 *   - line: The line number in the original source.  The line number
+	 *     is 1-based.
+	 *   - column: The column number in the original source.  The column
+	 *     number is 0-based.
 	 *
 	 * and an object is returned with the following properties:
 	 *
-	 *   - line: The line number in the generated source, or null.
+	 *   - line: The line number in the generated source, or null.  The
+	 *     line number is 1-based. 
 	 *   - column: The column number in the generated source, or null.
+	 *     The column number is 0-based.
 	 */
 	IndexedSourceMapConsumer.prototype.generatedPositionFor =
 	  function IndexedSourceMapConsumer_generatedPositionFor(aArgs) {
@@ -2378,15 +2533,16 @@ var SOURCE_MAP_TEST_MODULE =
 	        var mapping = sectionMappings[j];
 	
 	        var source = section.consumer._sources.at(mapping.source);
-	        if (section.consumer.sourceRoot !== null) {
-	          source = util.join(section.consumer.sourceRoot, source);
-	        }
+	        source = util.computeSourceURL(section.consumer.sourceRoot, source, this._sourceMapURL);
 	        this._sources.add(source);
 	        source = this._sources.indexOf(source);
 	
-	        var name = section.consumer._names.at(mapping.name);
-	        this._names.add(name);
-	        name = this._names.indexOf(name);
+	        var name = null;
+	        if (mapping.name) {
+	          name = section.consumer._names.at(mapping.name);
+	          this._names.add(name);
+	          name = this._names.indexOf(name);
+	        }
 	
 	        // The mappings coming from the consumer for the section have
 	        // generated positions relative to the start of the section, so we
@@ -2419,9 +2575,9 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer;
 
 
-/***/ },
+/***/ }),
 /* 9 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -2536,9 +2692,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 10 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -2656,9 +2812,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 11 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -2722,13 +2878,19 @@ var SOURCE_MAP_TEST_MODULE =
 	    // 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`.
+	    // Processed fragments are accessed by calling `shiftNextLine`.
 	    var remainingLines = aGeneratedCode.split(REGEX_NEWLINE);
+	    var remainingLinesIndex = 0;
 	    var shiftNextLine = function() {
-	      var lineContents = remainingLines.shift();
+	      var lineContents = getNextLine();
 	      // The last line of a file might not have a newline.
-	      var newLine = remainingLines.shift() || "";
+	      var newLine = getNextLine() || "";
 	      return lineContents + newLine;
+	
+	      function getNextLine() {
+	        return remainingLinesIndex < remainingLines.length ?
+	            remainingLines[remainingLinesIndex++] : undefined;
+	      }
 	    };
 	
 	    // We need to remember the position of "remainingLines"
@@ -2753,10 +2915,10 @@ var SOURCE_MAP_TEST_MODULE =
 	          // There is no new line in between.
 	          // Associate the code between "lastGeneratedColumn" and
 	          // "mapping.generatedColumn" with "lastMapping"
-	          var nextLine = remainingLines[0];
+	          var nextLine = remainingLines[remainingLinesIndex] || '';
 	          var code = nextLine.substr(0, mapping.generatedColumn -
 	                                        lastGeneratedColumn);
-	          remainingLines[0] = nextLine.substr(mapping.generatedColumn -
+	          remainingLines[remainingLinesIndex] = nextLine.substr(mapping.generatedColumn -
 	                                              lastGeneratedColumn);
 	          lastGeneratedColumn = mapping.generatedColumn;
 	          addMappingWithCode(lastMapping, code);
@@ -2773,21 +2935,21 @@ var SOURCE_MAP_TEST_MODULE =
 	        lastGeneratedLine++;
 	      }
 	      if (lastGeneratedColumn < mapping.generatedColumn) {
-	        var nextLine = remainingLines[0];
+	        var nextLine = remainingLines[remainingLinesIndex] || '';
 	        node.add(nextLine.substr(0, mapping.generatedColumn));
-	        remainingLines[0] = nextLine.substr(mapping.generatedColumn);
+	        remainingLines[remainingLinesIndex] = nextLine.substr(mapping.generatedColumn);
 	        lastGeneratedColumn = mapping.generatedColumn;
 	      }
 	      lastMapping = mapping;
 	    }, this);
 	    // We have processed all mappings.
-	    if (remainingLines.length > 0) {
+	    if (remainingLinesIndex < remainingLines.length) {
 	      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(""));
+	      node.add(remainingLines.splice(remainingLinesIndex).join(""));
 	    }
 	
 	    // Copy sourcesContent into SourceNode
@@ -3069,6 +3231,6 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.SourceNode = SourceNode;
 
 
-/***/ }
+/***/ })
 /******/ ]);
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAgMjI3NjhiZTI5Y2YzNjI3NTJmMjkiLCJ3ZWJwYWNrOi8vLy4vdGVzdC90ZXN0LWFwaS5qcyIsIndlYnBhY2s6Ly8vLi9zb3VyY2UtbWFwLmpzIiwid2VicGFjazovLy8uL2xpYi9zb3VyY2UtbWFwLWdlbmVyYXRvci5qcyIsIndlYnBhY2s6Ly8vLi9saWIvYmFzZTY0LXZscS5qcyIsIndlYnBhY2s6Ly8vLi9saWIvYmFzZTY0LmpzIiwid2VicGFjazovLy8uL2xpYi91dGlsLmpzIiwid2VicGFjazovLy8uL2xpYi9hcnJheS1zZXQuanMiLCJ3ZWJwYWNrOi8vLy4vbGliL21hcHBpbmctbGlzdC [...]
\ No newline at end of file
+//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAgM2VmZjI4Y2FkMDE3ODEyMWMzM2QiLCJ3ZWJwYWNrOi8vLy4vdGVzdC90ZXN0LWFwaS5qcyIsIndlYnBhY2s6Ly8vLi9zb3VyY2UtbWFwLmpzIiwid2VicGFjazovLy8uL2xpYi9zb3VyY2UtbWFwLWdlbmVyYXRvci5qcyIsIndlYnBhY2s6Ly8vLi9saWIvYmFzZTY0LXZscS5qcyIsIndlYnBhY2s6Ly8vLi9saWIvYmFzZTY0LmpzIiwid2VicGFjazovLy8uL2xpYi91dGlsLmpzIiwid2VicGFjazovLy8uL2xpYi9hcnJheS1zZXQuanMiLCJ3ZWJwYWNrOi8vLy4vbGliL21h [...]
\ No newline at end of file
diff --git a/dist/test/test_array_set.js b/dist/test/test_array_set.js
index 1736fb1..5a44f93 100644
--- a/dist/test/test_array_set.js
+++ b/dist/test/test_array_set.js
@@ -52,7 +52,7 @@ var SOURCE_MAP_TEST_MODULE =
 /************************************************************************/
 /******/ ([
 /* 0 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -192,9 +192,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 1 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -205,6 +205,7 @@ var SOURCE_MAP_TEST_MODULE =
 	
 	var util = __webpack_require__(2);
 	var has = Object.prototype.hasOwnProperty;
+	var hasNativeMap = typeof Map !== "undefined";
 	
 	/**
 	 * A data structure which is a combination of an array and a set. Adding a new
@@ -214,7 +215,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 */
 	function ArraySet() {
 	  this._array = [];
-	  this._set = Object.create(null);
+	  this._set = hasNativeMap ? new Map() : Object.create(null);
 	}
 	
 	/**
@@ -235,7 +236,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 * @returns Number
 	 */
 	ArraySet.prototype.size = function ArraySet_size() {
-	  return Object.getOwnPropertyNames(this._set).length;
+	  return hasNativeMap ? this._set.size : Object.getOwnPropertyNames(this._set).length;
 	};
 	
 	/**
@@ -244,14 +245,18 @@ var SOURCE_MAP_TEST_MODULE =
 	 * @param String aStr
 	 */
 	ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) {
-	  var sStr = util.toSetString(aStr);
-	  var isDuplicate = has.call(this._set, sStr);
+	  var sStr = hasNativeMap ? aStr : util.toSetString(aStr);
+	  var isDuplicate = hasNativeMap ? this.has(aStr) : has.call(this._set, sStr);
 	  var idx = this._array.length;
 	  if (!isDuplicate || aAllowDuplicates) {
 	    this._array.push(aStr);
 	  }
 	  if (!isDuplicate) {
-	    this._set[sStr] = idx;
+	    if (hasNativeMap) {
+	      this._set.set(aStr, idx);
+	    } else {
+	      this._set[sStr] = idx;
+	    }
 	  }
 	};
 	
@@ -261,8 +266,12 @@ var SOURCE_MAP_TEST_MODULE =
 	 * @param String aStr
 	 */
 	ArraySet.prototype.has = function ArraySet_has(aStr) {
-	  var sStr = util.toSetString(aStr);
-	  return has.call(this._set, sStr);
+	  if (hasNativeMap) {
+	    return this._set.has(aStr);
+	  } else {
+	    var sStr = util.toSetString(aStr);
+	    return has.call(this._set, sStr);
+	  }
 	};
 	
 	/**
@@ -271,10 +280,18 @@ var SOURCE_MAP_TEST_MODULE =
 	 * @param String aStr
 	 */
 	ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) {
-	  var sStr = util.toSetString(aStr);
-	  if (has.call(this._set, sStr)) {
-	    return this._set[sStr];
+	  if (hasNativeMap) {
+	    var idx = this._set.get(aStr);
+	    if (idx >= 0) {
+	        return idx;
+	    }
+	  } else {
+	    var sStr = util.toSetString(aStr);
+	    if (has.call(this._set, sStr)) {
+	      return this._set[sStr];
+	    }
 	  }
+	
 	  throw new Error('"' + aStr + '" is not in the set.');
 	};
 	
@@ -302,9 +319,9 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.ArraySet = ArraySet;
 
 
-/***/ },
+/***/ }),
 /* 2 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -334,7 +351,7 @@ var SOURCE_MAP_TEST_MODULE =
 	}
 	exports.getArg = getArg;
 	
-	var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/;
+	var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/;
 	var dataUrlRegexp = /^data:.+\,.+$/;
 	
 	function urlParse(aUrl) {
@@ -377,7 +394,7 @@ var SOURCE_MAP_TEST_MODULE =
 	/**
 	 * Normalizes a path, or the path portion of a URL:
 	 *
-	 * - Replaces consequtive slashes with one slash.
+	 * - Replaces consecutive slashes with one slash.
 	 * - Removes unnecessary '.' parts.
 	 * - Removes unnecessary '<dir>/..' parts.
 	 *
@@ -490,7 +507,7 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.join = join;
 	
 	exports.isAbsolute = function (aPath) {
-	  return aPath.charAt(0) === '/' || !!aPath.match(urlRegexp);
+	  return aPath.charAt(0) === '/' || urlRegexp.test(aPath);
 	};
 	
 	/**
@@ -610,7 +627,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 * stubbed out mapping.
 	 */
 	function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
-	  var cmp = mappingA.source - mappingB.source;
+	  var cmp = strcmp(mappingA.source, mappingB.source);
 	  if (cmp !== 0) {
 	    return cmp;
 	  }
@@ -635,7 +652,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    return cmp;
 	  }
 	
-	  return mappingA.name - mappingB.name;
+	  return strcmp(mappingA.name, mappingB.name);
 	}
 	exports.compareByOriginalPositions = compareByOriginalPositions;
 	
@@ -659,7 +676,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    return cmp;
 	  }
 	
-	  cmp = mappingA.source - mappingB.source;
+	  cmp = strcmp(mappingA.source, mappingB.source);
 	  if (cmp !== 0) {
 	    return cmp;
 	  }
@@ -674,7 +691,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    return cmp;
 	  }
 	
-	  return mappingA.name - mappingB.name;
+	  return strcmp(mappingA.name, mappingB.name);
 	}
 	exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated;
 	
@@ -683,6 +700,14 @@ var SOURCE_MAP_TEST_MODULE =
 	    return 0;
 	  }
 	
+	  if (aStr1 === null) {
+	    return 1; // aStr2 !== null
+	  }
+	
+	  if (aStr2 === null) {
+	    return -1; // aStr1 !== null
+	  }
+	
 	  if (aStr1 > aStr2) {
 	    return 1;
 	  }
@@ -723,8 +748,71 @@ var SOURCE_MAP_TEST_MODULE =
 	  return strcmp(mappingA.name, mappingB.name);
 	}
 	exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated;
+	
+	/**
+	 * Strip any JSON XSSI avoidance prefix from the string (as documented
+	 * in the source maps specification), and then parse the string as
+	 * JSON.
+	 */
+	function parseSourceMapInput(str) {
+	  return JSON.parse(str.replace(/^\)]}'[^\n]*\n/, ''));
+	}
+	exports.parseSourceMapInput = parseSourceMapInput;
+	
+	/**
+	 * Compute the URL of a source given the the source root, the source's
+	 * URL, and the source map's URL.
+	 */
+	function computeSourceURL(sourceRoot, sourceURL, sourceMapURL) {
+	  sourceURL = sourceURL || '';
+	
+	  if (sourceRoot) {
+	    // This follows what Chrome does.
+	    if (sourceRoot[sourceRoot.length - 1] !== '/' && sourceURL[0] !== '/') {
+	      sourceRoot += '/';
+	    }
+	    // The spec says:
+	    //   Line 4: An optional source root, useful for relocating source
+	    //   files on a server or removing repeated values in the
+	    //   “sources” entry.  This value is prepended to the individual
+	    //   entries in the “source” field.
+	    sourceURL = sourceRoot + sourceURL;
+	  }
+	
+	  // Historically, SourceMapConsumer did not take the sourceMapURL as
+	  // a parameter.  This mode is still somewhat supported, which is why
+	  // this code block is conditional.  However, it's preferable to pass
+	  // the source map URL to SourceMapConsumer, so that this function
+	  // can implement the source URL resolution algorithm as outlined in
+	  // the spec.  This block is basically the equivalent of:
+	  //    new URL(sourceURL, sourceMapURL).toString()
+	  // ... except it avoids using URL, which wasn't available in the
+	  // older releases of node still supported by this library.
+	  //
+	  // The spec says:
+	  //   If the sources are not absolute URLs after prepending of the
+	  //   “sourceRoot”, the sources are resolved relative to the
+	  //   SourceMap (like resolving script src in a html document).
+	  if (sourceMapURL) {
+	    var parsed = urlParse(sourceMapURL);
+	    if (!parsed) {
+	      throw new Error("sourceMapURL could not be parsed");
+	    }
+	    if (parsed.path) {
+	      // Strip the last path component, but keep the "/".
+	      var index = parsed.path.lastIndexOf('/');
+	      if (index >= 0) {
+	        parsed.path = parsed.path.substring(0, index + 1);
+	      }
+	    }
+	    sourceURL = join(urlGenerate(parsed), sourceURL);
+	  }
+	
+	  return normalize(sourceURL);
+	}
+	exports.computeSourceURL = computeSourceURL;
 
 
-/***/ }
+/***/ })
 /******/ ]);
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAgNWI0MDk5ZjRjODRkNGY2YzE3NTIiLCJ3ZWJwYWNrOi8vLy4vdGVzdC90ZXN0LWFycmF5LXNldC5qcyIsIndlYnBhY2s6Ly8vLi9saWIvYXJyYXktc2V0LmpzIiwid2VicGFjazovLy8uL2xpYi91dGlsLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7O0FBQUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsdUJBQWU7QUFDZjtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7O0FBR0E7QUFDQT [...]
\ No newline at end of file
+//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAgNGI2OTc1YTU4MGViMDE0YTdjNzQiLCJ3ZWJwYWNrOi8vLy4vdGVzdC90ZXN0LWFycmF5LXNldC5qcyIsIndlYnBhY2s6Ly8vLi9saWIvYXJyYXktc2V0LmpzIiwid2VicGFjazovLy8uL2xpYi91dGlsLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7O0FBQUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsdUJBQWU7QUFDZjtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7 [...]
\ No newline at end of file
diff --git a/dist/test/test_base64.js b/dist/test/test_base64.js
index 3990efc..2df884b 100644
--- a/dist/test/test_base64.js
+++ b/dist/test/test_base64.js
@@ -52,7 +52,7 @@ var SOURCE_MAP_TEST_MODULE =
 /************************************************************************/
 /******/ ([
 /* 0 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -83,9 +83,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 1 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -156,6 +156,6 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ }
+/***/ })
 /******/ ]);
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAgMzQ3MTQxYTIwOTBkYjZkMzI5NzIiLCJ3ZWJwYWNrOi8vLy4vdGVzdC90ZXN0LWJhc2U2NC5qcyIsIndlYnBhY2s6Ly8vLi9saWIvYmFzZTY0LmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7O0FBQUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsdUJBQWU7QUFDZjtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7O0FBR0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQT [...]
\ No newline at end of file
+//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAgMzQ3MTQxYTIwOTBkYjZkMzI5NzIiLCJ3ZWJwYWNrOi8vLy4vdGVzdC90ZXN0LWJhc2U2NC5qcyIsIndlYnBhY2s6Ly8vLi9saWIvYmFzZTY0LmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7O0FBQUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsdUJBQWU7QUFDZjtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7O0FBR0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7 [...]
\ No newline at end of file
diff --git a/dist/test/test_base64_vlq.js b/dist/test/test_base64_vlq.js
index 904eaff..3a2f90e 100644
--- a/dist/test/test_base64_vlq.js
+++ b/dist/test/test_base64_vlq.js
@@ -52,7 +52,7 @@ var SOURCE_MAP_TEST_MODULE =
 /************************************************************************/
 /******/ ([
 /* 0 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -74,9 +74,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 1 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -220,9 +220,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 2 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -293,6 +293,6 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ }
+/***/ })
 /******/ ]);
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAgOGVkYTJjYWJlZDMwZDJmODc4OTgiLCJ3ZWJwYWNrOi8vLy4vdGVzdC90ZXN0LWJhc2U2NC12bHEuanMiLCJ3ZWJwYWNrOi8vLy4vbGliL2Jhc2U2NC12bHEuanMiLCJ3ZWJwYWNrOi8vLy4vbGliL2Jhc2U2NC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7OztBQUFBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLHVCQUFlO0FBQ2Y7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7OztBQUdBO0 [...]
\ No newline at end of file
+//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAgOGVkYTJjYWJlZDMwZDJmODc4OTgiLCJ3ZWJwYWNrOi8vLy4vdGVzdC90ZXN0LWJhc2U2NC12bHEuanMiLCJ3ZWJwYWNrOi8vLy4vbGliL2Jhc2U2NC12bHEuanMiLCJ3ZWJwYWNrOi8vLy4vbGliL2Jhc2U2NC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7OztBQUFBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLHVCQUFlO0FBQ2Y7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FB [...]
\ No newline at end of file
diff --git a/dist/test/test_binary_search.js b/dist/test/test_binary_search.js
index 102724a..449a7aa 100644
--- a/dist/test/test_binary_search.js
+++ b/dist/test/test_binary_search.js
@@ -52,7 +52,7 @@ var SOURCE_MAP_TEST_MODULE =
 /************************************************************************/
 /******/ ([
 /* 0 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -152,9 +152,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 1 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -269,6 +269,6 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ }
+/***/ })
 /******/ ]);
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAgZWRhOGI0MDAzNWFjZjQ0MzMwMzEiLCJ3ZWJwYWNrOi8vLy4vdGVzdC90ZXN0LWJpbmFyeS1zZWFyY2guanMiLCJ3ZWJwYWNrOi8vLy4vbGliL2JpbmFyeS1zZWFyY2guanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7QUFBQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSx1QkFBZTtBQUNmO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOzs7QUFHQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QU [...]
\ No newline at end of file
+//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAgZWRhOGI0MDAzNWFjZjQ0MzMwMzEiLCJ3ZWJwYWNrOi8vLy4vdGVzdC90ZXN0LWJpbmFyeS1zZWFyY2guanMiLCJ3ZWJwYWNrOi8vLy4vbGliL2JpbmFyeS1zZWFyY2guanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7QUFBQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSx1QkFBZTtBQUNmO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOzs7QUFHQTtBQUNBOztBQUVBO0FB [...]
\ No newline at end of file
diff --git a/dist/test/test_dog_fooding.js b/dist/test/test_dog_fooding.js
index 70577fb..b0c7af0 100644
--- a/dist/test/test_dog_fooding.js
+++ b/dist/test/test_dog_fooding.js
@@ -52,7 +52,7 @@ var SOURCE_MAP_TEST_MODULE =
 /************************************************************************/
 /******/ ([
 /* 0 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -156,9 +156,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 1 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -213,6 +213,58 @@ var SOURCE_MAP_TEST_MODULE =
 	  sourceRoot: '',
 	  mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
 	};
+	exports.testMapSingleSource = {
+	  version: 3,
+	  file: 'min.js',
+	  names: ['bar', 'baz'],
+	  sources: ['one.js'],
+	  sourceRoot: '',
+	  mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID'
+	};
+	exports.testMapEmptyMappings = {
+	  version: 3,
+	  file: 'min.js',
+	  names: [],
+	  sources: ['one.js', 'two.js'],
+	  sourcesContent: [
+	    ' ONE.foo = 1;',
+	    ' TWO.inc = 2;'
+	  ],
+	  sourceRoot: '',
+	  mappings: ''
+	};
+	exports.testMapEmptyMappingsRelativeSources = {
+	  version: 3,
+	  file: 'min.js',
+	  names: [],
+	  sources: ['./one.js', './two.js'],
+	  sourcesContent: [
+	    ' ONE.foo = 1;',
+	    ' TWO.inc = 2;'
+	  ],
+	  sourceRoot: '/the/root',
+	  mappings: ''
+	};
+	exports.testMapEmptyMappingsRelativeSources_generatedExpected = {
+	  version: 3,
+	  file: 'min.js',
+	  names: [],
+	  sources: ['one.js', 'two.js'],
+	  sourcesContent: [
+	    ' ONE.foo = 1;',
+	    ' TWO.inc = 2;'
+	  ],
+	  sourceRoot: '/the/root',
+	  mappings: ''
+	};
+	exports.testMapMultiSourcesMappingRefersSingleSourceOnly = {
+	    version: 3,
+	    file: 'min.js',
+	    names: ['bar', 'baz'],
+	    sources: ['one.js', 'withoutMappings.js'],
+	    sourceRoot: '',
+	    mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID'
+	};
 	// This mapping is identical to above, but uses the indexed format instead.
 	exports.indexedTestMap = {
 	  version: 3,
@@ -457,9 +509,9 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.assertEqualMaps = assertEqualMaps;
 
 
-/***/ },
+/***/ }),
 /* 2 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -489,7 +541,7 @@ var SOURCE_MAP_TEST_MODULE =
 	}
 	exports.getArg = getArg;
 	
-	var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/;
+	var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/;
 	var dataUrlRegexp = /^data:.+\,.+$/;
 	
 	function urlParse(aUrl) {
@@ -532,7 +584,7 @@ var SOURCE_MAP_TEST_MODULE =
 	/**
 	 * Normalizes a path, or the path portion of a URL:
 	 *
-	 * - Replaces consequtive slashes with one slash.
+	 * - Replaces consecutive slashes with one slash.
 	 * - Removes unnecessary '.' parts.
 	 * - Removes unnecessary '<dir>/..' parts.
 	 *
@@ -645,7 +697,7 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.join = join;
 	
 	exports.isAbsolute = function (aPath) {
-	  return aPath.charAt(0) === '/' || !!aPath.match(urlRegexp);
+	  return aPath.charAt(0) === '/' || urlRegexp.test(aPath);
 	};
 	
 	/**
@@ -765,7 +817,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 * stubbed out mapping.
 	 */
 	function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
-	  var cmp = mappingA.source - mappingB.source;
+	  var cmp = strcmp(mappingA.source, mappingB.source);
 	  if (cmp !== 0) {
 	    return cmp;
 	  }
@@ -790,7 +842,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    return cmp;
 	  }
 	
-	  return mappingA.name - mappingB.name;
+	  return strcmp(mappingA.name, mappingB.name);
 	}
 	exports.compareByOriginalPositions = compareByOriginalPositions;
 	
@@ -814,7 +866,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    return cmp;
 	  }
 	
-	  cmp = mappingA.source - mappingB.source;
+	  cmp = strcmp(mappingA.source, mappingB.source);
 	  if (cmp !== 0) {
 	    return cmp;
 	  }
@@ -829,7 +881,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    return cmp;
 	  }
 	
-	  return mappingA.name - mappingB.name;
+	  return strcmp(mappingA.name, mappingB.name);
 	}
 	exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated;
 	
@@ -838,6 +890,14 @@ var SOURCE_MAP_TEST_MODULE =
 	    return 0;
 	  }
 	
+	  if (aStr1 === null) {
+	    return 1; // aStr2 !== null
+	  }
+	
+	  if (aStr2 === null) {
+	    return -1; // aStr1 !== null
+	  }
+	
 	  if (aStr1 > aStr2) {
 	    return 1;
 	  }
@@ -878,11 +938,74 @@ var SOURCE_MAP_TEST_MODULE =
 	  return strcmp(mappingA.name, mappingB.name);
 	}
 	exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated;
+	
+	/**
+	 * Strip any JSON XSSI avoidance prefix from the string (as documented
+	 * in the source maps specification), and then parse the string as
+	 * JSON.
+	 */
+	function parseSourceMapInput(str) {
+	  return JSON.parse(str.replace(/^\)]}'[^\n]*\n/, ''));
+	}
+	exports.parseSourceMapInput = parseSourceMapInput;
+	
+	/**
+	 * Compute the URL of a source given the the source root, the source's
+	 * URL, and the source map's URL.
+	 */
+	function computeSourceURL(sourceRoot, sourceURL, sourceMapURL) {
+	  sourceURL = sourceURL || '';
+	
+	  if (sourceRoot) {
+	    // This follows what Chrome does.
+	    if (sourceRoot[sourceRoot.length - 1] !== '/' && sourceURL[0] !== '/') {
+	      sourceRoot += '/';
+	    }
+	    // The spec says:
+	    //   Line 4: An optional source root, useful for relocating source
+	    //   files on a server or removing repeated values in the
+	    //   “sources” entry.  This value is prepended to the individual
+	    //   entries in the “source” field.
+	    sourceURL = sourceRoot + sourceURL;
+	  }
+	
+	  // Historically, SourceMapConsumer did not take the sourceMapURL as
+	  // a parameter.  This mode is still somewhat supported, which is why
+	  // this code block is conditional.  However, it's preferable to pass
+	  // the source map URL to SourceMapConsumer, so that this function
+	  // can implement the source URL resolution algorithm as outlined in
+	  // the spec.  This block is basically the equivalent of:
+	  //    new URL(sourceURL, sourceMapURL).toString()
+	  // ... except it avoids using URL, which wasn't available in the
+	  // older releases of node still supported by this library.
+	  //
+	  // The spec says:
+	  //   If the sources are not absolute URLs after prepending of the
+	  //   “sourceRoot”, the sources are resolved relative to the
+	  //   SourceMap (like resolving script src in a html document).
+	  if (sourceMapURL) {
+	    var parsed = urlParse(sourceMapURL);
+	    if (!parsed) {
+	      throw new Error("sourceMapURL could not be parsed");
+	    }
+	    if (parsed.path) {
+	      // Strip the last path component, but keep the "/".
+	      var index = parsed.path.lastIndexOf('/');
+	      if (index >= 0) {
+	        parsed.path = parsed.path.substring(0, index + 1);
+	      }
+	    }
+	    sourceURL = join(urlGenerate(parsed), sourceURL);
+	  }
+	
+	  return normalize(sourceURL);
+	}
+	exports.computeSourceURL = computeSourceURL;
 
 
-/***/ },
+/***/ }),
 /* 3 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -897,15 +1020,15 @@ var SOURCE_MAP_TEST_MODULE =
 	var base64VLQ = __webpack_require__(6);
 	var quickSort = __webpack_require__(8).quickSort;
 	
-	function SourceMapConsumer(aSourceMap) {
+	function SourceMapConsumer(aSourceMap, aSourceMapURL) {
 	  var sourceMap = aSourceMap;
 	  if (typeof aSourceMap === 'string') {
-	    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+	    sourceMap = util.parseSourceMapInput(aSourceMap);
 	  }
 	
 	  return sourceMap.sections != null
-	    ? new IndexedSourceMapConsumer(sourceMap)
-	    : new BasicSourceMapConsumer(sourceMap);
+	    ? new IndexedSourceMapConsumer(sourceMap, aSourceMapURL)
+	    : new BasicSourceMapConsumer(sourceMap, aSourceMapURL);
 	}
 	
 	SourceMapConsumer.fromSourceMap = function(aSourceMap) {
@@ -949,6 +1072,8 @@ var SOURCE_MAP_TEST_MODULE =
 	
 	SourceMapConsumer.prototype.__generatedMappings = null;
 	Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', {
+	  configurable: true,
+	  enumerable: true,
 	  get: function () {
 	    if (!this.__generatedMappings) {
 	      this._parseMappings(this._mappings, this.sourceRoot);
@@ -960,6 +1085,8 @@ var SOURCE_MAP_TEST_MODULE =
 	
 	SourceMapConsumer.prototype.__originalMappings = null;
 	Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', {
+	  configurable: true,
+	  enumerable: true,
 	  get: function () {
 	    if (!this.__originalMappings) {
 	      this._parseMappings(this._mappings, this.sourceRoot);
@@ -1027,9 +1154,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    var sourceRoot = this.sourceRoot;
 	    mappings.map(function (mapping) {
 	      var source = mapping.source === null ? null : this._sources.at(mapping.source);
-	      if (source != null && sourceRoot != null) {
-	        source = util.join(sourceRoot, source);
-	      }
+	      source = util.computeSourceURL(sourceRoot, source, this._sourceMapURL);
 	      return {
 	        source: source,
 	        generatedLine: mapping.generatedLine,
@@ -1052,13 +1177,16 @@ var SOURCE_MAP_TEST_MODULE =
 	 * The only argument is an object with the following properties:
 	 *
 	 *   - source: The filename of the original source.
-	 *   - line: The line number in the original source.
+	 *   - line: The line number in the original source.  The line number is 1-based.
 	 *   - column: Optional. the column number in the original source.
+	 *    The column number is 0-based.
 	 *
 	 * and an array of objects is returned, each with the following properties:
 	 *
-	 *   - line: The line number in the generated source, or null.
+	 *   - line: The line number in the generated source, or null.  The
+	 *    line number is 1-based.
 	 *   - column: The column number in the generated source, or null.
+	 *    The column number is 0-based.
 	 */
 	SourceMapConsumer.prototype.allGeneratedPositionsFor =
 	  function SourceMapConsumer_allGeneratedPositionsFor(aArgs) {
@@ -1140,7 +1268,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 * query for information about the original file positions by giving it a file
 	 * position in the generated source.
 	 *
-	 * The only parameter is the raw source map (either as a JSON string, or
+	 * The first parameter is the raw source map (either as a JSON string, or
 	 * already parsed to an object). According to the spec, source maps have the
 	 * following attributes:
 	 *
@@ -1163,12 +1291,16 @@ var SOURCE_MAP_TEST_MODULE =
 	 *       mappings: "AA,AB;;ABCDE;"
 	 *     }
 	 *
+	 * The second parameter, if given, is a string whose value is the URL
+	 * at which the source map was found.  This URL is used to compute the
+	 * sources array.
+	 *
 	 * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#
 	 */
-	function BasicSourceMapConsumer(aSourceMap) {
+	function BasicSourceMapConsumer(aSourceMap, aSourceMapURL) {
 	  var sourceMap = aSourceMap;
 	  if (typeof aSourceMap === 'string') {
-	    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+	    sourceMap = util.parseSourceMapInput(aSourceMap);
 	  }
 	
 	  var version = util.getArg(sourceMap, 'version');
@@ -1187,6 +1319,10 @@ var SOURCE_MAP_TEST_MODULE =
 	    throw new Error('Unsupported version: ' + version);
 	  }
 	
+	  if (sourceRoot) {
+	    sourceRoot = util.normalize(sourceRoot);
+	  }
+	
 	  sources = sources
 	    .map(String)
 	    // Some source maps produce relative source paths like "./foo.js" instead of
@@ -1213,6 +1349,7 @@ var SOURCE_MAP_TEST_MODULE =
 	  this.sourceRoot = sourceRoot;
 	  this.sourcesContent = sourcesContent;
 	  this._mappings = mappings;
+	  this._sourceMapURL = aSourceMapURL;
 	  this.file = file;
 	}
 	
@@ -1224,10 +1361,12 @@ var SOURCE_MAP_TEST_MODULE =
 	 *
 	 * @param SourceMapGenerator aSourceMap
 	 *        The source map that will be consumed.
+	 * @param String aSourceMapURL
+	 *        The URL at which the source map can be found (optional)
 	 * @returns BasicSourceMapConsumer
 	 */
 	BasicSourceMapConsumer.fromSourceMap =
-	  function SourceMapConsumer_fromSourceMap(aSourceMap) {
+	  function SourceMapConsumer_fromSourceMap(aSourceMap, aSourceMapURL) {
 	    var smc = Object.create(BasicSourceMapConsumer.prototype);
 	
 	    var names = smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);
@@ -1236,6 +1375,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(),
 	                                                            smc.sourceRoot);
 	    smc.file = aSourceMap._file;
+	    smc._sourceMapURL = aSourceMapURL;
 	
 	    // Because we are modifying the entries (by converting string sources and
 	    // names to indices into the sources and names ArraySets), we have to make
@@ -1283,7 +1423,7 @@ var SOURCE_MAP_TEST_MODULE =
 	Object.defineProperty(BasicSourceMapConsumer.prototype, 'sources', {
 	  get: function () {
 	    return this._sources.toArray().map(function (s) {
-	      return this.sourceRoot != null ? util.join(this.sourceRoot, s) : s;
+	      return util.computeSourceURL(this.sourceRoot, s, this._sourceMapURL);
 	    }, this);
 	  }
 	});
@@ -1465,8 +1605,10 @@ var SOURCE_MAP_TEST_MODULE =
 	 * source's line and column positions provided. The only argument is an object
 	 * with the following properties:
 	 *
-	 *   - line: The line number in the generated source.
-	 *   - column: The column number in the generated source.
+	 *   - line: The line number in the generated source.  The line number
+	 *     is 1-based.
+	 *   - column: The column number in the generated source.  The column
+	 *     number is 0-based.
 	 *   - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or
 	 *     'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the
 	 *     closest element that is smaller than or greater than the one we are
@@ -1476,8 +1618,10 @@ var SOURCE_MAP_TEST_MODULE =
 	 * and an object is returned with the following properties:
 	 *
 	 *   - source: The original source file, or null.
-	 *   - line: The line number in the original source, or null.
-	 *   - column: The column number in the original source, or null.
+	 *   - line: The line number in the original source, or null.  The
+	 *     line number is 1-based.
+	 *   - column: The column number in the original source, or null.  The
+	 *     column number is 0-based.
 	 *   - name: The original identifier, or null.
 	 */
 	BasicSourceMapConsumer.prototype.originalPositionFor =
@@ -1503,9 +1647,7 @@ var SOURCE_MAP_TEST_MODULE =
 	        var source = util.getArg(mapping, 'source', null);
 	        if (source !== null) {
 	          source = this._sources.at(source);
-	          if (this.sourceRoot != null) {
-	            source = util.join(this.sourceRoot, source);
-	          }
+	          source = util.computeSourceURL(this.sourceRoot, source, this._sourceMapURL);
 	        }
 	        var name = util.getArg(mapping, 'name', null);
 	        if (name !== null) {
@@ -1552,12 +1694,23 @@ var SOURCE_MAP_TEST_MODULE =
 	      return null;
 	    }
 	
+	    var relativeSource = aSource;
 	    if (this.sourceRoot != null) {
-	      aSource = util.relative(this.sourceRoot, aSource);
+	      relativeSource = util.relative(this.sourceRoot, relativeSource);
 	    }
 	
-	    if (this._sources.has(aSource)) {
-	      return this.sourcesContent[this._sources.indexOf(aSource)];
+	    if (this._sources.has(relativeSource)) {
+	      return this.sourcesContent[this._sources.indexOf(relativeSource)];
+	    }
+	
+	    // Maybe aSource is an absolute URL as returned by |sources|.  In
+	    // this case we can't simply undo the transform.
+	    var sourceArray = this.sources;
+	    var i;
+	    for (i = 0; i < sourceArray.length; ++i) {
+	      if (sourceArray[i] == aSource) {
+	        return this.sourcesContent[i];
+	      }
 	    }
 	
 	    var url;
@@ -1567,15 +1720,15 @@ var SOURCE_MAP_TEST_MODULE =
 	      // many users. We can help them out when they expect file:// URIs to
 	      // behave like it would if they were running a local HTTP server. See
 	      // https://bugzilla.mozilla.org/show_bug.cgi?id=885597.
-	      var fileUriAbsPath = aSource.replace(/^file:\/\//, "");
+	      var fileUriAbsPath = relativeSource.replace(/^file:\/\//, "");
 	      if (url.scheme == "file"
 	          && this._sources.has(fileUriAbsPath)) {
 	        return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)]
 	      }
 	
 	      if ((!url.path || url.path == "/")
-	          && this._sources.has("/" + aSource)) {
-	        return this.sourcesContent[this._sources.indexOf("/" + aSource)];
+	          && this._sources.has("/" + relativeSource)) {
+	        return this.sourcesContent[this._sources.indexOf("/" + relativeSource)];
 	      }
 	    }
 	
@@ -1587,7 +1740,7 @@ var SOURCE_MAP_TEST_MODULE =
 	      return null;
 	    }
 	    else {
-	      throw new Error('"' + aSource + '" is not in the SourceMap.');
+	      throw new Error('"' + relativeSource + '" is not in the SourceMap.');
 	    }
 	  };
 	
@@ -1597,8 +1750,10 @@ var SOURCE_MAP_TEST_MODULE =
 	 * the following properties:
 	 *
 	 *   - source: The filename of the original source.
-	 *   - line: The line number in the original source.
-	 *   - column: The column number in the original source.
+	 *   - line: The line number in the original source.  The line number
+	 *     is 1-based.
+	 *   - column: The column number in the original source.  The column
+	 *     number is 0-based.
 	 *   - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or
 	 *     'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the
 	 *     closest element that is smaller than or greater than the one we are
@@ -1607,8 +1762,10 @@ var SOURCE_MAP_TEST_MODULE =
 	 *
 	 * and an object is returned with the following properties:
 	 *
-	 *   - line: The line number in the generated source, or null.
+	 *   - line: The line number in the generated source, or null.  The
+	 *     line number is 1-based.
 	 *   - column: The column number in the generated source, or null.
+	 *     The column number is 0-based.
 	 */
 	BasicSourceMapConsumer.prototype.generatedPositionFor =
 	  function SourceMapConsumer_generatedPositionFor(aArgs) {
@@ -1667,7 +1824,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 * that it takes "indexed" source maps (i.e. ones with a "sections" field) as
 	 * input.
 	 *
-	 * The only parameter is a raw source map (either as a JSON string, or already
+	 * The first parameter is a raw source map (either as a JSON string, or already
 	 * parsed to an object). According to the spec for indexed source maps, they
 	 * have the following attributes:
 	 *
@@ -1704,12 +1861,16 @@ var SOURCE_MAP_TEST_MODULE =
 	 *    }],
 	 *  }
 	 *
+	 * The second parameter, if given, is a string whose value is the URL
+	 * at which the source map was found.  This URL is used to compute the
+	 * sources array.
+	 *
 	 * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.535es3xeprgt
 	 */
-	function IndexedSourceMapConsumer(aSourceMap) {
+	function IndexedSourceMapConsumer(aSourceMap, aSourceMapURL) {
 	  var sourceMap = aSourceMap;
 	  if (typeof aSourceMap === 'string') {
-	    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+	    sourceMap = util.parseSourceMapInput(aSourceMap);
 	  }
 	
 	  var version = util.getArg(sourceMap, 'version');
@@ -1749,7 +1910,7 @@ var SOURCE_MAP_TEST_MODULE =
 	        generatedLine: offsetLine + 1,
 	        generatedColumn: offsetColumn + 1
 	      },
-	      consumer: new SourceMapConsumer(util.getArg(s, 'map'))
+	      consumer: new SourceMapConsumer(util.getArg(s, 'map'), aSourceMapURL)
 	    }
 	  });
 	}
@@ -1782,14 +1943,18 @@ var SOURCE_MAP_TEST_MODULE =
 	 * source's line and column positions provided. The only argument is an object
 	 * with the following properties:
 	 *
-	 *   - line: The line number in the generated source.
-	 *   - column: The column number in the generated source.
+	 *   - line: The line number in the generated source.  The line number
+	 *     is 1-based.
+	 *   - column: The column number in the generated source.  The column
+	 *     number is 0-based.
 	 *
 	 * and an object is returned with the following properties:
 	 *
 	 *   - source: The original source file, or null.
-	 *   - line: The line number in the original source, or null.
-	 *   - column: The column number in the original source, or null.
+	 *   - line: The line number in the original source, or null.  The
+	 *     line number is 1-based.
+	 *   - column: The column number in the original source, or null.  The
+	 *     column number is 0-based.
 	 *   - name: The original identifier, or null.
 	 */
 	IndexedSourceMapConsumer.prototype.originalPositionFor =
@@ -1873,13 +2038,17 @@ var SOURCE_MAP_TEST_MODULE =
 	 * the following properties:
 	 *
 	 *   - source: The filename of the original source.
-	 *   - line: The line number in the original source.
-	 *   - column: The column number in the original source.
+	 *   - line: The line number in the original source.  The line number
+	 *     is 1-based.
+	 *   - column: The column number in the original source.  The column
+	 *     number is 0-based.
 	 *
 	 * and an object is returned with the following properties:
 	 *
-	 *   - line: The line number in the generated source, or null.
+	 *   - line: The line number in the generated source, or null.  The
+	 *     line number is 1-based. 
 	 *   - column: The column number in the generated source, or null.
+	 *     The column number is 0-based.
 	 */
 	IndexedSourceMapConsumer.prototype.generatedPositionFor =
 	  function IndexedSourceMapConsumer_generatedPositionFor(aArgs) {
@@ -1927,15 +2096,16 @@ var SOURCE_MAP_TEST_MODULE =
 	        var mapping = sectionMappings[j];
 	
 	        var source = section.consumer._sources.at(mapping.source);
-	        if (section.consumer.sourceRoot !== null) {
-	          source = util.join(section.consumer.sourceRoot, source);
-	        }
+	        source = util.computeSourceURL(section.consumer.sourceRoot, source, this._sourceMapURL);
 	        this._sources.add(source);
 	        source = this._sources.indexOf(source);
 	
-	        var name = section.consumer._names.at(mapping.name);
-	        this._names.add(name);
-	        name = this._names.indexOf(name);
+	        var name = null;
+	        if (mapping.name) {
+	          name = section.consumer._names.at(mapping.name);
+	          this._names.add(name);
+	          name = this._names.indexOf(name);
+	        }
 	
 	        // The mappings coming from the consumer for the section have
 	        // generated positions relative to the start of the section, so we
@@ -1968,9 +2138,9 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer;
 
 
-/***/ },
+/***/ }),
 /* 4 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -2085,9 +2255,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 5 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -2098,6 +2268,7 @@ var SOURCE_MAP_TEST_MODULE =
 	
 	var util = __webpack_require__(2);
 	var has = Object.prototype.hasOwnProperty;
+	var hasNativeMap = typeof Map !== "undefined";
 	
 	/**
 	 * A data structure which is a combination of an array and a set. Adding a new
@@ -2107,7 +2278,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 */
 	function ArraySet() {
 	  this._array = [];
-	  this._set = Object.create(null);
+	  this._set = hasNativeMap ? new Map() : Object.create(null);
 	}
 	
 	/**
@@ -2128,7 +2299,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 * @returns Number
 	 */
 	ArraySet.prototype.size = function ArraySet_size() {
-	  return Object.getOwnPropertyNames(this._set).length;
+	  return hasNativeMap ? this._set.size : Object.getOwnPropertyNames(this._set).length;
 	};
 	
 	/**
@@ -2137,14 +2308,18 @@ var SOURCE_MAP_TEST_MODULE =
 	 * @param String aStr
 	 */
 	ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) {
-	  var sStr = util.toSetString(aStr);
-	  var isDuplicate = has.call(this._set, sStr);
+	  var sStr = hasNativeMap ? aStr : util.toSetString(aStr);
+	  var isDuplicate = hasNativeMap ? this.has(aStr) : has.call(this._set, sStr);
 	  var idx = this._array.length;
 	  if (!isDuplicate || aAllowDuplicates) {
 	    this._array.push(aStr);
 	  }
 	  if (!isDuplicate) {
-	    this._set[sStr] = idx;
+	    if (hasNativeMap) {
+	      this._set.set(aStr, idx);
+	    } else {
+	      this._set[sStr] = idx;
+	    }
 	  }
 	};
 	
@@ -2154,8 +2329,12 @@ var SOURCE_MAP_TEST_MODULE =
 	 * @param String aStr
 	 */
 	ArraySet.prototype.has = function ArraySet_has(aStr) {
-	  var sStr = util.toSetString(aStr);
-	  return has.call(this._set, sStr);
+	  if (hasNativeMap) {
+	    return this._set.has(aStr);
+	  } else {
+	    var sStr = util.toSetString(aStr);
+	    return has.call(this._set, sStr);
+	  }
 	};
 	
 	/**
@@ -2164,10 +2343,18 @@ var SOURCE_MAP_TEST_MODULE =
 	 * @param String aStr
 	 */
 	ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) {
-	  var sStr = util.toSetString(aStr);
-	  if (has.call(this._set, sStr)) {
-	    return this._set[sStr];
+	  if (hasNativeMap) {
+	    var idx = this._set.get(aStr);
+	    if (idx >= 0) {
+	        return idx;
+	    }
+	  } else {
+	    var sStr = util.toSetString(aStr);
+	    if (has.call(this._set, sStr)) {
+	      return this._set[sStr];
+	    }
 	  }
+	
 	  throw new Error('"' + aStr + '" is not in the set.');
 	};
 	
@@ -2195,9 +2382,9 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.ArraySet = ArraySet;
 
 
-/***/ },
+/***/ }),
 /* 6 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -2341,9 +2528,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 7 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -2414,9 +2601,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 8 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -2534,9 +2721,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 9 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -2612,6 +2799,15 @@ var SOURCE_MAP_TEST_MODULE =
 	      generator.addMapping(newMapping);
 	    });
 	    aSourceMapConsumer.sources.forEach(function (sourceFile) {
+	      var sourceRelative = sourceFile;
+	      if (sourceRoot !== null) {
+	        sourceRelative = util.relative(sourceRoot, sourceFile);
+	      }
+	
+	      if (!generator._sources.has(sourceRelative)) {
+	        generator._sources.add(sourceRelative);
+	      }
+	
 	      var content = aSourceMapConsumer.sourceContentFor(sourceFile);
 	      if (content != null) {
 	        generator.setSourceContent(sourceFile, content);
@@ -2799,6 +2995,18 @@ var SOURCE_MAP_TEST_MODULE =
 	SourceMapGenerator.prototype._validateMapping =
 	  function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource,
 	                                              aName) {
+	    // When aOriginal is truthy but has empty values for .line and .column,
+	    // it is most likely a programmer error. In this case we throw a very
+	    // specific error message to try to guide them the right way.
+	    // For example: https://github.com/Polymer/polymer-bundler/pull/519
+	    if (aOriginal && typeof aOriginal.line !== 'number' && typeof aOriginal.column !== 'number') {
+	        throw new Error(
+	            'original.line and original.column are not numbers -- you probably meant to omit ' +
+	            'the original mapping entirely and only map the generated position. If so, pass ' +
+	            'null for the original mapping instead of an object with empty or null values.'
+	        );
+	    }
+	
 	    if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
 	        && aGenerated.line > 0 && aGenerated.column >= 0
 	        && !aOriginal && !aSource && !aName) {
@@ -2944,9 +3152,9 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.SourceMapGenerator = SourceMapGenerator;
 
 
-/***/ },
+/***/ }),
 /* 10 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -3029,6 +3237,6 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.MappingList = MappingList;
 
 
-/***/ }
+/***/ })
 /******/ ]);
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAgYjU4NTI2NGQ1NmJmYTUxOGY3MjAiLCJ3ZWJwYWNrOi8vLy4vdGVzdC90ZXN0LWRvZy1mb29kaW5nLmpzIiwid2VicGFjazovLy8uL3Rlc3QvdXRpbC5qcyIsIndlYnBhY2s6Ly8vLi9saWIvdXRpbC5qcyIsIndlYnBhY2s6Ly8vLi9saWIvc291cmNlLW1hcC1jb25zdW1lci5qcyIsIndlYnBhY2s6Ly8vLi9saWIvYmluYXJ5LXNlYXJjaC5qcyIsIndlYnBhY2s6Ly8vLi9saWIvYXJyYXktc2V0LmpzIiwid2VicGFjazovLy8uL2xpYi9iYXNlNjQtdmxxLmpzIiwid2VicGFjazovLy8uL2xpYi [...]
\ No newline at end of file
+//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAgNDYwYzc5Yzc3MDg2YWU2MzYwNWYiLCJ3ZWJwYWNrOi8vLy4vdGVzdC90ZXN0LWRvZy1mb29kaW5nLmpzIiwid2VicGFjazovLy8uL3Rlc3QvdXRpbC5qcyIsIndlYnBhY2s6Ly8vLi9saWIvdXRpbC5qcyIsIndlYnBhY2s6Ly8vLi9saWIvc291cmNlLW1hcC1jb25zdW1lci5qcyIsIndlYnBhY2s6Ly8vLi9saWIvYmluYXJ5LXNlYXJjaC5qcyIsIndlYnBhY2s6Ly8vLi9saWIvYXJyYXktc2V0LmpzIiwid2VicGFjazovLy8uL2xpYi9iYXNlNjQtdmxxLmpzIiwid2VicGFj [...]
\ No newline at end of file
diff --git a/dist/test/test_quick_sort.js b/dist/test/test_quick_sort.js
index 9afab28..12ce81c 100644
--- a/dist/test/test_quick_sort.js
+++ b/dist/test/test_quick_sort.js
@@ -52,7 +52,7 @@ var SOURCE_MAP_TEST_MODULE =
 /************************************************************************/
 /******/ ([
 /* 0 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -101,9 +101,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 1 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -221,6 +221,6 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ }
+/***/ })
 /******/ ]);
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAgZDAyMzFjYjcwYWJmOTIyNmYzOTEiLCJ3ZWJwYWNrOi8vLy4vdGVzdC90ZXN0LXF1aWNrLXNvcnQuanMiLCJ3ZWJwYWNrOi8vLy4vbGliL3F1aWNrLXNvcnQuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7QUFBQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSx1QkFBZTtBQUNmO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOzs7QUFHQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QU [...]
\ No newline at end of file
+//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAgZDAyMzFjYjcwYWJmOTIyNmYzOTEiLCJ3ZWJwYWNrOi8vLy4vdGVzdC90ZXN0LXF1aWNrLXNvcnQuanMiLCJ3ZWJwYWNrOi8vLy4vbGliL3F1aWNrLXNvcnQuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7QUFBQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSx1QkFBZTtBQUNmO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOzs7QUFHQTtBQUNBOztBQUVBO0FBQ0E7O0FB [...]
\ No newline at end of file
diff --git a/dist/test/test_source_map_consumer.js b/dist/test/test_source_map_consumer.js
index 8ac6469..81efae5 100644
--- a/dist/test/test_source_map_consumer.js
+++ b/dist/test/test_source_map_consumer.js
@@ -52,7 +52,7 @@ var SOURCE_MAP_TEST_MODULE =
 /************************************************************************/
 /******/ ([
 /* 0 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -292,7 +292,7 @@ var SOURCE_MAP_TEST_MODULE =
 	
 	exports['test creating source map consumers with )]}\' prefix'] = function (assert) {
 	  assert.doesNotThrow(function () {
-	    var map = new SourceMapConsumer(")]}'" + JSON.stringify(util.testMap));
+	    var map = new SourceMapConsumer(")]}'\n" + JSON.stringify(util.testMap));
 	  });
 	};
 	
@@ -877,26 +877,27 @@ var SOURCE_MAP_TEST_MODULE =
 	  assert.equal(sources[0], 'http://www.example.com/original.js');
 	};
 	
-	exports['test github issue #43'] = function (assert) {
+	// Was github issue #43, but that's no longer valid.
+	exports['test source resolution with sourceMapURL'] = function (assert) {
 	  var map = new SourceMapGenerator({
-	    sourceRoot: 'http://example.com',
+	    sourceRoot: '',
 	    file: 'foo.js'
 	  });
 	  map.addMapping({
 	    original: { line: 1, column: 1 },
 	    generated: { line: 2, column: 2 },
-	    source: 'http://cdn.example.com/original.js'
+	    source: 'original.js',
 	  });
-	  map = new SourceMapConsumer(map.toString());
+	  map = new SourceMapConsumer(map.toString(), 'http://cdn.example.com');
 	
 	  var sources = map.sources;
 	  assert.equal(sources.length, 1,
 	               'Should only be one source.');
 	  assert.equal(sources[0], 'http://cdn.example.com/original.js',
-	               'Should not be joined with the sourceRoot.');
+	               'Should be joined with the source map URL.');
 	};
 	
-	exports['test absolute path, but same host sources'] = function (assert) {
+	exports['test sourceRoot prepending'] = function (assert) {
 	  var map = new SourceMapGenerator({
 	    sourceRoot: 'http://example.com/foo/bar',
 	    file: 'foo.js'
@@ -911,8 +912,8 @@ var SOURCE_MAP_TEST_MODULE =
 	  var sources = map.sources;
 	  assert.equal(sources.length, 1,
 	               'Should only be one source.');
-	  assert.equal(sources[0], 'http://example.com/original.js',
-	               'Source should be relative the host of the source root.');
+	  assert.equal(sources[0], 'http://example.com/foo/bar/original.js',
+	               'Source include the source root.');
 	};
 	
 	exports['test indexed source map errors when sections are out of order by line'] = function(assert) {
@@ -944,6 +945,22 @@ var SOURCE_MAP_TEST_MODULE =
 	  assert.equal(map.sourceContentFor("/a"), "foo");
 	};
 	
+	exports['test full source content with sourceMapURL'] = function (assert) {
+	  var map = new SourceMapConsumer({
+	    'version': 3,
+	    'file': 'foo.js',
+	    'sourceRoot': '',
+	    'sources': ['original.js'],
+	    'names': [],
+	    'mappings': 'AACA',
+	    'sourcesContent': ['yellow warbler']
+	  }, 'http://cdn.example.com');
+	
+	  var sources = map.sources;
+	  assert.equal(map.sourceContentFor('http://cdn.example.com/original.js'), 'yellow warbler',
+	               'Source content should be found using full URL');
+	};
+	
 	exports['test bug 885597'] = function (assert) {
 	  var map = new SourceMapConsumer({
 	    "version": 3,
@@ -1175,11 +1192,101 @@ var SOURCE_MAP_TEST_MODULE =
 	  });
 	  assert.equal(i, 1);
 	};
+	
+	exports['test non-normalized sourceRoot (from issue #227)'] = function (assert) {
+	  var consumer = new SourceMapConsumer({
+	    version: 3,
+	    sources: [ 'index.js' ],
+	    names: [],
+	    mappings: ';;AAAA,IAAI,OAAO,MAAP',
+	    file: 'index.js',
+	    sourceRoot: './src/',
+	    sourcesContent: [ 'var name = "Mark"\n' ]
+	  });
+	  assert.equal(consumer.sourceRoot, 'src/', 'sourceRoot was normalized');
+	  // Before the fix, this threw an exception.
+	  consumer.sourceContentFor(consumer.sources[0]);
+	};
+	
+	exports['test webpack URL resolution'] = function (assert) {
+	  var map = {
+	    version: 3,
+	    sources:  ["webpack:///webpack/bootstrap 67e184f9679733298d44"],
+	    names: [],
+	    mappings: "CAAS",
+	    file: "static/js/manifest.b7cf97680f7a50fa150f.js",
+	    sourceRoot: ""
+	  };
+	  var consumer = new SourceMapConsumer(map);
+	
+	  assert.equal(consumer.sources.length, 1);
+	  assert.equal(consumer.sources[0], "webpack:///webpack/bootstrap 67e184f9679733298d44");
+	};
+	
+	exports['test webpack URL resolution with sourceMapURL'] = function (assert) {
+	  var map = {
+	    version: 3,
+	    sources:  ["webpack:///webpack/bootstrap 67e184f9679733298d44"],
+	    names: [],
+	    mappings: "CAAS",
+	    file: "static/js/manifest.b7cf97680f7a50fa150f.js",
+	    sourceRoot: ""
+	  };
+	  var consumer = new SourceMapConsumer(map, 'http://www.example.com/q.js.map');
+	
+	  assert.equal(consumer.sources.length, 1);
+	  assert.equal(consumer.sources[0], "webpack:///webpack/bootstrap 67e184f9679733298d44");
+	};
+	
+	exports['test relative webpack URL resolution with sourceMapURL'] = function (assert) {
+	  var map = {
+	    version: 3,
+	    sources:  ["webpack/bootstrap.js"],
+	    names: [],
+	    mappings: "CAAS",
+	    file: "static/js/manifest.b7cf97680f7a50fa150f.js",
+	    sourceRoot: "webpack:///"
+	  };
+	  var consumer = new SourceMapConsumer(map, 'http://www.example.com/q.js.map');
+	
+	  assert.equal(consumer.sources.length, 1);
+	  assert.equal(consumer.sources[0], "webpack:///webpack/bootstrap.js");
+	};
+	
+	exports['test basic URL resolution with sourceMapURL'] = function (assert) {
+	  var map = {
+	    version: 3,
+	    sources:  ["something.js"],
+	    names: [],
+	    mappings: "CAAS",
+	    file: "static/js/manifest.b7cf97680f7a50fa150f.js",
+	    sourceRoot: "src"
+	  };
+	  var consumer = new SourceMapConsumer(map, 'http://www.example.com/x/q.js.map');
+	
+	  assert.equal(consumer.sources.length, 1);
+	  assert.equal(consumer.sources[0], 'http://www.example.com/x/src/something.js');
+	};
+	
+	exports['test absolute sourceURL resolution with sourceMapURL'] = function (assert) {
+	  var map = {
+	    version: 3,
+	    sources:  ["something.js"],
+	    names: [],
+	    mappings: "CAAS",
+	    file: "static/js/manifest.b7cf97680f7a50fa150f.js",
+	    sourceRoot: "http://www.example.com/src"
+	  };
+	  var consumer = new SourceMapConsumer(map, 'http://www.example.com/x/q.js.map');
+	
+	  assert.equal(consumer.sources.length, 1);
+	  assert.equal(consumer.sources[0], 'http://www.example.com/src/something.js');
+	};
 
 
-/***/ },
+/***/ }),
 /* 1 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -1234,6 +1341,58 @@ var SOURCE_MAP_TEST_MODULE =
 	  sourceRoot: '',
 	  mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
 	};
+	exports.testMapSingleSource = {
+	  version: 3,
+	  file: 'min.js',
+	  names: ['bar', 'baz'],
+	  sources: ['one.js'],
+	  sourceRoot: '',
+	  mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID'
+	};
+	exports.testMapEmptyMappings = {
+	  version: 3,
+	  file: 'min.js',
+	  names: [],
+	  sources: ['one.js', 'two.js'],
+	  sourcesContent: [
+	    ' ONE.foo = 1;',
+	    ' TWO.inc = 2;'
+	  ],
+	  sourceRoot: '',
+	  mappings: ''
+	};
+	exports.testMapEmptyMappingsRelativeSources = {
+	  version: 3,
+	  file: 'min.js',
+	  names: [],
+	  sources: ['./one.js', './two.js'],
+	  sourcesContent: [
+	    ' ONE.foo = 1;',
+	    ' TWO.inc = 2;'
+	  ],
+	  sourceRoot: '/the/root',
+	  mappings: ''
+	};
+	exports.testMapEmptyMappingsRelativeSources_generatedExpected = {
+	  version: 3,
+	  file: 'min.js',
+	  names: [],
+	  sources: ['one.js', 'two.js'],
+	  sourcesContent: [
+	    ' ONE.foo = 1;',
+	    ' TWO.inc = 2;'
+	  ],
+	  sourceRoot: '/the/root',
+	  mappings: ''
+	};
+	exports.testMapMultiSourcesMappingRefersSingleSourceOnly = {
+	    version: 3,
+	    file: 'min.js',
+	    names: ['bar', 'baz'],
+	    sources: ['one.js', 'withoutMappings.js'],
+	    sourceRoot: '',
+	    mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID'
+	};
 	// This mapping is identical to above, but uses the indexed format instead.
 	exports.indexedTestMap = {
 	  version: 3,
@@ -1478,9 +1637,9 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.assertEqualMaps = assertEqualMaps;
 
 
-/***/ },
+/***/ }),
 /* 2 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -1510,7 +1669,7 @@ var SOURCE_MAP_TEST_MODULE =
 	}
 	exports.getArg = getArg;
 	
-	var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/;
+	var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/;
 	var dataUrlRegexp = /^data:.+\,.+$/;
 	
 	function urlParse(aUrl) {
@@ -1553,7 +1712,7 @@ var SOURCE_MAP_TEST_MODULE =
 	/**
 	 * Normalizes a path, or the path portion of a URL:
 	 *
-	 * - Replaces consequtive slashes with one slash.
+	 * - Replaces consecutive slashes with one slash.
 	 * - Removes unnecessary '.' parts.
 	 * - Removes unnecessary '<dir>/..' parts.
 	 *
@@ -1666,7 +1825,7 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.join = join;
 	
 	exports.isAbsolute = function (aPath) {
-	  return aPath.charAt(0) === '/' || !!aPath.match(urlRegexp);
+	  return aPath.charAt(0) === '/' || urlRegexp.test(aPath);
 	};
 	
 	/**
@@ -1786,7 +1945,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 * stubbed out mapping.
 	 */
 	function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
-	  var cmp = mappingA.source - mappingB.source;
+	  var cmp = strcmp(mappingA.source, mappingB.source);
 	  if (cmp !== 0) {
 	    return cmp;
 	  }
@@ -1811,7 +1970,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    return cmp;
 	  }
 	
-	  return mappingA.name - mappingB.name;
+	  return strcmp(mappingA.name, mappingB.name);
 	}
 	exports.compareByOriginalPositions = compareByOriginalPositions;
 	
@@ -1835,7 +1994,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    return cmp;
 	  }
 	
-	  cmp = mappingA.source - mappingB.source;
+	  cmp = strcmp(mappingA.source, mappingB.source);
 	  if (cmp !== 0) {
 	    return cmp;
 	  }
@@ -1850,7 +2009,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    return cmp;
 	  }
 	
-	  return mappingA.name - mappingB.name;
+	  return strcmp(mappingA.name, mappingB.name);
 	}
 	exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated;
 	
@@ -1859,6 +2018,14 @@ var SOURCE_MAP_TEST_MODULE =
 	    return 0;
 	  }
 	
+	  if (aStr1 === null) {
+	    return 1; // aStr2 !== null
+	  }
+	
+	  if (aStr2 === null) {
+	    return -1; // aStr1 !== null
+	  }
+	
 	  if (aStr1 > aStr2) {
 	    return 1;
 	  }
@@ -1899,11 +2066,74 @@ var SOURCE_MAP_TEST_MODULE =
 	  return strcmp(mappingA.name, mappingB.name);
 	}
 	exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated;
+	
+	/**
+	 * Strip any JSON XSSI avoidance prefix from the string (as documented
+	 * in the source maps specification), and then parse the string as
+	 * JSON.
+	 */
+	function parseSourceMapInput(str) {
+	  return JSON.parse(str.replace(/^\)]}'[^\n]*\n/, ''));
+	}
+	exports.parseSourceMapInput = parseSourceMapInput;
+	
+	/**
+	 * Compute the URL of a source given the the source root, the source's
+	 * URL, and the source map's URL.
+	 */
+	function computeSourceURL(sourceRoot, sourceURL, sourceMapURL) {
+	  sourceURL = sourceURL || '';
+	
+	  if (sourceRoot) {
+	    // This follows what Chrome does.
+	    if (sourceRoot[sourceRoot.length - 1] !== '/' && sourceURL[0] !== '/') {
+	      sourceRoot += '/';
+	    }
+	    // The spec says:
+	    //   Line 4: An optional source root, useful for relocating source
+	    //   files on a server or removing repeated values in the
+	    //   “sources” entry.  This value is prepended to the individual
+	    //   entries in the “source” field.
+	    sourceURL = sourceRoot + sourceURL;
+	  }
+	
+	  // Historically, SourceMapConsumer did not take the sourceMapURL as
+	  // a parameter.  This mode is still somewhat supported, which is why
+	  // this code block is conditional.  However, it's preferable to pass
+	  // the source map URL to SourceMapConsumer, so that this function
+	  // can implement the source URL resolution algorithm as outlined in
+	  // the spec.  This block is basically the equivalent of:
+	  //    new URL(sourceURL, sourceMapURL).toString()
+	  // ... except it avoids using URL, which wasn't available in the
+	  // older releases of node still supported by this library.
+	  //
+	  // The spec says:
+	  //   If the sources are not absolute URLs after prepending of the
+	  //   “sourceRoot”, the sources are resolved relative to the
+	  //   SourceMap (like resolving script src in a html document).
+	  if (sourceMapURL) {
+	    var parsed = urlParse(sourceMapURL);
+	    if (!parsed) {
+	      throw new Error("sourceMapURL could not be parsed");
+	    }
+	    if (parsed.path) {
+	      // Strip the last path component, but keep the "/".
+	      var index = parsed.path.lastIndexOf('/');
+	      if (index >= 0) {
+	        parsed.path = parsed.path.substring(0, index + 1);
+	      }
+	    }
+	    sourceURL = join(urlGenerate(parsed), sourceURL);
+	  }
+	
+	  return normalize(sourceURL);
+	}
+	exports.computeSourceURL = computeSourceURL;
 
 
-/***/ },
+/***/ }),
 /* 3 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -1918,15 +2148,15 @@ var SOURCE_MAP_TEST_MODULE =
 	var base64VLQ = __webpack_require__(6);
 	var quickSort = __webpack_require__(8).quickSort;
 	
-	function SourceMapConsumer(aSourceMap) {
+	function SourceMapConsumer(aSourceMap, aSourceMapURL) {
 	  var sourceMap = aSourceMap;
 	  if (typeof aSourceMap === 'string') {
-	    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+	    sourceMap = util.parseSourceMapInput(aSourceMap);
 	  }
 	
 	  return sourceMap.sections != null
-	    ? new IndexedSourceMapConsumer(sourceMap)
-	    : new BasicSourceMapConsumer(sourceMap);
+	    ? new IndexedSourceMapConsumer(sourceMap, aSourceMapURL)
+	    : new BasicSourceMapConsumer(sourceMap, aSourceMapURL);
 	}
 	
 	SourceMapConsumer.fromSourceMap = function(aSourceMap) {
@@ -1970,6 +2200,8 @@ var SOURCE_MAP_TEST_MODULE =
 	
 	SourceMapConsumer.prototype.__generatedMappings = null;
 	Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', {
+	  configurable: true,
+	  enumerable: true,
 	  get: function () {
 	    if (!this.__generatedMappings) {
 	      this._parseMappings(this._mappings, this.sourceRoot);
@@ -1981,6 +2213,8 @@ var SOURCE_MAP_TEST_MODULE =
 	
 	SourceMapConsumer.prototype.__originalMappings = null;
 	Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', {
+	  configurable: true,
+	  enumerable: true,
 	  get: function () {
 	    if (!this.__originalMappings) {
 	      this._parseMappings(this._mappings, this.sourceRoot);
@@ -2048,9 +2282,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    var sourceRoot = this.sourceRoot;
 	    mappings.map(function (mapping) {
 	      var source = mapping.source === null ? null : this._sources.at(mapping.source);
-	      if (source != null && sourceRoot != null) {
-	        source = util.join(sourceRoot, source);
-	      }
+	      source = util.computeSourceURL(sourceRoot, source, this._sourceMapURL);
 	      return {
 	        source: source,
 	        generatedLine: mapping.generatedLine,
@@ -2073,13 +2305,16 @@ var SOURCE_MAP_TEST_MODULE =
 	 * The only argument is an object with the following properties:
 	 *
 	 *   - source: The filename of the original source.
-	 *   - line: The line number in the original source.
+	 *   - line: The line number in the original source.  The line number is 1-based.
 	 *   - column: Optional. the column number in the original source.
+	 *    The column number is 0-based.
 	 *
 	 * and an array of objects is returned, each with the following properties:
 	 *
-	 *   - line: The line number in the generated source, or null.
+	 *   - line: The line number in the generated source, or null.  The
+	 *    line number is 1-based.
 	 *   - column: The column number in the generated source, or null.
+	 *    The column number is 0-based.
 	 */
 	SourceMapConsumer.prototype.allGeneratedPositionsFor =
 	  function SourceMapConsumer_allGeneratedPositionsFor(aArgs) {
@@ -2161,7 +2396,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 * query for information about the original file positions by giving it a file
 	 * position in the generated source.
 	 *
-	 * The only parameter is the raw source map (either as a JSON string, or
+	 * The first parameter is the raw source map (either as a JSON string, or
 	 * already parsed to an object). According to the spec, source maps have the
 	 * following attributes:
 	 *
@@ -2184,12 +2419,16 @@ var SOURCE_MAP_TEST_MODULE =
 	 *       mappings: "AA,AB;;ABCDE;"
 	 *     }
 	 *
+	 * The second parameter, if given, is a string whose value is the URL
+	 * at which the source map was found.  This URL is used to compute the
+	 * sources array.
+	 *
 	 * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#
 	 */
-	function BasicSourceMapConsumer(aSourceMap) {
+	function BasicSourceMapConsumer(aSourceMap, aSourceMapURL) {
 	  var sourceMap = aSourceMap;
 	  if (typeof aSourceMap === 'string') {
-	    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+	    sourceMap = util.parseSourceMapInput(aSourceMap);
 	  }
 	
 	  var version = util.getArg(sourceMap, 'version');
@@ -2208,6 +2447,10 @@ var SOURCE_MAP_TEST_MODULE =
 	    throw new Error('Unsupported version: ' + version);
 	  }
 	
+	  if (sourceRoot) {
+	    sourceRoot = util.normalize(sourceRoot);
+	  }
+	
 	  sources = sources
 	    .map(String)
 	    // Some source maps produce relative source paths like "./foo.js" instead of
@@ -2234,6 +2477,7 @@ var SOURCE_MAP_TEST_MODULE =
 	  this.sourceRoot = sourceRoot;
 	  this.sourcesContent = sourcesContent;
 	  this._mappings = mappings;
+	  this._sourceMapURL = aSourceMapURL;
 	  this.file = file;
 	}
 	
@@ -2245,10 +2489,12 @@ var SOURCE_MAP_TEST_MODULE =
 	 *
 	 * @param SourceMapGenerator aSourceMap
 	 *        The source map that will be consumed.
+	 * @param String aSourceMapURL
+	 *        The URL at which the source map can be found (optional)
 	 * @returns BasicSourceMapConsumer
 	 */
 	BasicSourceMapConsumer.fromSourceMap =
-	  function SourceMapConsumer_fromSourceMap(aSourceMap) {
+	  function SourceMapConsumer_fromSourceMap(aSourceMap, aSourceMapURL) {
 	    var smc = Object.create(BasicSourceMapConsumer.prototype);
 	
 	    var names = smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);
@@ -2257,6 +2503,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(),
 	                                                            smc.sourceRoot);
 	    smc.file = aSourceMap._file;
+	    smc._sourceMapURL = aSourceMapURL;
 	
 	    // Because we are modifying the entries (by converting string sources and
 	    // names to indices into the sources and names ArraySets), we have to make
@@ -2304,7 +2551,7 @@ var SOURCE_MAP_TEST_MODULE =
 	Object.defineProperty(BasicSourceMapConsumer.prototype, 'sources', {
 	  get: function () {
 	    return this._sources.toArray().map(function (s) {
-	      return this.sourceRoot != null ? util.join(this.sourceRoot, s) : s;
+	      return util.computeSourceURL(this.sourceRoot, s, this._sourceMapURL);
 	    }, this);
 	  }
 	});
@@ -2486,8 +2733,10 @@ var SOURCE_MAP_TEST_MODULE =
 	 * source's line and column positions provided. The only argument is an object
 	 * with the following properties:
 	 *
-	 *   - line: The line number in the generated source.
-	 *   - column: The column number in the generated source.
+	 *   - line: The line number in the generated source.  The line number
+	 *     is 1-based.
+	 *   - column: The column number in the generated source.  The column
+	 *     number is 0-based.
 	 *   - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or
 	 *     'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the
 	 *     closest element that is smaller than or greater than the one we are
@@ -2497,8 +2746,10 @@ var SOURCE_MAP_TEST_MODULE =
 	 * and an object is returned with the following properties:
 	 *
 	 *   - source: The original source file, or null.
-	 *   - line: The line number in the original source, or null.
-	 *   - column: The column number in the original source, or null.
+	 *   - line: The line number in the original source, or null.  The
+	 *     line number is 1-based.
+	 *   - column: The column number in the original source, or null.  The
+	 *     column number is 0-based.
 	 *   - name: The original identifier, or null.
 	 */
 	BasicSourceMapConsumer.prototype.originalPositionFor =
@@ -2524,9 +2775,7 @@ var SOURCE_MAP_TEST_MODULE =
 	        var source = util.getArg(mapping, 'source', null);
 	        if (source !== null) {
 	          source = this._sources.at(source);
-	          if (this.sourceRoot != null) {
-	            source = util.join(this.sourceRoot, source);
-	          }
+	          source = util.computeSourceURL(this.sourceRoot, source, this._sourceMapURL);
 	        }
 	        var name = util.getArg(mapping, 'name', null);
 	        if (name !== null) {
@@ -2573,12 +2822,23 @@ var SOURCE_MAP_TEST_MODULE =
 	      return null;
 	    }
 	
+	    var relativeSource = aSource;
 	    if (this.sourceRoot != null) {
-	      aSource = util.relative(this.sourceRoot, aSource);
+	      relativeSource = util.relative(this.sourceRoot, relativeSource);
 	    }
 	
-	    if (this._sources.has(aSource)) {
-	      return this.sourcesContent[this._sources.indexOf(aSource)];
+	    if (this._sources.has(relativeSource)) {
+	      return this.sourcesContent[this._sources.indexOf(relativeSource)];
+	    }
+	
+	    // Maybe aSource is an absolute URL as returned by |sources|.  In
+	    // this case we can't simply undo the transform.
+	    var sourceArray = this.sources;
+	    var i;
+	    for (i = 0; i < sourceArray.length; ++i) {
+	      if (sourceArray[i] == aSource) {
+	        return this.sourcesContent[i];
+	      }
 	    }
 	
 	    var url;
@@ -2588,15 +2848,15 @@ var SOURCE_MAP_TEST_MODULE =
 	      // many users. We can help them out when they expect file:// URIs to
 	      // behave like it would if they were running a local HTTP server. See
 	      // https://bugzilla.mozilla.org/show_bug.cgi?id=885597.
-	      var fileUriAbsPath = aSource.replace(/^file:\/\//, "");
+	      var fileUriAbsPath = relativeSource.replace(/^file:\/\//, "");
 	      if (url.scheme == "file"
 	          && this._sources.has(fileUriAbsPath)) {
 	        return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)]
 	      }
 	
 	      if ((!url.path || url.path == "/")
-	          && this._sources.has("/" + aSource)) {
-	        return this.sourcesContent[this._sources.indexOf("/" + aSource)];
+	          && this._sources.has("/" + relativeSource)) {
+	        return this.sourcesContent[this._sources.indexOf("/" + relativeSource)];
 	      }
 	    }
 	
@@ -2608,7 +2868,7 @@ var SOURCE_MAP_TEST_MODULE =
 	      return null;
 	    }
 	    else {
-	      throw new Error('"' + aSource + '" is not in the SourceMap.');
+	      throw new Error('"' + relativeSource + '" is not in the SourceMap.');
 	    }
 	  };
 	
@@ -2618,8 +2878,10 @@ var SOURCE_MAP_TEST_MODULE =
 	 * the following properties:
 	 *
 	 *   - source: The filename of the original source.
-	 *   - line: The line number in the original source.
-	 *   - column: The column number in the original source.
+	 *   - line: The line number in the original source.  The line number
+	 *     is 1-based.
+	 *   - column: The column number in the original source.  The column
+	 *     number is 0-based.
 	 *   - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or
 	 *     'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the
 	 *     closest element that is smaller than or greater than the one we are
@@ -2628,8 +2890,10 @@ var SOURCE_MAP_TEST_MODULE =
 	 *
 	 * and an object is returned with the following properties:
 	 *
-	 *   - line: The line number in the generated source, or null.
+	 *   - line: The line number in the generated source, or null.  The
+	 *     line number is 1-based.
 	 *   - column: The column number in the generated source, or null.
+	 *     The column number is 0-based.
 	 */
 	BasicSourceMapConsumer.prototype.generatedPositionFor =
 	  function SourceMapConsumer_generatedPositionFor(aArgs) {
@@ -2688,7 +2952,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 * that it takes "indexed" source maps (i.e. ones with a "sections" field) as
 	 * input.
 	 *
-	 * The only parameter is a raw source map (either as a JSON string, or already
+	 * The first parameter is a raw source map (either as a JSON string, or already
 	 * parsed to an object). According to the spec for indexed source maps, they
 	 * have the following attributes:
 	 *
@@ -2725,12 +2989,16 @@ var SOURCE_MAP_TEST_MODULE =
 	 *    }],
 	 *  }
 	 *
+	 * The second parameter, if given, is a string whose value is the URL
+	 * at which the source map was found.  This URL is used to compute the
+	 * sources array.
+	 *
 	 * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.535es3xeprgt
 	 */
-	function IndexedSourceMapConsumer(aSourceMap) {
+	function IndexedSourceMapConsumer(aSourceMap, aSourceMapURL) {
 	  var sourceMap = aSourceMap;
 	  if (typeof aSourceMap === 'string') {
-	    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+	    sourceMap = util.parseSourceMapInput(aSourceMap);
 	  }
 	
 	  var version = util.getArg(sourceMap, 'version');
@@ -2770,7 +3038,7 @@ var SOURCE_MAP_TEST_MODULE =
 	        generatedLine: offsetLine + 1,
 	        generatedColumn: offsetColumn + 1
 	      },
-	      consumer: new SourceMapConsumer(util.getArg(s, 'map'))
+	      consumer: new SourceMapConsumer(util.getArg(s, 'map'), aSourceMapURL)
 	    }
 	  });
 	}
@@ -2803,14 +3071,18 @@ var SOURCE_MAP_TEST_MODULE =
 	 * source's line and column positions provided. The only argument is an object
 	 * with the following properties:
 	 *
-	 *   - line: The line number in the generated source.
-	 *   - column: The column number in the generated source.
+	 *   - line: The line number in the generated source.  The line number
+	 *     is 1-based.
+	 *   - column: The column number in the generated source.  The column
+	 *     number is 0-based.
 	 *
 	 * and an object is returned with the following properties:
 	 *
 	 *   - source: The original source file, or null.
-	 *   - line: The line number in the original source, or null.
-	 *   - column: The column number in the original source, or null.
+	 *   - line: The line number in the original source, or null.  The
+	 *     line number is 1-based.
+	 *   - column: The column number in the original source, or null.  The
+	 *     column number is 0-based.
 	 *   - name: The original identifier, or null.
 	 */
 	IndexedSourceMapConsumer.prototype.originalPositionFor =
@@ -2894,13 +3166,17 @@ var SOURCE_MAP_TEST_MODULE =
 	 * the following properties:
 	 *
 	 *   - source: The filename of the original source.
-	 *   - line: The line number in the original source.
-	 *   - column: The column number in the original source.
+	 *   - line: The line number in the original source.  The line number
+	 *     is 1-based.
+	 *   - column: The column number in the original source.  The column
+	 *     number is 0-based.
 	 *
 	 * and an object is returned with the following properties:
 	 *
-	 *   - line: The line number in the generated source, or null.
+	 *   - line: The line number in the generated source, or null.  The
+	 *     line number is 1-based. 
 	 *   - column: The column number in the generated source, or null.
+	 *     The column number is 0-based.
 	 */
 	IndexedSourceMapConsumer.prototype.generatedPositionFor =
 	  function IndexedSourceMapConsumer_generatedPositionFor(aArgs) {
@@ -2948,15 +3224,16 @@ var SOURCE_MAP_TEST_MODULE =
 	        var mapping = sectionMappings[j];
 	
 	        var source = section.consumer._sources.at(mapping.source);
-	        if (section.consumer.sourceRoot !== null) {
-	          source = util.join(section.consumer.sourceRoot, source);
-	        }
+	        source = util.computeSourceURL(section.consumer.sourceRoot, source, this._sourceMapURL);
 	        this._sources.add(source);
 	        source = this._sources.indexOf(source);
 	
-	        var name = section.consumer._names.at(mapping.name);
-	        this._names.add(name);
-	        name = this._names.indexOf(name);
+	        var name = null;
+	        if (mapping.name) {
+	          name = section.consumer._names.at(mapping.name);
+	          this._names.add(name);
+	          name = this._names.indexOf(name);
+	        }
 	
 	        // The mappings coming from the consumer for the section have
 	        // generated positions relative to the start of the section, so we
@@ -2989,9 +3266,9 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer;
 
 
-/***/ },
+/***/ }),
 /* 4 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -3106,9 +3383,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 5 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -3119,6 +3396,7 @@ var SOURCE_MAP_TEST_MODULE =
 	
 	var util = __webpack_require__(2);
 	var has = Object.prototype.hasOwnProperty;
+	var hasNativeMap = typeof Map !== "undefined";
 	
 	/**
 	 * A data structure which is a combination of an array and a set. Adding a new
@@ -3128,7 +3406,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 */
 	function ArraySet() {
 	  this._array = [];
-	  this._set = Object.create(null);
+	  this._set = hasNativeMap ? new Map() : Object.create(null);
 	}
 	
 	/**
@@ -3149,7 +3427,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 * @returns Number
 	 */
 	ArraySet.prototype.size = function ArraySet_size() {
-	  return Object.getOwnPropertyNames(this._set).length;
+	  return hasNativeMap ? this._set.size : Object.getOwnPropertyNames(this._set).length;
 	};
 	
 	/**
@@ -3158,14 +3436,18 @@ var SOURCE_MAP_TEST_MODULE =
 	 * @param String aStr
 	 */
 	ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) {
-	  var sStr = util.toSetString(aStr);
-	  var isDuplicate = has.call(this._set, sStr);
+	  var sStr = hasNativeMap ? aStr : util.toSetString(aStr);
+	  var isDuplicate = hasNativeMap ? this.has(aStr) : has.call(this._set, sStr);
 	  var idx = this._array.length;
 	  if (!isDuplicate || aAllowDuplicates) {
 	    this._array.push(aStr);
 	  }
 	  if (!isDuplicate) {
-	    this._set[sStr] = idx;
+	    if (hasNativeMap) {
+	      this._set.set(aStr, idx);
+	    } else {
+	      this._set[sStr] = idx;
+	    }
 	  }
 	};
 	
@@ -3175,8 +3457,12 @@ var SOURCE_MAP_TEST_MODULE =
 	 * @param String aStr
 	 */
 	ArraySet.prototype.has = function ArraySet_has(aStr) {
-	  var sStr = util.toSetString(aStr);
-	  return has.call(this._set, sStr);
+	  if (hasNativeMap) {
+	    return this._set.has(aStr);
+	  } else {
+	    var sStr = util.toSetString(aStr);
+	    return has.call(this._set, sStr);
+	  }
 	};
 	
 	/**
@@ -3185,10 +3471,18 @@ var SOURCE_MAP_TEST_MODULE =
 	 * @param String aStr
 	 */
 	ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) {
-	  var sStr = util.toSetString(aStr);
-	  if (has.call(this._set, sStr)) {
-	    return this._set[sStr];
+	  if (hasNativeMap) {
+	    var idx = this._set.get(aStr);
+	    if (idx >= 0) {
+	        return idx;
+	    }
+	  } else {
+	    var sStr = util.toSetString(aStr);
+	    if (has.call(this._set, sStr)) {
+	      return this._set[sStr];
+	    }
 	  }
+	
 	  throw new Error('"' + aStr + '" is not in the set.');
 	};
 	
@@ -3216,9 +3510,9 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.ArraySet = ArraySet;
 
 
-/***/ },
+/***/ }),
 /* 6 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -3362,9 +3656,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 7 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -3435,9 +3729,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 8 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -3555,9 +3849,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 9 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -3633,6 +3927,15 @@ var SOURCE_MAP_TEST_MODULE =
 	      generator.addMapping(newMapping);
 	    });
 	    aSourceMapConsumer.sources.forEach(function (sourceFile) {
+	      var sourceRelative = sourceFile;
+	      if (sourceRoot !== null) {
+	        sourceRelative = util.relative(sourceRoot, sourceFile);
+	      }
+	
+	      if (!generator._sources.has(sourceRelative)) {
+	        generator._sources.add(sourceRelative);
+	      }
+	
 	      var content = aSourceMapConsumer.sourceContentFor(sourceFile);
 	      if (content != null) {
 	        generator.setSourceContent(sourceFile, content);
@@ -3820,6 +4123,18 @@ var SOURCE_MAP_TEST_MODULE =
 	SourceMapGenerator.prototype._validateMapping =
 	  function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource,
 	                                              aName) {
+	    // When aOriginal is truthy but has empty values for .line and .column,
+	    // it is most likely a programmer error. In this case we throw a very
+	    // specific error message to try to guide them the right way.
+	    // For example: https://github.com/Polymer/polymer-bundler/pull/519
+	    if (aOriginal && typeof aOriginal.line !== 'number' && typeof aOriginal.column !== 'number') {
+	        throw new Error(
+	            'original.line and original.column are not numbers -- you probably meant to omit ' +
+	            'the original mapping entirely and only map the generated position. If so, pass ' +
+	            'null for the original mapping instead of an object with empty or null values.'
+	        );
+	    }
+	
 	    if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
 	        && aGenerated.line > 0 && aGenerated.column >= 0
 	        && !aOriginal && !aSource && !aName) {
@@ -3965,9 +4280,9 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.SourceMapGenerator = SourceMapGenerator;
 
 
-/***/ },
+/***/ }),
 /* 10 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -4050,6 +4365,6 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.MappingList = MappingList;
 
 
-/***/ }
+/***/ })
 /******/ ]);
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAgYjRjM2ZlYjNiNDMxZjA2NmYwNWMiLCJ3ZWJwYWNrOi8vLy4vdGVzdC90ZXN0LXNvdXJjZS1tYXAtY29uc3VtZXIuanMiLCJ3ZWJwYWNrOi8vLy4vdGVzdC91dGlsLmpzIiwid2VicGFjazovLy8uL2xpYi91dGlsLmpzIiwid2VicGFjazovLy8uL2xpYi9zb3VyY2UtbWFwLWNvbnN1bWVyLmpzIiwid2VicGFjazovLy8uL2xpYi9iaW5hcnktc2VhcmNoLmpzIiwid2VicGFjazovLy8uL2xpYi9hcnJheS1zZXQuanMiLCJ3ZWJwYWNrOi8vLy4vbGliL2Jhc2U2NC12bHEuanMiLCJ3ZWJwYWNrOi [...]
\ No newline at end of file
+//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAgNTg4ZmJiMWFlNGU1MDljNjhlMTkiLCJ3ZWJwYWNrOi8vLy4vdGVzdC90ZXN0LXNvdXJjZS1tYXAtY29uc3VtZXIuanMiLCJ3ZWJwYWNrOi8vLy4vdGVzdC91dGlsLmpzIiwid2VicGFjazovLy8uL2xpYi91dGlsLmpzIiwid2VicGFjazovLy8uL2xpYi9zb3VyY2UtbWFwLWNvbnN1bWVyLmpzIiwid2VicGFjazovLy8uL2xpYi9iaW5hcnktc2VhcmNoLmpzIiwid2VicGFjazovLy8uL2xpYi9hcnJheS1zZXQuanMiLCJ3ZWJwYWNrOi8vLy4vbGliL2Jhc2U2NC12bHEuanMi [...]
\ No newline at end of file
diff --git a/dist/test/test_source_map_generator.js b/dist/test/test_source_map_generator.js
index f93828a..a914ae9 100644
--- a/dist/test/test_source_map_generator.js
+++ b/dist/test/test_source_map_generator.js
@@ -52,7 +52,7 @@ var SOURCE_MAP_TEST_MODULE =
 /************************************************************************/
 /******/ ([
 /* 0 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -308,6 +308,30 @@ var SOURCE_MAP_TEST_MODULE =
 	  util.assertEqualMaps(assert, map.toJSON(), util.testMapWithSourcesContent);
 	};
 	
+	exports['test .fromSourceMap with single source'] = function (assert) {
+	  var map = SourceMapGenerator.fromSourceMap(
+	      new SourceMapConsumer(util.testMapSingleSource));
+	  util.assertEqualMaps(assert, map.toJSON(), util.testMapSingleSource);
+	};
+	
+	exports['test .fromSourceMap with empty mappings'] = function (assert) {
+	  var map = SourceMapGenerator.fromSourceMap(
+	    new SourceMapConsumer(util.testMapEmptyMappings));
+	  util.assertEqualMaps(assert, map.toJSON(), util.testMapEmptyMappings);
+	};
+	
+	exports['test .fromSourceMap with empty mappings and relative sources'] = function (assert) {
+	  var map = SourceMapGenerator.fromSourceMap(
+	    new SourceMapConsumer(util.testMapEmptyMappingsRelativeSources));
+	  util.assertEqualMaps(assert, map.toJSON(), util.testMapEmptyMappingsRelativeSources_generatedExpected);
+	};
+	
+	exports['test .fromSourceMap with multiple sources where mappings refers only to single source'] = function (assert) {
+	    var map = SourceMapGenerator.fromSourceMap(
+	        new SourceMapConsumer(util.testMapMultiSourcesMappingRefersSingleSourceOnly));
+	    util.assertEqualMaps(assert, map.toJSON(), util.testMapMultiSourcesMappingRefersSingleSourceOnly);
+	};
+	
 	exports['test applySourceMap'] = function (assert) {
 	  var node = new SourceNode(null, null, null, [
 	    new SourceNode(2, 0, 'fileX', 'lineX2\n'),
@@ -810,9 +834,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 1 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -888,6 +912,15 @@ var SOURCE_MAP_TEST_MODULE =
 	      generator.addMapping(newMapping);
 	    });
 	    aSourceMapConsumer.sources.forEach(function (sourceFile) {
+	      var sourceRelative = sourceFile;
+	      if (sourceRoot !== null) {
+	        sourceRelative = util.relative(sourceRoot, sourceFile);
+	      }
+	
+	      if (!generator._sources.has(sourceRelative)) {
+	        generator._sources.add(sourceRelative);
+	      }
+	
 	      var content = aSourceMapConsumer.sourceContentFor(sourceFile);
 	      if (content != null) {
 	        generator.setSourceContent(sourceFile, content);
@@ -1075,6 +1108,18 @@ var SOURCE_MAP_TEST_MODULE =
 	SourceMapGenerator.prototype._validateMapping =
 	  function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource,
 	                                              aName) {
+	    // When aOriginal is truthy but has empty values for .line and .column,
+	    // it is most likely a programmer error. In this case we throw a very
+	    // specific error message to try to guide them the right way.
+	    // For example: https://github.com/Polymer/polymer-bundler/pull/519
+	    if (aOriginal && typeof aOriginal.line !== 'number' && typeof aOriginal.column !== 'number') {
+	        throw new Error(
+	            'original.line and original.column are not numbers -- you probably meant to omit ' +
+	            'the original mapping entirely and only map the generated position. If so, pass ' +
+	            'null for the original mapping instead of an object with empty or null values.'
+	        );
+	    }
+	
 	    if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
 	        && aGenerated.line > 0 && aGenerated.column >= 0
 	        && !aOriginal && !aSource && !aName) {
@@ -1220,9 +1265,9 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.SourceMapGenerator = SourceMapGenerator;
 
 
-/***/ },
+/***/ }),
 /* 2 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -1366,9 +1411,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 3 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -1439,9 +1484,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 4 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -1471,7 +1516,7 @@ var SOURCE_MAP_TEST_MODULE =
 	}
 	exports.getArg = getArg;
 	
-	var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/;
+	var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/;
 	var dataUrlRegexp = /^data:.+\,.+$/;
 	
 	function urlParse(aUrl) {
@@ -1514,7 +1559,7 @@ var SOURCE_MAP_TEST_MODULE =
 	/**
 	 * Normalizes a path, or the path portion of a URL:
 	 *
-	 * - Replaces consequtive slashes with one slash.
+	 * - Replaces consecutive slashes with one slash.
 	 * - Removes unnecessary '.' parts.
 	 * - Removes unnecessary '<dir>/..' parts.
 	 *
@@ -1627,7 +1672,7 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.join = join;
 	
 	exports.isAbsolute = function (aPath) {
-	  return aPath.charAt(0) === '/' || !!aPath.match(urlRegexp);
+	  return aPath.charAt(0) === '/' || urlRegexp.test(aPath);
 	};
 	
 	/**
@@ -1747,7 +1792,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 * stubbed out mapping.
 	 */
 	function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
-	  var cmp = mappingA.source - mappingB.source;
+	  var cmp = strcmp(mappingA.source, mappingB.source);
 	  if (cmp !== 0) {
 	    return cmp;
 	  }
@@ -1772,7 +1817,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    return cmp;
 	  }
 	
-	  return mappingA.name - mappingB.name;
+	  return strcmp(mappingA.name, mappingB.name);
 	}
 	exports.compareByOriginalPositions = compareByOriginalPositions;
 	
@@ -1796,7 +1841,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    return cmp;
 	  }
 	
-	  cmp = mappingA.source - mappingB.source;
+	  cmp = strcmp(mappingA.source, mappingB.source);
 	  if (cmp !== 0) {
 	    return cmp;
 	  }
@@ -1811,7 +1856,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    return cmp;
 	  }
 	
-	  return mappingA.name - mappingB.name;
+	  return strcmp(mappingA.name, mappingB.name);
 	}
 	exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated;
 	
@@ -1820,6 +1865,14 @@ var SOURCE_MAP_TEST_MODULE =
 	    return 0;
 	  }
 	
+	  if (aStr1 === null) {
+	    return 1; // aStr2 !== null
+	  }
+	
+	  if (aStr2 === null) {
+	    return -1; // aStr1 !== null
+	  }
+	
 	  if (aStr1 > aStr2) {
 	    return 1;
 	  }
@@ -1860,11 +1913,74 @@ var SOURCE_MAP_TEST_MODULE =
 	  return strcmp(mappingA.name, mappingB.name);
 	}
 	exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated;
+	
+	/**
+	 * Strip any JSON XSSI avoidance prefix from the string (as documented
+	 * in the source maps specification), and then parse the string as
+	 * JSON.
+	 */
+	function parseSourceMapInput(str) {
+	  return JSON.parse(str.replace(/^\)]}'[^\n]*\n/, ''));
+	}
+	exports.parseSourceMapInput = parseSourceMapInput;
+	
+	/**
+	 * Compute the URL of a source given the the source root, the source's
+	 * URL, and the source map's URL.
+	 */
+	function computeSourceURL(sourceRoot, sourceURL, sourceMapURL) {
+	  sourceURL = sourceURL || '';
+	
+	  if (sourceRoot) {
+	    // This follows what Chrome does.
+	    if (sourceRoot[sourceRoot.length - 1] !== '/' && sourceURL[0] !== '/') {
+	      sourceRoot += '/';
+	    }
+	    // The spec says:
+	    //   Line 4: An optional source root, useful for relocating source
+	    //   files on a server or removing repeated values in the
+	    //   “sources” entry.  This value is prepended to the individual
+	    //   entries in the “source” field.
+	    sourceURL = sourceRoot + sourceURL;
+	  }
+	
+	  // Historically, SourceMapConsumer did not take the sourceMapURL as
+	  // a parameter.  This mode is still somewhat supported, which is why
+	  // this code block is conditional.  However, it's preferable to pass
+	  // the source map URL to SourceMapConsumer, so that this function
+	  // can implement the source URL resolution algorithm as outlined in
+	  // the spec.  This block is basically the equivalent of:
+	  //    new URL(sourceURL, sourceMapURL).toString()
+	  // ... except it avoids using URL, which wasn't available in the
+	  // older releases of node still supported by this library.
+	  //
+	  // The spec says:
+	  //   If the sources are not absolute URLs after prepending of the
+	  //   “sourceRoot”, the sources are resolved relative to the
+	  //   SourceMap (like resolving script src in a html document).
+	  if (sourceMapURL) {
+	    var parsed = urlParse(sourceMapURL);
+	    if (!parsed) {
+	      throw new Error("sourceMapURL could not be parsed");
+	    }
+	    if (parsed.path) {
+	      // Strip the last path component, but keep the "/".
+	      var index = parsed.path.lastIndexOf('/');
+	      if (index >= 0) {
+	        parsed.path = parsed.path.substring(0, index + 1);
+	      }
+	    }
+	    sourceURL = join(urlGenerate(parsed), sourceURL);
+	  }
+	
+	  return normalize(sourceURL);
+	}
+	exports.computeSourceURL = computeSourceURL;
 
 
-/***/ },
+/***/ }),
 /* 5 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -1875,6 +1991,7 @@ var SOURCE_MAP_TEST_MODULE =
 	
 	var util = __webpack_require__(4);
 	var has = Object.prototype.hasOwnProperty;
+	var hasNativeMap = typeof Map !== "undefined";
 	
 	/**
 	 * A data structure which is a combination of an array and a set. Adding a new
@@ -1884,7 +2001,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 */
 	function ArraySet() {
 	  this._array = [];
-	  this._set = Object.create(null);
+	  this._set = hasNativeMap ? new Map() : Object.create(null);
 	}
 	
 	/**
@@ -1905,7 +2022,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 * @returns Number
 	 */
 	ArraySet.prototype.size = function ArraySet_size() {
-	  return Object.getOwnPropertyNames(this._set).length;
+	  return hasNativeMap ? this._set.size : Object.getOwnPropertyNames(this._set).length;
 	};
 	
 	/**
@@ -1914,14 +2031,18 @@ var SOURCE_MAP_TEST_MODULE =
 	 * @param String aStr
 	 */
 	ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) {
-	  var sStr = util.toSetString(aStr);
-	  var isDuplicate = has.call(this._set, sStr);
+	  var sStr = hasNativeMap ? aStr : util.toSetString(aStr);
+	  var isDuplicate = hasNativeMap ? this.has(aStr) : has.call(this._set, sStr);
 	  var idx = this._array.length;
 	  if (!isDuplicate || aAllowDuplicates) {
 	    this._array.push(aStr);
 	  }
 	  if (!isDuplicate) {
-	    this._set[sStr] = idx;
+	    if (hasNativeMap) {
+	      this._set.set(aStr, idx);
+	    } else {
+	      this._set[sStr] = idx;
+	    }
 	  }
 	};
 	
@@ -1931,8 +2052,12 @@ var SOURCE_MAP_TEST_MODULE =
 	 * @param String aStr
 	 */
 	ArraySet.prototype.has = function ArraySet_has(aStr) {
-	  var sStr = util.toSetString(aStr);
-	  return has.call(this._set, sStr);
+	  if (hasNativeMap) {
+	    return this._set.has(aStr);
+	  } else {
+	    var sStr = util.toSetString(aStr);
+	    return has.call(this._set, sStr);
+	  }
 	};
 	
 	/**
@@ -1941,10 +2066,18 @@ var SOURCE_MAP_TEST_MODULE =
 	 * @param String aStr
 	 */
 	ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) {
-	  var sStr = util.toSetString(aStr);
-	  if (has.call(this._set, sStr)) {
-	    return this._set[sStr];
+	  if (hasNativeMap) {
+	    var idx = this._set.get(aStr);
+	    if (idx >= 0) {
+	        return idx;
+	    }
+	  } else {
+	    var sStr = util.toSetString(aStr);
+	    if (has.call(this._set, sStr)) {
+	      return this._set[sStr];
+	    }
 	  }
+	
 	  throw new Error('"' + aStr + '" is not in the set.');
 	};
 	
@@ -1972,9 +2105,9 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.ArraySet = ArraySet;
 
 
-/***/ },
+/***/ }),
 /* 6 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -2057,9 +2190,9 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.MappingList = MappingList;
 
 
-/***/ },
+/***/ }),
 /* 7 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -2074,15 +2207,15 @@ var SOURCE_MAP_TEST_MODULE =
 	var base64VLQ = __webpack_require__(2);
 	var quickSort = __webpack_require__(9).quickSort;
 	
-	function SourceMapConsumer(aSourceMap) {
+	function SourceMapConsumer(aSourceMap, aSourceMapURL) {
 	  var sourceMap = aSourceMap;
 	  if (typeof aSourceMap === 'string') {
-	    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+	    sourceMap = util.parseSourceMapInput(aSourceMap);
 	  }
 	
 	  return sourceMap.sections != null
-	    ? new IndexedSourceMapConsumer(sourceMap)
-	    : new BasicSourceMapConsumer(sourceMap);
+	    ? new IndexedSourceMapConsumer(sourceMap, aSourceMapURL)
+	    : new BasicSourceMapConsumer(sourceMap, aSourceMapURL);
 	}
 	
 	SourceMapConsumer.fromSourceMap = function(aSourceMap) {
@@ -2126,6 +2259,8 @@ var SOURCE_MAP_TEST_MODULE =
 	
 	SourceMapConsumer.prototype.__generatedMappings = null;
 	Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', {
+	  configurable: true,
+	  enumerable: true,
 	  get: function () {
 	    if (!this.__generatedMappings) {
 	      this._parseMappings(this._mappings, this.sourceRoot);
@@ -2137,6 +2272,8 @@ var SOURCE_MAP_TEST_MODULE =
 	
 	SourceMapConsumer.prototype.__originalMappings = null;
 	Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', {
+	  configurable: true,
+	  enumerable: true,
 	  get: function () {
 	    if (!this.__originalMappings) {
 	      this._parseMappings(this._mappings, this.sourceRoot);
@@ -2204,9 +2341,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    var sourceRoot = this.sourceRoot;
 	    mappings.map(function (mapping) {
 	      var source = mapping.source === null ? null : this._sources.at(mapping.source);
-	      if (source != null && sourceRoot != null) {
-	        source = util.join(sourceRoot, source);
-	      }
+	      source = util.computeSourceURL(sourceRoot, source, this._sourceMapURL);
 	      return {
 	        source: source,
 	        generatedLine: mapping.generatedLine,
@@ -2229,13 +2364,16 @@ var SOURCE_MAP_TEST_MODULE =
 	 * The only argument is an object with the following properties:
 	 *
 	 *   - source: The filename of the original source.
-	 *   - line: The line number in the original source.
+	 *   - line: The line number in the original source.  The line number is 1-based.
 	 *   - column: Optional. the column number in the original source.
+	 *    The column number is 0-based.
 	 *
 	 * and an array of objects is returned, each with the following properties:
 	 *
-	 *   - line: The line number in the generated source, or null.
+	 *   - line: The line number in the generated source, or null.  The
+	 *    line number is 1-based.
 	 *   - column: The column number in the generated source, or null.
+	 *    The column number is 0-based.
 	 */
 	SourceMapConsumer.prototype.allGeneratedPositionsFor =
 	  function SourceMapConsumer_allGeneratedPositionsFor(aArgs) {
@@ -2317,7 +2455,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 * query for information about the original file positions by giving it a file
 	 * position in the generated source.
 	 *
-	 * The only parameter is the raw source map (either as a JSON string, or
+	 * The first parameter is the raw source map (either as a JSON string, or
 	 * already parsed to an object). According to the spec, source maps have the
 	 * following attributes:
 	 *
@@ -2340,12 +2478,16 @@ var SOURCE_MAP_TEST_MODULE =
 	 *       mappings: "AA,AB;;ABCDE;"
 	 *     }
 	 *
+	 * The second parameter, if given, is a string whose value is the URL
+	 * at which the source map was found.  This URL is used to compute the
+	 * sources array.
+	 *
 	 * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#
 	 */
-	function BasicSourceMapConsumer(aSourceMap) {
+	function BasicSourceMapConsumer(aSourceMap, aSourceMapURL) {
 	  var sourceMap = aSourceMap;
 	  if (typeof aSourceMap === 'string') {
-	    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+	    sourceMap = util.parseSourceMapInput(aSourceMap);
 	  }
 	
 	  var version = util.getArg(sourceMap, 'version');
@@ -2364,6 +2506,10 @@ var SOURCE_MAP_TEST_MODULE =
 	    throw new Error('Unsupported version: ' + version);
 	  }
 	
+	  if (sourceRoot) {
+	    sourceRoot = util.normalize(sourceRoot);
+	  }
+	
 	  sources = sources
 	    .map(String)
 	    // Some source maps produce relative source paths like "./foo.js" instead of
@@ -2390,6 +2536,7 @@ var SOURCE_MAP_TEST_MODULE =
 	  this.sourceRoot = sourceRoot;
 	  this.sourcesContent = sourcesContent;
 	  this._mappings = mappings;
+	  this._sourceMapURL = aSourceMapURL;
 	  this.file = file;
 	}
 	
@@ -2401,10 +2548,12 @@ var SOURCE_MAP_TEST_MODULE =
 	 *
 	 * @param SourceMapGenerator aSourceMap
 	 *        The source map that will be consumed.
+	 * @param String aSourceMapURL
+	 *        The URL at which the source map can be found (optional)
 	 * @returns BasicSourceMapConsumer
 	 */
 	BasicSourceMapConsumer.fromSourceMap =
-	  function SourceMapConsumer_fromSourceMap(aSourceMap) {
+	  function SourceMapConsumer_fromSourceMap(aSourceMap, aSourceMapURL) {
 	    var smc = Object.create(BasicSourceMapConsumer.prototype);
 	
 	    var names = smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);
@@ -2413,6 +2562,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(),
 	                                                            smc.sourceRoot);
 	    smc.file = aSourceMap._file;
+	    smc._sourceMapURL = aSourceMapURL;
 	
 	    // Because we are modifying the entries (by converting string sources and
 	    // names to indices into the sources and names ArraySets), we have to make
@@ -2460,7 +2610,7 @@ var SOURCE_MAP_TEST_MODULE =
 	Object.defineProperty(BasicSourceMapConsumer.prototype, 'sources', {
 	  get: function () {
 	    return this._sources.toArray().map(function (s) {
-	      return this.sourceRoot != null ? util.join(this.sourceRoot, s) : s;
+	      return util.computeSourceURL(this.sourceRoot, s, this._sourceMapURL);
 	    }, this);
 	  }
 	});
@@ -2642,8 +2792,10 @@ var SOURCE_MAP_TEST_MODULE =
 	 * source's line and column positions provided. The only argument is an object
 	 * with the following properties:
 	 *
-	 *   - line: The line number in the generated source.
-	 *   - column: The column number in the generated source.
+	 *   - line: The line number in the generated source.  The line number
+	 *     is 1-based.
+	 *   - column: The column number in the generated source.  The column
+	 *     number is 0-based.
 	 *   - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or
 	 *     'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the
 	 *     closest element that is smaller than or greater than the one we are
@@ -2653,8 +2805,10 @@ var SOURCE_MAP_TEST_MODULE =
 	 * and an object is returned with the following properties:
 	 *
 	 *   - source: The original source file, or null.
-	 *   - line: The line number in the original source, or null.
-	 *   - column: The column number in the original source, or null.
+	 *   - line: The line number in the original source, or null.  The
+	 *     line number is 1-based.
+	 *   - column: The column number in the original source, or null.  The
+	 *     column number is 0-based.
 	 *   - name: The original identifier, or null.
 	 */
 	BasicSourceMapConsumer.prototype.originalPositionFor =
@@ -2680,9 +2834,7 @@ var SOURCE_MAP_TEST_MODULE =
 	        var source = util.getArg(mapping, 'source', null);
 	        if (source !== null) {
 	          source = this._sources.at(source);
-	          if (this.sourceRoot != null) {
-	            source = util.join(this.sourceRoot, source);
-	          }
+	          source = util.computeSourceURL(this.sourceRoot, source, this._sourceMapURL);
 	        }
 	        var name = util.getArg(mapping, 'name', null);
 	        if (name !== null) {
@@ -2729,12 +2881,23 @@ var SOURCE_MAP_TEST_MODULE =
 	      return null;
 	    }
 	
+	    var relativeSource = aSource;
 	    if (this.sourceRoot != null) {
-	      aSource = util.relative(this.sourceRoot, aSource);
+	      relativeSource = util.relative(this.sourceRoot, relativeSource);
 	    }
 	
-	    if (this._sources.has(aSource)) {
-	      return this.sourcesContent[this._sources.indexOf(aSource)];
+	    if (this._sources.has(relativeSource)) {
+	      return this.sourcesContent[this._sources.indexOf(relativeSource)];
+	    }
+	
+	    // Maybe aSource is an absolute URL as returned by |sources|.  In
+	    // this case we can't simply undo the transform.
+	    var sourceArray = this.sources;
+	    var i;
+	    for (i = 0; i < sourceArray.length; ++i) {
+	      if (sourceArray[i] == aSource) {
+	        return this.sourcesContent[i];
+	      }
 	    }
 	
 	    var url;
@@ -2744,15 +2907,15 @@ var SOURCE_MAP_TEST_MODULE =
 	      // many users. We can help them out when they expect file:// URIs to
 	      // behave like it would if they were running a local HTTP server. See
 	      // https://bugzilla.mozilla.org/show_bug.cgi?id=885597.
-	      var fileUriAbsPath = aSource.replace(/^file:\/\//, "");
+	      var fileUriAbsPath = relativeSource.replace(/^file:\/\//, "");
 	      if (url.scheme == "file"
 	          && this._sources.has(fileUriAbsPath)) {
 	        return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)]
 	      }
 	
 	      if ((!url.path || url.path == "/")
-	          && this._sources.has("/" + aSource)) {
-	        return this.sourcesContent[this._sources.indexOf("/" + aSource)];
+	          && this._sources.has("/" + relativeSource)) {
+	        return this.sourcesContent[this._sources.indexOf("/" + relativeSource)];
 	      }
 	    }
 	
@@ -2764,7 +2927,7 @@ var SOURCE_MAP_TEST_MODULE =
 	      return null;
 	    }
 	    else {
-	      throw new Error('"' + aSource + '" is not in the SourceMap.');
+	      throw new Error('"' + relativeSource + '" is not in the SourceMap.');
 	    }
 	  };
 	
@@ -2774,8 +2937,10 @@ var SOURCE_MAP_TEST_MODULE =
 	 * the following properties:
 	 *
 	 *   - source: The filename of the original source.
-	 *   - line: The line number in the original source.
-	 *   - column: The column number in the original source.
+	 *   - line: The line number in the original source.  The line number
+	 *     is 1-based.
+	 *   - column: The column number in the original source.  The column
+	 *     number is 0-based.
 	 *   - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or
 	 *     'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the
 	 *     closest element that is smaller than or greater than the one we are
@@ -2784,8 +2949,10 @@ var SOURCE_MAP_TEST_MODULE =
 	 *
 	 * and an object is returned with the following properties:
 	 *
-	 *   - line: The line number in the generated source, or null.
+	 *   - line: The line number in the generated source, or null.  The
+	 *     line number is 1-based.
 	 *   - column: The column number in the generated source, or null.
+	 *     The column number is 0-based.
 	 */
 	BasicSourceMapConsumer.prototype.generatedPositionFor =
 	  function SourceMapConsumer_generatedPositionFor(aArgs) {
@@ -2844,7 +3011,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 * that it takes "indexed" source maps (i.e. ones with a "sections" field) as
 	 * input.
 	 *
-	 * The only parameter is a raw source map (either as a JSON string, or already
+	 * The first parameter is a raw source map (either as a JSON string, or already
 	 * parsed to an object). According to the spec for indexed source maps, they
 	 * have the following attributes:
 	 *
@@ -2881,12 +3048,16 @@ var SOURCE_MAP_TEST_MODULE =
 	 *    }],
 	 *  }
 	 *
+	 * The second parameter, if given, is a string whose value is the URL
+	 * at which the source map was found.  This URL is used to compute the
+	 * sources array.
+	 *
 	 * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.535es3xeprgt
 	 */
-	function IndexedSourceMapConsumer(aSourceMap) {
+	function IndexedSourceMapConsumer(aSourceMap, aSourceMapURL) {
 	  var sourceMap = aSourceMap;
 	  if (typeof aSourceMap === 'string') {
-	    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+	    sourceMap = util.parseSourceMapInput(aSourceMap);
 	  }
 	
 	  var version = util.getArg(sourceMap, 'version');
@@ -2926,7 +3097,7 @@ var SOURCE_MAP_TEST_MODULE =
 	        generatedLine: offsetLine + 1,
 	        generatedColumn: offsetColumn + 1
 	      },
-	      consumer: new SourceMapConsumer(util.getArg(s, 'map'))
+	      consumer: new SourceMapConsumer(util.getArg(s, 'map'), aSourceMapURL)
 	    }
 	  });
 	}
@@ -2959,14 +3130,18 @@ var SOURCE_MAP_TEST_MODULE =
 	 * source's line and column positions provided. The only argument is an object
 	 * with the following properties:
 	 *
-	 *   - line: The line number in the generated source.
-	 *   - column: The column number in the generated source.
+	 *   - line: The line number in the generated source.  The line number
+	 *     is 1-based.
+	 *   - column: The column number in the generated source.  The column
+	 *     number is 0-based.
 	 *
 	 * and an object is returned with the following properties:
 	 *
 	 *   - source: The original source file, or null.
-	 *   - line: The line number in the original source, or null.
-	 *   - column: The column number in the original source, or null.
+	 *   - line: The line number in the original source, or null.  The
+	 *     line number is 1-based.
+	 *   - column: The column number in the original source, or null.  The
+	 *     column number is 0-based.
 	 *   - name: The original identifier, or null.
 	 */
 	IndexedSourceMapConsumer.prototype.originalPositionFor =
@@ -3050,13 +3225,17 @@ var SOURCE_MAP_TEST_MODULE =
 	 * the following properties:
 	 *
 	 *   - source: The filename of the original source.
-	 *   - line: The line number in the original source.
-	 *   - column: The column number in the original source.
+	 *   - line: The line number in the original source.  The line number
+	 *     is 1-based.
+	 *   - column: The column number in the original source.  The column
+	 *     number is 0-based.
 	 *
 	 * and an object is returned with the following properties:
 	 *
-	 *   - line: The line number in the generated source, or null.
+	 *   - line: The line number in the generated source, or null.  The
+	 *     line number is 1-based. 
 	 *   - column: The column number in the generated source, or null.
+	 *     The column number is 0-based.
 	 */
 	IndexedSourceMapConsumer.prototype.generatedPositionFor =
 	  function IndexedSourceMapConsumer_generatedPositionFor(aArgs) {
@@ -3104,15 +3283,16 @@ var SOURCE_MAP_TEST_MODULE =
 	        var mapping = sectionMappings[j];
 	
 	        var source = section.consumer._sources.at(mapping.source);
-	        if (section.consumer.sourceRoot !== null) {
-	          source = util.join(section.consumer.sourceRoot, source);
-	        }
+	        source = util.computeSourceURL(section.consumer.sourceRoot, source, this._sourceMapURL);
 	        this._sources.add(source);
 	        source = this._sources.indexOf(source);
 	
-	        var name = section.consumer._names.at(mapping.name);
-	        this._names.add(name);
-	        name = this._names.indexOf(name);
+	        var name = null;
+	        if (mapping.name) {
+	          name = section.consumer._names.at(mapping.name);
+	          this._names.add(name);
+	          name = this._names.indexOf(name);
+	        }
 	
 	        // The mappings coming from the consumer for the section have
 	        // generated positions relative to the start of the section, so we
@@ -3145,9 +3325,9 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer;
 
 
-/***/ },
+/***/ }),
 /* 8 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -3262,9 +3442,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 9 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -3382,9 +3562,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 10 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -3448,13 +3628,19 @@ var SOURCE_MAP_TEST_MODULE =
 	    // 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`.
+	    // Processed fragments are accessed by calling `shiftNextLine`.
 	    var remainingLines = aGeneratedCode.split(REGEX_NEWLINE);
+	    var remainingLinesIndex = 0;
 	    var shiftNextLine = function() {
-	      var lineContents = remainingLines.shift();
+	      var lineContents = getNextLine();
 	      // The last line of a file might not have a newline.
-	      var newLine = remainingLines.shift() || "";
+	      var newLine = getNextLine() || "";
 	      return lineContents + newLine;
+	
+	      function getNextLine() {
+	        return remainingLinesIndex < remainingLines.length ?
+	            remainingLines[remainingLinesIndex++] : undefined;
+	      }
 	    };
 	
 	    // We need to remember the position of "remainingLines"
@@ -3479,10 +3665,10 @@ var SOURCE_MAP_TEST_MODULE =
 	          // There is no new line in between.
 	          // Associate the code between "lastGeneratedColumn" and
 	          // "mapping.generatedColumn" with "lastMapping"
-	          var nextLine = remainingLines[0];
+	          var nextLine = remainingLines[remainingLinesIndex] || '';
 	          var code = nextLine.substr(0, mapping.generatedColumn -
 	                                        lastGeneratedColumn);
-	          remainingLines[0] = nextLine.substr(mapping.generatedColumn -
+	          remainingLines[remainingLinesIndex] = nextLine.substr(mapping.generatedColumn -
 	                                              lastGeneratedColumn);
 	          lastGeneratedColumn = mapping.generatedColumn;
 	          addMappingWithCode(lastMapping, code);
@@ -3499,21 +3685,21 @@ var SOURCE_MAP_TEST_MODULE =
 	        lastGeneratedLine++;
 	      }
 	      if (lastGeneratedColumn < mapping.generatedColumn) {
-	        var nextLine = remainingLines[0];
+	        var nextLine = remainingLines[remainingLinesIndex] || '';
 	        node.add(nextLine.substr(0, mapping.generatedColumn));
-	        remainingLines[0] = nextLine.substr(mapping.generatedColumn);
+	        remainingLines[remainingLinesIndex] = nextLine.substr(mapping.generatedColumn);
 	        lastGeneratedColumn = mapping.generatedColumn;
 	      }
 	      lastMapping = mapping;
 	    }, this);
 	    // We have processed all mappings.
-	    if (remainingLines.length > 0) {
+	    if (remainingLinesIndex < remainingLines.length) {
 	      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(""));
+	      node.add(remainingLines.splice(remainingLinesIndex).join(""));
 	    }
 	
 	    // Copy sourcesContent into SourceNode
@@ -3795,9 +3981,9 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.SourceNode = SourceNode;
 
 
-/***/ },
+/***/ }),
 /* 11 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -3852,6 +4038,58 @@ var SOURCE_MAP_TEST_MODULE =
 	  sourceRoot: '',
 	  mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
 	};
+	exports.testMapSingleSource = {
+	  version: 3,
+	  file: 'min.js',
+	  names: ['bar', 'baz'],
+	  sources: ['one.js'],
+	  sourceRoot: '',
+	  mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID'
+	};
+	exports.testMapEmptyMappings = {
+	  version: 3,
+	  file: 'min.js',
+	  names: [],
+	  sources: ['one.js', 'two.js'],
+	  sourcesContent: [
+	    ' ONE.foo = 1;',
+	    ' TWO.inc = 2;'
+	  ],
+	  sourceRoot: '',
+	  mappings: ''
+	};
+	exports.testMapEmptyMappingsRelativeSources = {
+	  version: 3,
+	  file: 'min.js',
+	  names: [],
+	  sources: ['./one.js', './two.js'],
+	  sourcesContent: [
+	    ' ONE.foo = 1;',
+	    ' TWO.inc = 2;'
+	  ],
+	  sourceRoot: '/the/root',
+	  mappings: ''
+	};
+	exports.testMapEmptyMappingsRelativeSources_generatedExpected = {
+	  version: 3,
+	  file: 'min.js',
+	  names: [],
+	  sources: ['one.js', 'two.js'],
+	  sourcesContent: [
+	    ' ONE.foo = 1;',
+	    ' TWO.inc = 2;'
+	  ],
+	  sourceRoot: '/the/root',
+	  mappings: ''
+	};
+	exports.testMapMultiSourcesMappingRefersSingleSourceOnly = {
+	    version: 3,
+	    file: 'min.js',
+	    names: ['bar', 'baz'],
+	    sources: ['one.js', 'withoutMappings.js'],
+	    sourceRoot: '',
+	    mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID'
+	};
 	// This mapping is identical to above, but uses the indexed format instead.
 	exports.indexedTestMap = {
 	  version: 3,
@@ -4096,6 +4334,6 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.assertEqualMaps = assertEqualMaps;
 
 
-/***/ }
+/***/ })
 /******/ ]);
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAgNTgyZGFmYjY0NDhiNzk1ZDc1NGIiLCJ3ZWJwYWNrOi8vLy4vdGVzdC90ZXN0LXNvdXJjZS1tYXAtZ2VuZXJhdG9yLmpzIiwid2VicGFjazovLy8uL2xpYi9zb3VyY2UtbWFwLWdlbmVyYXRvci5qcyIsIndlYnBhY2s6Ly8vLi9saWIvYmFzZTY0LXZscS5qcyIsIndlYnBhY2s6Ly8vLi9saWIvYmFzZTY0LmpzIiwid2VicGFjazovLy8uL2xpYi91dGlsLmpzIiwid2VicGFjazovLy8uL2xpYi9hcnJheS1zZXQuanMiLCJ3ZWJwYWNrOi8vLy4vbGliL21hcHBpbmctbGlzdC5qcyIsIndlYnBhY2 [...]
\ No newline at end of file
+//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAgZDI1OWQ2YzJjMjNmNDk1MDI2NGQiLCJ3ZWJwYWNrOi8vLy4vdGVzdC90ZXN0LXNvdXJjZS1tYXAtZ2VuZXJhdG9yLmpzIiwid2VicGFjazovLy8uL2xpYi9zb3VyY2UtbWFwLWdlbmVyYXRvci5qcyIsIndlYnBhY2s6Ly8vLi9saWIvYmFzZTY0LXZscS5qcyIsIndlYnBhY2s6Ly8vLi9saWIvYmFzZTY0LmpzIiwid2VicGFjazovLy8uL2xpYi91dGlsLmpzIiwid2VicGFjazovLy8uL2xpYi9hcnJheS1zZXQuanMiLCJ3ZWJwYWNrOi8vLy4vbGliL21hcHBpbmctbGlzdC5q [...]
\ No newline at end of file
diff --git a/dist/test/test_source_node.js b/dist/test/test_source_node.js
index 00bd8bd..86b2804 100644
--- a/dist/test/test_source_node.js
+++ b/dist/test/test_source_node.js
@@ -52,7 +52,7 @@ var SOURCE_MAP_TEST_MODULE =
 /************************************************************************/
 /******/ ([
 /* 0 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -334,7 +334,6 @@ var SOURCE_MAP_TEST_MODULE =
 	  // Assume the following directory structure:
 	  //
 	  // http://foo.org/
-	  //   bar.coffee
 	  //   app/
 	  //     coffee/
 	  //       foo.coffee
@@ -346,14 +345,9 @@ var SOURCE_MAP_TEST_MODULE =
 	  //     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: '..'
@@ -381,8 +375,6 @@ var SOURCE_MAP_TEST_MODULE =
 	
 	  test('../coffee/maps', [
 	    '../coffee/foo.coffee',
-	    '/bar.coffee',
-	    'http://www.example.com/baz.coffee',
 	    'foo.js'
 	  ]);
 	
@@ -391,29 +383,21 @@ var SOURCE_MAP_TEST_MODULE =
 	
 	  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'
 	  ]);
 	};
@@ -662,11 +646,26 @@ var SOURCE_MAP_TEST_MODULE =
 	  assert.equal(results[1][0], 'b.js');
 	  assert.equal(results[1][1], 'otherContent');
 	};
+	
+	exports['test from issue 258'] = function (assert) {
+	  var node = new SourceNode();
+	
+	  var reactCode =
+	      ";require(0);\n//# sourceMappingURL=/index.ios.map?platform=ios&dev=false&minify=true";
+	
+	  var reactMap =
+	      "{\"version\":3,\"file\":\"/index.ios.bundle?platform=ios&dev=false&minify=true\",\"sections\":[{\"offset\":{\"line\":0,\"column\":0},\"map\":{\"version\":3,\"sources\":[\"require-0.js\"],\"names\":[],\"mappings\":\"AAAA;\",\"file\":\"require-0.js\",\"sourcesContent\":[\";require(0);\"]}}]}";
+	
+	  node.add(SourceNode.fromStringWithSourceMap(
+	    reactCode,
+	    new SourceMapConsumer(reactMap)
+	  ));
+	}
 
 
-/***/ },
+/***/ }),
 /* 1 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -721,6 +720,58 @@ var SOURCE_MAP_TEST_MODULE =
 	  sourceRoot: '',
 	  mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
 	};
+	exports.testMapSingleSource = {
+	  version: 3,
+	  file: 'min.js',
+	  names: ['bar', 'baz'],
+	  sources: ['one.js'],
+	  sourceRoot: '',
+	  mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID'
+	};
+	exports.testMapEmptyMappings = {
+	  version: 3,
+	  file: 'min.js',
+	  names: [],
+	  sources: ['one.js', 'two.js'],
+	  sourcesContent: [
+	    ' ONE.foo = 1;',
+	    ' TWO.inc = 2;'
+	  ],
+	  sourceRoot: '',
+	  mappings: ''
+	};
+	exports.testMapEmptyMappingsRelativeSources = {
+	  version: 3,
+	  file: 'min.js',
+	  names: [],
+	  sources: ['./one.js', './two.js'],
+	  sourcesContent: [
+	    ' ONE.foo = 1;',
+	    ' TWO.inc = 2;'
+	  ],
+	  sourceRoot: '/the/root',
+	  mappings: ''
+	};
+	exports.testMapEmptyMappingsRelativeSources_generatedExpected = {
+	  version: 3,
+	  file: 'min.js',
+	  names: [],
+	  sources: ['one.js', 'two.js'],
+	  sourcesContent: [
+	    ' ONE.foo = 1;',
+	    ' TWO.inc = 2;'
+	  ],
+	  sourceRoot: '/the/root',
+	  mappings: ''
+	};
+	exports.testMapMultiSourcesMappingRefersSingleSourceOnly = {
+	    version: 3,
+	    file: 'min.js',
+	    names: ['bar', 'baz'],
+	    sources: ['one.js', 'withoutMappings.js'],
+	    sourceRoot: '',
+	    mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID'
+	};
 	// This mapping is identical to above, but uses the indexed format instead.
 	exports.indexedTestMap = {
 	  version: 3,
@@ -965,9 +1016,9 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.assertEqualMaps = assertEqualMaps;
 
 
-/***/ },
+/***/ }),
 /* 2 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -997,7 +1048,7 @@ var SOURCE_MAP_TEST_MODULE =
 	}
 	exports.getArg = getArg;
 	
-	var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/;
+	var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/;
 	var dataUrlRegexp = /^data:.+\,.+$/;
 	
 	function urlParse(aUrl) {
@@ -1040,7 +1091,7 @@ var SOURCE_MAP_TEST_MODULE =
 	/**
 	 * Normalizes a path, or the path portion of a URL:
 	 *
-	 * - Replaces consequtive slashes with one slash.
+	 * - Replaces consecutive slashes with one slash.
 	 * - Removes unnecessary '.' parts.
 	 * - Removes unnecessary '<dir>/..' parts.
 	 *
@@ -1153,7 +1204,7 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.join = join;
 	
 	exports.isAbsolute = function (aPath) {
-	  return aPath.charAt(0) === '/' || !!aPath.match(urlRegexp);
+	  return aPath.charAt(0) === '/' || urlRegexp.test(aPath);
 	};
 	
 	/**
@@ -1273,7 +1324,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 * stubbed out mapping.
 	 */
 	function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
-	  var cmp = mappingA.source - mappingB.source;
+	  var cmp = strcmp(mappingA.source, mappingB.source);
 	  if (cmp !== 0) {
 	    return cmp;
 	  }
@@ -1298,7 +1349,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    return cmp;
 	  }
 	
-	  return mappingA.name - mappingB.name;
+	  return strcmp(mappingA.name, mappingB.name);
 	}
 	exports.compareByOriginalPositions = compareByOriginalPositions;
 	
@@ -1322,7 +1373,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    return cmp;
 	  }
 	
-	  cmp = mappingA.source - mappingB.source;
+	  cmp = strcmp(mappingA.source, mappingB.source);
 	  if (cmp !== 0) {
 	    return cmp;
 	  }
@@ -1337,7 +1388,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    return cmp;
 	  }
 	
-	  return mappingA.name - mappingB.name;
+	  return strcmp(mappingA.name, mappingB.name);
 	}
 	exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated;
 	
@@ -1346,6 +1397,14 @@ var SOURCE_MAP_TEST_MODULE =
 	    return 0;
 	  }
 	
+	  if (aStr1 === null) {
+	    return 1; // aStr2 !== null
+	  }
+	
+	  if (aStr2 === null) {
+	    return -1; // aStr1 !== null
+	  }
+	
 	  if (aStr1 > aStr2) {
 	    return 1;
 	  }
@@ -1386,11 +1445,74 @@ var SOURCE_MAP_TEST_MODULE =
 	  return strcmp(mappingA.name, mappingB.name);
 	}
 	exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated;
+	
+	/**
+	 * Strip any JSON XSSI avoidance prefix from the string (as documented
+	 * in the source maps specification), and then parse the string as
+	 * JSON.
+	 */
+	function parseSourceMapInput(str) {
+	  return JSON.parse(str.replace(/^\)]}'[^\n]*\n/, ''));
+	}
+	exports.parseSourceMapInput = parseSourceMapInput;
+	
+	/**
+	 * Compute the URL of a source given the the source root, the source's
+	 * URL, and the source map's URL.
+	 */
+	function computeSourceURL(sourceRoot, sourceURL, sourceMapURL) {
+	  sourceURL = sourceURL || '';
+	
+	  if (sourceRoot) {
+	    // This follows what Chrome does.
+	    if (sourceRoot[sourceRoot.length - 1] !== '/' && sourceURL[0] !== '/') {
+	      sourceRoot += '/';
+	    }
+	    // The spec says:
+	    //   Line 4: An optional source root, useful for relocating source
+	    //   files on a server or removing repeated values in the
+	    //   “sources” entry.  This value is prepended to the individual
+	    //   entries in the “source” field.
+	    sourceURL = sourceRoot + sourceURL;
+	  }
+	
+	  // Historically, SourceMapConsumer did not take the sourceMapURL as
+	  // a parameter.  This mode is still somewhat supported, which is why
+	  // this code block is conditional.  However, it's preferable to pass
+	  // the source map URL to SourceMapConsumer, so that this function
+	  // can implement the source URL resolution algorithm as outlined in
+	  // the spec.  This block is basically the equivalent of:
+	  //    new URL(sourceURL, sourceMapURL).toString()
+	  // ... except it avoids using URL, which wasn't available in the
+	  // older releases of node still supported by this library.
+	  //
+	  // The spec says:
+	  //   If the sources are not absolute URLs after prepending of the
+	  //   “sourceRoot”, the sources are resolved relative to the
+	  //   SourceMap (like resolving script src in a html document).
+	  if (sourceMapURL) {
+	    var parsed = urlParse(sourceMapURL);
+	    if (!parsed) {
+	      throw new Error("sourceMapURL could not be parsed");
+	    }
+	    if (parsed.path) {
+	      // Strip the last path component, but keep the "/".
+	      var index = parsed.path.lastIndexOf('/');
+	      if (index >= 0) {
+	        parsed.path = parsed.path.substring(0, index + 1);
+	      }
+	    }
+	    sourceURL = join(urlGenerate(parsed), sourceURL);
+	  }
+	
+	  return normalize(sourceURL);
+	}
+	exports.computeSourceURL = computeSourceURL;
 
 
-/***/ },
+/***/ }),
 /* 3 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -1466,6 +1588,15 @@ var SOURCE_MAP_TEST_MODULE =
 	      generator.addMapping(newMapping);
 	    });
 	    aSourceMapConsumer.sources.forEach(function (sourceFile) {
+	      var sourceRelative = sourceFile;
+	      if (sourceRoot !== null) {
+	        sourceRelative = util.relative(sourceRoot, sourceFile);
+	      }
+	
+	      if (!generator._sources.has(sourceRelative)) {
+	        generator._sources.add(sourceRelative);
+	      }
+	
 	      var content = aSourceMapConsumer.sourceContentFor(sourceFile);
 	      if (content != null) {
 	        generator.setSourceContent(sourceFile, content);
@@ -1653,6 +1784,18 @@ var SOURCE_MAP_TEST_MODULE =
 	SourceMapGenerator.prototype._validateMapping =
 	  function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource,
 	                                              aName) {
+	    // When aOriginal is truthy but has empty values for .line and .column,
+	    // it is most likely a programmer error. In this case we throw a very
+	    // specific error message to try to guide them the right way.
+	    // For example: https://github.com/Polymer/polymer-bundler/pull/519
+	    if (aOriginal && typeof aOriginal.line !== 'number' && typeof aOriginal.column !== 'number') {
+	        throw new Error(
+	            'original.line and original.column are not numbers -- you probably meant to omit ' +
+	            'the original mapping entirely and only map the generated position. If so, pass ' +
+	            'null for the original mapping instead of an object with empty or null values.'
+	        );
+	    }
+	
 	    if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
 	        && aGenerated.line > 0 && aGenerated.column >= 0
 	        && !aOriginal && !aSource && !aName) {
@@ -1798,9 +1941,9 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.SourceMapGenerator = SourceMapGenerator;
 
 
-/***/ },
+/***/ }),
 /* 4 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -1944,9 +2087,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 5 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -2017,9 +2160,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 6 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -2030,6 +2173,7 @@ var SOURCE_MAP_TEST_MODULE =
 	
 	var util = __webpack_require__(2);
 	var has = Object.prototype.hasOwnProperty;
+	var hasNativeMap = typeof Map !== "undefined";
 	
 	/**
 	 * A data structure which is a combination of an array and a set. Adding a new
@@ -2039,7 +2183,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 */
 	function ArraySet() {
 	  this._array = [];
-	  this._set = Object.create(null);
+	  this._set = hasNativeMap ? new Map() : Object.create(null);
 	}
 	
 	/**
@@ -2060,7 +2204,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 * @returns Number
 	 */
 	ArraySet.prototype.size = function ArraySet_size() {
-	  return Object.getOwnPropertyNames(this._set).length;
+	  return hasNativeMap ? this._set.size : Object.getOwnPropertyNames(this._set).length;
 	};
 	
 	/**
@@ -2069,14 +2213,18 @@ var SOURCE_MAP_TEST_MODULE =
 	 * @param String aStr
 	 */
 	ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) {
-	  var sStr = util.toSetString(aStr);
-	  var isDuplicate = has.call(this._set, sStr);
+	  var sStr = hasNativeMap ? aStr : util.toSetString(aStr);
+	  var isDuplicate = hasNativeMap ? this.has(aStr) : has.call(this._set, sStr);
 	  var idx = this._array.length;
 	  if (!isDuplicate || aAllowDuplicates) {
 	    this._array.push(aStr);
 	  }
 	  if (!isDuplicate) {
-	    this._set[sStr] = idx;
+	    if (hasNativeMap) {
+	      this._set.set(aStr, idx);
+	    } else {
+	      this._set[sStr] = idx;
+	    }
 	  }
 	};
 	
@@ -2086,8 +2234,12 @@ var SOURCE_MAP_TEST_MODULE =
 	 * @param String aStr
 	 */
 	ArraySet.prototype.has = function ArraySet_has(aStr) {
-	  var sStr = util.toSetString(aStr);
-	  return has.call(this._set, sStr);
+	  if (hasNativeMap) {
+	    return this._set.has(aStr);
+	  } else {
+	    var sStr = util.toSetString(aStr);
+	    return has.call(this._set, sStr);
+	  }
 	};
 	
 	/**
@@ -2096,10 +2248,18 @@ var SOURCE_MAP_TEST_MODULE =
 	 * @param String aStr
 	 */
 	ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) {
-	  var sStr = util.toSetString(aStr);
-	  if (has.call(this._set, sStr)) {
-	    return this._set[sStr];
+	  if (hasNativeMap) {
+	    var idx = this._set.get(aStr);
+	    if (idx >= 0) {
+	        return idx;
+	    }
+	  } else {
+	    var sStr = util.toSetString(aStr);
+	    if (has.call(this._set, sStr)) {
+	      return this._set[sStr];
+	    }
 	  }
+	
 	  throw new Error('"' + aStr + '" is not in the set.');
 	};
 	
@@ -2127,9 +2287,9 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.ArraySet = ArraySet;
 
 
-/***/ },
+/***/ }),
 /* 7 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -2212,9 +2372,9 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.MappingList = MappingList;
 
 
-/***/ },
+/***/ }),
 /* 8 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -2229,15 +2389,15 @@ var SOURCE_MAP_TEST_MODULE =
 	var base64VLQ = __webpack_require__(4);
 	var quickSort = __webpack_require__(10).quickSort;
 	
-	function SourceMapConsumer(aSourceMap) {
+	function SourceMapConsumer(aSourceMap, aSourceMapURL) {
 	  var sourceMap = aSourceMap;
 	  if (typeof aSourceMap === 'string') {
-	    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+	    sourceMap = util.parseSourceMapInput(aSourceMap);
 	  }
 	
 	  return sourceMap.sections != null
-	    ? new IndexedSourceMapConsumer(sourceMap)
-	    : new BasicSourceMapConsumer(sourceMap);
+	    ? new IndexedSourceMapConsumer(sourceMap, aSourceMapURL)
+	    : new BasicSourceMapConsumer(sourceMap, aSourceMapURL);
 	}
 	
 	SourceMapConsumer.fromSourceMap = function(aSourceMap) {
@@ -2281,6 +2441,8 @@ var SOURCE_MAP_TEST_MODULE =
 	
 	SourceMapConsumer.prototype.__generatedMappings = null;
 	Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', {
+	  configurable: true,
+	  enumerable: true,
 	  get: function () {
 	    if (!this.__generatedMappings) {
 	      this._parseMappings(this._mappings, this.sourceRoot);
@@ -2292,6 +2454,8 @@ var SOURCE_MAP_TEST_MODULE =
 	
 	SourceMapConsumer.prototype.__originalMappings = null;
 	Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', {
+	  configurable: true,
+	  enumerable: true,
 	  get: function () {
 	    if (!this.__originalMappings) {
 	      this._parseMappings(this._mappings, this.sourceRoot);
@@ -2359,9 +2523,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    var sourceRoot = this.sourceRoot;
 	    mappings.map(function (mapping) {
 	      var source = mapping.source === null ? null : this._sources.at(mapping.source);
-	      if (source != null && sourceRoot != null) {
-	        source = util.join(sourceRoot, source);
-	      }
+	      source = util.computeSourceURL(sourceRoot, source, this._sourceMapURL);
 	      return {
 	        source: source,
 	        generatedLine: mapping.generatedLine,
@@ -2384,13 +2546,16 @@ var SOURCE_MAP_TEST_MODULE =
 	 * The only argument is an object with the following properties:
 	 *
 	 *   - source: The filename of the original source.
-	 *   - line: The line number in the original source.
+	 *   - line: The line number in the original source.  The line number is 1-based.
 	 *   - column: Optional. the column number in the original source.
+	 *    The column number is 0-based.
 	 *
 	 * and an array of objects is returned, each with the following properties:
 	 *
-	 *   - line: The line number in the generated source, or null.
+	 *   - line: The line number in the generated source, or null.  The
+	 *    line number is 1-based.
 	 *   - column: The column number in the generated source, or null.
+	 *    The column number is 0-based.
 	 */
 	SourceMapConsumer.prototype.allGeneratedPositionsFor =
 	  function SourceMapConsumer_allGeneratedPositionsFor(aArgs) {
@@ -2472,7 +2637,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 * query for information about the original file positions by giving it a file
 	 * position in the generated source.
 	 *
-	 * The only parameter is the raw source map (either as a JSON string, or
+	 * The first parameter is the raw source map (either as a JSON string, or
 	 * already parsed to an object). According to the spec, source maps have the
 	 * following attributes:
 	 *
@@ -2495,12 +2660,16 @@ var SOURCE_MAP_TEST_MODULE =
 	 *       mappings: "AA,AB;;ABCDE;"
 	 *     }
 	 *
+	 * The second parameter, if given, is a string whose value is the URL
+	 * at which the source map was found.  This URL is used to compute the
+	 * sources array.
+	 *
 	 * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#
 	 */
-	function BasicSourceMapConsumer(aSourceMap) {
+	function BasicSourceMapConsumer(aSourceMap, aSourceMapURL) {
 	  var sourceMap = aSourceMap;
 	  if (typeof aSourceMap === 'string') {
-	    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+	    sourceMap = util.parseSourceMapInput(aSourceMap);
 	  }
 	
 	  var version = util.getArg(sourceMap, 'version');
@@ -2519,6 +2688,10 @@ var SOURCE_MAP_TEST_MODULE =
 	    throw new Error('Unsupported version: ' + version);
 	  }
 	
+	  if (sourceRoot) {
+	    sourceRoot = util.normalize(sourceRoot);
+	  }
+	
 	  sources = sources
 	    .map(String)
 	    // Some source maps produce relative source paths like "./foo.js" instead of
@@ -2545,6 +2718,7 @@ var SOURCE_MAP_TEST_MODULE =
 	  this.sourceRoot = sourceRoot;
 	  this.sourcesContent = sourcesContent;
 	  this._mappings = mappings;
+	  this._sourceMapURL = aSourceMapURL;
 	  this.file = file;
 	}
 	
@@ -2556,10 +2730,12 @@ var SOURCE_MAP_TEST_MODULE =
 	 *
 	 * @param SourceMapGenerator aSourceMap
 	 *        The source map that will be consumed.
+	 * @param String aSourceMapURL
+	 *        The URL at which the source map can be found (optional)
 	 * @returns BasicSourceMapConsumer
 	 */
 	BasicSourceMapConsumer.fromSourceMap =
-	  function SourceMapConsumer_fromSourceMap(aSourceMap) {
+	  function SourceMapConsumer_fromSourceMap(aSourceMap, aSourceMapURL) {
 	    var smc = Object.create(BasicSourceMapConsumer.prototype);
 	
 	    var names = smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);
@@ -2568,6 +2744,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(),
 	                                                            smc.sourceRoot);
 	    smc.file = aSourceMap._file;
+	    smc._sourceMapURL = aSourceMapURL;
 	
 	    // Because we are modifying the entries (by converting string sources and
 	    // names to indices into the sources and names ArraySets), we have to make
@@ -2615,7 +2792,7 @@ var SOURCE_MAP_TEST_MODULE =
 	Object.defineProperty(BasicSourceMapConsumer.prototype, 'sources', {
 	  get: function () {
 	    return this._sources.toArray().map(function (s) {
-	      return this.sourceRoot != null ? util.join(this.sourceRoot, s) : s;
+	      return util.computeSourceURL(this.sourceRoot, s, this._sourceMapURL);
 	    }, this);
 	  }
 	});
@@ -2797,8 +2974,10 @@ var SOURCE_MAP_TEST_MODULE =
 	 * source's line and column positions provided. The only argument is an object
 	 * with the following properties:
 	 *
-	 *   - line: The line number in the generated source.
-	 *   - column: The column number in the generated source.
+	 *   - line: The line number in the generated source.  The line number
+	 *     is 1-based.
+	 *   - column: The column number in the generated source.  The column
+	 *     number is 0-based.
 	 *   - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or
 	 *     'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the
 	 *     closest element that is smaller than or greater than the one we are
@@ -2808,8 +2987,10 @@ var SOURCE_MAP_TEST_MODULE =
 	 * and an object is returned with the following properties:
 	 *
 	 *   - source: The original source file, or null.
-	 *   - line: The line number in the original source, or null.
-	 *   - column: The column number in the original source, or null.
+	 *   - line: The line number in the original source, or null.  The
+	 *     line number is 1-based.
+	 *   - column: The column number in the original source, or null.  The
+	 *     column number is 0-based.
 	 *   - name: The original identifier, or null.
 	 */
 	BasicSourceMapConsumer.prototype.originalPositionFor =
@@ -2835,9 +3016,7 @@ var SOURCE_MAP_TEST_MODULE =
 	        var source = util.getArg(mapping, 'source', null);
 	        if (source !== null) {
 	          source = this._sources.at(source);
-	          if (this.sourceRoot != null) {
-	            source = util.join(this.sourceRoot, source);
-	          }
+	          source = util.computeSourceURL(this.sourceRoot, source, this._sourceMapURL);
 	        }
 	        var name = util.getArg(mapping, 'name', null);
 	        if (name !== null) {
@@ -2884,12 +3063,23 @@ var SOURCE_MAP_TEST_MODULE =
 	      return null;
 	    }
 	
+	    var relativeSource = aSource;
 	    if (this.sourceRoot != null) {
-	      aSource = util.relative(this.sourceRoot, aSource);
+	      relativeSource = util.relative(this.sourceRoot, relativeSource);
+	    }
+	
+	    if (this._sources.has(relativeSource)) {
+	      return this.sourcesContent[this._sources.indexOf(relativeSource)];
 	    }
 	
-	    if (this._sources.has(aSource)) {
-	      return this.sourcesContent[this._sources.indexOf(aSource)];
+	    // Maybe aSource is an absolute URL as returned by |sources|.  In
+	    // this case we can't simply undo the transform.
+	    var sourceArray = this.sources;
+	    var i;
+	    for (i = 0; i < sourceArray.length; ++i) {
+	      if (sourceArray[i] == aSource) {
+	        return this.sourcesContent[i];
+	      }
 	    }
 	
 	    var url;
@@ -2899,15 +3089,15 @@ var SOURCE_MAP_TEST_MODULE =
 	      // many users. We can help them out when they expect file:// URIs to
 	      // behave like it would if they were running a local HTTP server. See
 	      // https://bugzilla.mozilla.org/show_bug.cgi?id=885597.
-	      var fileUriAbsPath = aSource.replace(/^file:\/\//, "");
+	      var fileUriAbsPath = relativeSource.replace(/^file:\/\//, "");
 	      if (url.scheme == "file"
 	          && this._sources.has(fileUriAbsPath)) {
 	        return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)]
 	      }
 	
 	      if ((!url.path || url.path == "/")
-	          && this._sources.has("/" + aSource)) {
-	        return this.sourcesContent[this._sources.indexOf("/" + aSource)];
+	          && this._sources.has("/" + relativeSource)) {
+	        return this.sourcesContent[this._sources.indexOf("/" + relativeSource)];
 	      }
 	    }
 	
@@ -2919,7 +3109,7 @@ var SOURCE_MAP_TEST_MODULE =
 	      return null;
 	    }
 	    else {
-	      throw new Error('"' + aSource + '" is not in the SourceMap.');
+	      throw new Error('"' + relativeSource + '" is not in the SourceMap.');
 	    }
 	  };
 	
@@ -2929,8 +3119,10 @@ var SOURCE_MAP_TEST_MODULE =
 	 * the following properties:
 	 *
 	 *   - source: The filename of the original source.
-	 *   - line: The line number in the original source.
-	 *   - column: The column number in the original source.
+	 *   - line: The line number in the original source.  The line number
+	 *     is 1-based.
+	 *   - column: The column number in the original source.  The column
+	 *     number is 0-based.
 	 *   - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or
 	 *     'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the
 	 *     closest element that is smaller than or greater than the one we are
@@ -2939,8 +3131,10 @@ var SOURCE_MAP_TEST_MODULE =
 	 *
 	 * and an object is returned with the following properties:
 	 *
-	 *   - line: The line number in the generated source, or null.
+	 *   - line: The line number in the generated source, or null.  The
+	 *     line number is 1-based.
 	 *   - column: The column number in the generated source, or null.
+	 *     The column number is 0-based.
 	 */
 	BasicSourceMapConsumer.prototype.generatedPositionFor =
 	  function SourceMapConsumer_generatedPositionFor(aArgs) {
@@ -2999,7 +3193,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 * that it takes "indexed" source maps (i.e. ones with a "sections" field) as
 	 * input.
 	 *
-	 * The only parameter is a raw source map (either as a JSON string, or already
+	 * The first parameter is a raw source map (either as a JSON string, or already
 	 * parsed to an object). According to the spec for indexed source maps, they
 	 * have the following attributes:
 	 *
@@ -3036,12 +3230,16 @@ var SOURCE_MAP_TEST_MODULE =
 	 *    }],
 	 *  }
 	 *
+	 * The second parameter, if given, is a string whose value is the URL
+	 * at which the source map was found.  This URL is used to compute the
+	 * sources array.
+	 *
 	 * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.535es3xeprgt
 	 */
-	function IndexedSourceMapConsumer(aSourceMap) {
+	function IndexedSourceMapConsumer(aSourceMap, aSourceMapURL) {
 	  var sourceMap = aSourceMap;
 	  if (typeof aSourceMap === 'string') {
-	    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+	    sourceMap = util.parseSourceMapInput(aSourceMap);
 	  }
 	
 	  var version = util.getArg(sourceMap, 'version');
@@ -3081,7 +3279,7 @@ var SOURCE_MAP_TEST_MODULE =
 	        generatedLine: offsetLine + 1,
 	        generatedColumn: offsetColumn + 1
 	      },
-	      consumer: new SourceMapConsumer(util.getArg(s, 'map'))
+	      consumer: new SourceMapConsumer(util.getArg(s, 'map'), aSourceMapURL)
 	    }
 	  });
 	}
@@ -3114,14 +3312,18 @@ var SOURCE_MAP_TEST_MODULE =
 	 * source's line and column positions provided. The only argument is an object
 	 * with the following properties:
 	 *
-	 *   - line: The line number in the generated source.
-	 *   - column: The column number in the generated source.
+	 *   - line: The line number in the generated source.  The line number
+	 *     is 1-based.
+	 *   - column: The column number in the generated source.  The column
+	 *     number is 0-based.
 	 *
 	 * and an object is returned with the following properties:
 	 *
 	 *   - source: The original source file, or null.
-	 *   - line: The line number in the original source, or null.
-	 *   - column: The column number in the original source, or null.
+	 *   - line: The line number in the original source, or null.  The
+	 *     line number is 1-based.
+	 *   - column: The column number in the original source, or null.  The
+	 *     column number is 0-based.
 	 *   - name: The original identifier, or null.
 	 */
 	IndexedSourceMapConsumer.prototype.originalPositionFor =
@@ -3205,13 +3407,17 @@ var SOURCE_MAP_TEST_MODULE =
 	 * the following properties:
 	 *
 	 *   - source: The filename of the original source.
-	 *   - line: The line number in the original source.
-	 *   - column: The column number in the original source.
+	 *   - line: The line number in the original source.  The line number
+	 *     is 1-based.
+	 *   - column: The column number in the original source.  The column
+	 *     number is 0-based.
 	 *
 	 * and an object is returned with the following properties:
 	 *
-	 *   - line: The line number in the generated source, or null.
+	 *   - line: The line number in the generated source, or null.  The
+	 *     line number is 1-based. 
 	 *   - column: The column number in the generated source, or null.
+	 *     The column number is 0-based.
 	 */
 	IndexedSourceMapConsumer.prototype.generatedPositionFor =
 	  function IndexedSourceMapConsumer_generatedPositionFor(aArgs) {
@@ -3259,15 +3465,16 @@ var SOURCE_MAP_TEST_MODULE =
 	        var mapping = sectionMappings[j];
 	
 	        var source = section.consumer._sources.at(mapping.source);
-	        if (section.consumer.sourceRoot !== null) {
-	          source = util.join(section.consumer.sourceRoot, source);
-	        }
+	        source = util.computeSourceURL(section.consumer.sourceRoot, source, this._sourceMapURL);
 	        this._sources.add(source);
 	        source = this._sources.indexOf(source);
 	
-	        var name = section.consumer._names.at(mapping.name);
-	        this._names.add(name);
-	        name = this._names.indexOf(name);
+	        var name = null;
+	        if (mapping.name) {
+	          name = section.consumer._names.at(mapping.name);
+	          this._names.add(name);
+	          name = this._names.indexOf(name);
+	        }
 	
 	        // The mappings coming from the consumer for the section have
 	        // generated positions relative to the start of the section, so we
@@ -3300,9 +3507,9 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer;
 
 
-/***/ },
+/***/ }),
 /* 9 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -3417,9 +3624,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 10 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -3537,9 +3744,9 @@ var SOURCE_MAP_TEST_MODULE =
 	};
 
 
-/***/ },
+/***/ }),
 /* 11 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -3603,13 +3810,19 @@ var SOURCE_MAP_TEST_MODULE =
 	    // 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`.
+	    // Processed fragments are accessed by calling `shiftNextLine`.
 	    var remainingLines = aGeneratedCode.split(REGEX_NEWLINE);
+	    var remainingLinesIndex = 0;
 	    var shiftNextLine = function() {
-	      var lineContents = remainingLines.shift();
+	      var lineContents = getNextLine();
 	      // The last line of a file might not have a newline.
-	      var newLine = remainingLines.shift() || "";
+	      var newLine = getNextLine() || "";
 	      return lineContents + newLine;
+	
+	      function getNextLine() {
+	        return remainingLinesIndex < remainingLines.length ?
+	            remainingLines[remainingLinesIndex++] : undefined;
+	      }
 	    };
 	
 	    // We need to remember the position of "remainingLines"
@@ -3634,10 +3847,10 @@ var SOURCE_MAP_TEST_MODULE =
 	          // There is no new line in between.
 	          // Associate the code between "lastGeneratedColumn" and
 	          // "mapping.generatedColumn" with "lastMapping"
-	          var nextLine = remainingLines[0];
+	          var nextLine = remainingLines[remainingLinesIndex] || '';
 	          var code = nextLine.substr(0, mapping.generatedColumn -
 	                                        lastGeneratedColumn);
-	          remainingLines[0] = nextLine.substr(mapping.generatedColumn -
+	          remainingLines[remainingLinesIndex] = nextLine.substr(mapping.generatedColumn -
 	                                              lastGeneratedColumn);
 	          lastGeneratedColumn = mapping.generatedColumn;
 	          addMappingWithCode(lastMapping, code);
@@ -3654,21 +3867,21 @@ var SOURCE_MAP_TEST_MODULE =
 	        lastGeneratedLine++;
 	      }
 	      if (lastGeneratedColumn < mapping.generatedColumn) {
-	        var nextLine = remainingLines[0];
+	        var nextLine = remainingLines[remainingLinesIndex] || '';
 	        node.add(nextLine.substr(0, mapping.generatedColumn));
-	        remainingLines[0] = nextLine.substr(mapping.generatedColumn);
+	        remainingLines[remainingLinesIndex] = nextLine.substr(mapping.generatedColumn);
 	        lastGeneratedColumn = mapping.generatedColumn;
 	      }
 	      lastMapping = mapping;
 	    }, this);
 	    // We have processed all mappings.
-	    if (remainingLines.length > 0) {
+	    if (remainingLinesIndex < remainingLines.length) {
 	      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(""));
+	      node.add(remainingLines.splice(remainingLinesIndex).join(""));
 	    }
 	
 	    // Copy sourcesContent into SourceNode
@@ -3950,6 +4163,6 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.SourceNode = SourceNode;
 
 
-/***/ }
+/***/ })
 /******/ ]);
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAgZjQzMWU1ZTAxZjc4YzgxM2Y1OWMiLCJ3ZWJwYWNrOi8vLy4vdGVzdC90ZXN0LXNvdXJjZS1ub2RlLmpzIiwid2VicGFjazovLy8uL3Rlc3QvdXRpbC5qcyIsIndlYnBhY2s6Ly8vLi9saWIvdXRpbC5qcyIsIndlYnBhY2s6Ly8vLi9saWIvc291cmNlLW1hcC1nZW5lcmF0b3IuanMiLCJ3ZWJwYWNrOi8vLy4vbGliL2Jhc2U2NC12bHEuanMiLCJ3ZWJwYWNrOi8vLy4vbGliL2Jhc2U2NC5qcyIsIndlYnBhY2s6Ly8vLi9saWIvYXJyYXktc2V0LmpzIiwid2VicGFjazovLy8uL2xpYi9tYXBwaW [...]
\ No newline at end of file
+//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAgMDNmZDU3NzA3ZTNhYjIwNDQ0ZGMiLCJ3ZWJwYWNrOi8vLy4vdGVzdC90ZXN0LXNvdXJjZS1ub2RlLmpzIiwid2VicGFjazovLy8uL3Rlc3QvdXRpbC5qcyIsIndlYnBhY2s6Ly8vLi9saWIvdXRpbC5qcyIsIndlYnBhY2s6Ly8vLi9saWIvc291cmNlLW1hcC1nZW5lcmF0b3IuanMiLCJ3ZWJwYWNrOi8vLy4vbGliL2Jhc2U2NC12bHEuanMiLCJ3ZWJwYWNrOi8vLy4vbGliL2Jhc2U2NC5qcyIsIndlYnBhY2s6Ly8vLi9saWIvYXJyYXktc2V0LmpzIiwid2VicGFjazovLy8u [...]
\ No newline at end of file
diff --git a/dist/test/test_util.js b/dist/test/test_util.js
index 24c522b..dc5c1b3 100644
--- a/dist/test/test_util.js
+++ b/dist/test/test_util.js
@@ -52,7 +52,7 @@ var SOURCE_MAP_TEST_MODULE =
 /************************************************************************/
 /******/ ([
 /* 0 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ (function(module, exports, __webpack_require__) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -88,6 +88,18 @@ var SOURCE_MAP_TEST_MODULE =
 	  assert.equal(libUtil.urlParse('a//b'), null);
 	  assert.equal(libUtil.urlParse('/a'), null);
 	  assert.equal(libUtil.urlParse('data:foo,bar'), null);
+	
+	  var parsed = libUtil.urlParse('http://x-y.com/bar');
+	  assert.equal(parsed.scheme, 'http');
+	  assert.equal(parsed.host, 'x-y.com');
+	  assert.equal(parsed.path, '/bar');
+	
+	  var webpackURL = 'webpack:///webpack/bootstrap 67e184f9679733298d44'
+	  parsed = libUtil.urlParse(webpackURL);
+	  assert.equal(parsed.scheme, 'webpack');
+	  assert.equal(parsed.host, '');
+	  assert.equal(parsed.path, '/webpack/bootstrap 67e184f9679733298d44');
+	  assert.equal(webpackURL, libUtil.urlGenerate(parsed));
 	};
 	
 	exports['test normalize()'] = function (assert) {
@@ -268,11 +280,43 @@ var SOURCE_MAP_TEST_MODULE =
 	  assert.equal(libUtil.relative('/', '/the/root/one.js'), 'the/root/one.js');
 	  assert.equal(libUtil.relative('/', 'the/root/one.js'), 'the/root/one.js');
 	};
+	
+	exports['test computeSourceURL'] = function (assert) {
+	  // Tests with sourceMapURL.
+	  assert.equal(libUtil.computeSourceURL('', 'src/test.js', 'http://example.com'),
+	               'http://example.com/src/test.js');
+	  assert.equal(libUtil.computeSourceURL(undefined, 'src/test.js', 'http://example.com'),
+	               'http://example.com/src/test.js');
+	  assert.equal(libUtil.computeSourceURL('src', 'test.js', 'http://example.com'),
+	               'http://example.com/src/test.js');
+	  assert.equal(libUtil.computeSourceURL('src/', 'test.js', 'http://example.com'),
+	               'http://example.com/src/test.js');
+	  assert.equal(libUtil.computeSourceURL('src', '/test.js', 'http://example.com'),
+	               'http://example.com/src/test.js');
+	  assert.equal(libUtil.computeSourceURL('http://mozilla.com', 'src/test.js', 'http://example.com'),
+	               'http://mozilla.com/src/test.js');
+	  assert.equal(libUtil.computeSourceURL('', 'test.js', 'http://example.com/src/test.js.map'),
+	               'http://example.com/src/test.js');
+	
+	  // Legacy code won't pass in the sourceMapURL.
+	  assert.equal(libUtil.computeSourceURL('', 'src/test.js'), 'src/test.js');
+	  assert.equal(libUtil.computeSourceURL(undefined, 'src/test.js'), 'src/test.js');
+	  assert.equal(libUtil.computeSourceURL('src', 'test.js'), 'src/test.js');
+	  assert.equal(libUtil.computeSourceURL('src/', 'test.js'), 'src/test.js');
+	  assert.equal(libUtil.computeSourceURL('src', '/test.js'), 'src/test.js');
+	  assert.equal(libUtil.computeSourceURL('src', '../test.js'), 'test.js');
+	  assert.equal(libUtil.computeSourceURL('src/dir', '../././../test.js'), 'test.js');
+	
+	  // This gives different results with the old algorithm and the new
+	  // spec-compliant algorithm.
+	  assert.equal(libUtil.computeSourceURL('http://example.com/dir', '/test.js'),
+	               'http://example.com/dir/test.js');
+	};
 
 
-/***/ },
+/***/ }),
 /* 1 */
-/***/ function(module, exports) {
+/***/ (function(module, exports) {
 
 	/* -*- Mode: js; js-indent-level: 2; -*- */
 	/*
@@ -302,7 +346,7 @@ var SOURCE_MAP_TEST_MODULE =
 	}
 	exports.getArg = getArg;
 	
-	var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/;
+	var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/;
 	var dataUrlRegexp = /^data:.+\,.+$/;
 	
 	function urlParse(aUrl) {
@@ -345,7 +389,7 @@ var SOURCE_MAP_TEST_MODULE =
 	/**
 	 * Normalizes a path, or the path portion of a URL:
 	 *
-	 * - Replaces consequtive slashes with one slash.
+	 * - Replaces consecutive slashes with one slash.
 	 * - Removes unnecessary '.' parts.
 	 * - Removes unnecessary '<dir>/..' parts.
 	 *
@@ -458,7 +502,7 @@ var SOURCE_MAP_TEST_MODULE =
 	exports.join = join;
 	
 	exports.isAbsolute = function (aPath) {
-	  return aPath.charAt(0) === '/' || !!aPath.match(urlRegexp);
+	  return aPath.charAt(0) === '/' || urlRegexp.test(aPath);
 	};
 	
 	/**
@@ -578,7 +622,7 @@ var SOURCE_MAP_TEST_MODULE =
 	 * stubbed out mapping.
 	 */
 	function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
-	  var cmp = mappingA.source - mappingB.source;
+	  var cmp = strcmp(mappingA.source, mappingB.source);
 	  if (cmp !== 0) {
 	    return cmp;
 	  }
@@ -603,7 +647,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    return cmp;
 	  }
 	
-	  return mappingA.name - mappingB.name;
+	  return strcmp(mappingA.name, mappingB.name);
 	}
 	exports.compareByOriginalPositions = compareByOriginalPositions;
 	
@@ -627,7 +671,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    return cmp;
 	  }
 	
-	  cmp = mappingA.source - mappingB.source;
+	  cmp = strcmp(mappingA.source, mappingB.source);
 	  if (cmp !== 0) {
 	    return cmp;
 	  }
@@ -642,7 +686,7 @@ var SOURCE_MAP_TEST_MODULE =
 	    return cmp;
 	  }
 	
-	  return mappingA.name - mappingB.name;
+	  return strcmp(mappingA.name, mappingB.name);
 	}
 	exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated;
 	
@@ -651,6 +695,14 @@ var SOURCE_MAP_TEST_MODULE =
 	    return 0;
 	  }
 	
+	  if (aStr1 === null) {
+	    return 1; // aStr2 !== null
+	  }
+	
+	  if (aStr2 === null) {
+	    return -1; // aStr1 !== null
+	  }
+	
 	  if (aStr1 > aStr2) {
 	    return 1;
 	  }
@@ -691,8 +743,71 @@ var SOURCE_MAP_TEST_MODULE =
 	  return strcmp(mappingA.name, mappingB.name);
 	}
 	exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated;
+	
+	/**
+	 * Strip any JSON XSSI avoidance prefix from the string (as documented
+	 * in the source maps specification), and then parse the string as
+	 * JSON.
+	 */
+	function parseSourceMapInput(str) {
+	  return JSON.parse(str.replace(/^\)]}'[^\n]*\n/, ''));
+	}
+	exports.parseSourceMapInput = parseSourceMapInput;
+	
+	/**
+	 * Compute the URL of a source given the the source root, the source's
+	 * URL, and the source map's URL.
+	 */
+	function computeSourceURL(sourceRoot, sourceURL, sourceMapURL) {
+	  sourceURL = sourceURL || '';
+	
+	  if (sourceRoot) {
+	    // This follows what Chrome does.
+	    if (sourceRoot[sourceRoot.length - 1] !== '/' && sourceURL[0] !== '/') {
+	      sourceRoot += '/';
+	    }
+	    // The spec says:
+	    //   Line 4: An optional source root, useful for relocating source
+	    //   files on a server or removing repeated values in the
+	    //   “sources” entry.  This value is prepended to the individual
+	    //   entries in the “source” field.
+	    sourceURL = sourceRoot + sourceURL;
+	  }
+	
+	  // Historically, SourceMapConsumer did not take the sourceMapURL as
+	  // a parameter.  This mode is still somewhat supported, which is why
+	  // this code block is conditional.  However, it's preferable to pass
+	  // the source map URL to SourceMapConsumer, so that this function
+	  // can implement the source URL resolution algorithm as outlined in
+	  // the spec.  This block is basically the equivalent of:
+	  //    new URL(sourceURL, sourceMapURL).toString()
+	  // ... except it avoids using URL, which wasn't available in the
+	  // older releases of node still supported by this library.
+	  //
+	  // The spec says:
+	  //   If the sources are not absolute URLs after prepending of the
+	  //   “sourceRoot”, the sources are resolved relative to the
+	  //   SourceMap (like resolving script src in a html document).
+	  if (sourceMapURL) {
+	    var parsed = urlParse(sourceMapURL);
+	    if (!parsed) {
+	      throw new Error("sourceMapURL could not be parsed");
+	    }
+	    if (parsed.path) {
+	      // Strip the last path component, but keep the "/".
+	      var index = parsed.path.lastIndexOf('/');
+	      if (index >= 0) {
+	        parsed.path = parsed.path.substring(0, index + 1);
+	      }
+	    }
+	    sourceURL = join(urlGenerate(parsed), sourceURL);
+	  }
+	
+	  return normalize(sourceURL);
+	}
+	exports.computeSourceURL = computeSourceURL;
 
 
-/***/ }
+/***/ })
 /******/ ]);
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAgY2U5NWIwNTk3MzYzYzc2M2Y1ZjciLCJ3ZWJwYWNrOi8vLy4vdGVzdC90ZXN0LXV0aWwuanMiLCJ3ZWJwYWNrOi8vLy4vbGliL3V0aWwuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7QUFBQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSx1QkFBZTtBQUNmO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOzs7QUFHQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOzs7Oz [...]
\ No newline at end of file
+//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAgYjQ4ZDVkYjU2ZTA1NDMwM2M4Y2YiLCJ3ZWJwYWNrOi8vLy4vdGVzdC90ZXN0LXV0aWwuanMiLCJ3ZWJwYWNrOi8vLy4vbGliL3V0aWwuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7QUFBQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSx1QkFBZTtBQUNmO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOzs7QUFHQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFF [...]
\ No newline at end of file
diff --git a/lib/source-map-consumer.js b/lib/source-map-consumer.js
index 6abcc28..37b5357 100644
--- a/lib/source-map-consumer.js
+++ b/lib/source-map-consumer.js
@@ -11,15 +11,15 @@ var ArraySet = require('./array-set').ArraySet;
 var base64VLQ = require('./base64-vlq');
 var quickSort = require('./quick-sort').quickSort;
 
-function SourceMapConsumer(aSourceMap) {
+function SourceMapConsumer(aSourceMap, aSourceMapURL) {
   var sourceMap = aSourceMap;
   if (typeof aSourceMap === 'string') {
-    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+    sourceMap = util.parseSourceMapInput(aSourceMap);
   }
 
   return sourceMap.sections != null
-    ? new IndexedSourceMapConsumer(sourceMap)
-    : new BasicSourceMapConsumer(sourceMap);
+    ? new IndexedSourceMapConsumer(sourceMap, aSourceMapURL)
+    : new BasicSourceMapConsumer(sourceMap, aSourceMapURL);
 }
 
 SourceMapConsumer.fromSourceMap = function(aSourceMap) {
@@ -63,6 +63,8 @@ SourceMapConsumer.prototype._version = 3;
 
 SourceMapConsumer.prototype.__generatedMappings = null;
 Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', {
+  configurable: true,
+  enumerable: true,
   get: function () {
     if (!this.__generatedMappings) {
       this._parseMappings(this._mappings, this.sourceRoot);
@@ -74,6 +76,8 @@ Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', {
 
 SourceMapConsumer.prototype.__originalMappings = null;
 Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', {
+  configurable: true,
+  enumerable: true,
   get: function () {
     if (!this.__originalMappings) {
       this._parseMappings(this._mappings, this.sourceRoot);
@@ -141,9 +145,7 @@ SourceMapConsumer.prototype.eachMapping =
     var sourceRoot = this.sourceRoot;
     mappings.map(function (mapping) {
       var source = mapping.source === null ? null : this._sources.at(mapping.source);
-      if (source != null && sourceRoot != null) {
-        source = util.join(sourceRoot, source);
-      }
+      source = util.computeSourceURL(sourceRoot, source, this._sourceMapURL);
       return {
         source: source,
         generatedLine: mapping.generatedLine,
@@ -166,13 +168,16 @@ SourceMapConsumer.prototype.eachMapping =
  * The only argument is an object with the following properties:
  *
  *   - source: The filename of the original source.
- *   - line: The line number in the original source.
+ *   - line: The line number in the original source.  The line number is 1-based.
  *   - column: Optional. the column number in the original source.
+ *    The column number is 0-based.
  *
  * and an array of objects is returned, each with the following properties:
  *
- *   - line: The line number in the generated source, or null.
+ *   - line: The line number in the generated source, or null.  The
+ *    line number is 1-based.
  *   - column: The column number in the generated source, or null.
+ *    The column number is 0-based.
  */
 SourceMapConsumer.prototype.allGeneratedPositionsFor =
   function SourceMapConsumer_allGeneratedPositionsFor(aArgs) {
@@ -254,7 +259,7 @@ exports.SourceMapConsumer = SourceMapConsumer;
  * query for information about the original file positions by giving it a file
  * position in the generated source.
  *
- * The only parameter is the raw source map (either as a JSON string, or
+ * The first parameter is the raw source map (either as a JSON string, or
  * already parsed to an object). According to the spec, source maps have the
  * following attributes:
  *
@@ -277,12 +282,16 @@ exports.SourceMapConsumer = SourceMapConsumer;
  *       mappings: "AA,AB;;ABCDE;"
  *     }
  *
+ * The second parameter, if given, is a string whose value is the URL
+ * at which the source map was found.  This URL is used to compute the
+ * sources array.
+ *
  * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#
  */
-function BasicSourceMapConsumer(aSourceMap) {
+function BasicSourceMapConsumer(aSourceMap, aSourceMapURL) {
   var sourceMap = aSourceMap;
   if (typeof aSourceMap === 'string') {
-    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+    sourceMap = util.parseSourceMapInput(aSourceMap);
   }
 
   var version = util.getArg(sourceMap, 'version');
@@ -301,6 +310,10 @@ function BasicSourceMapConsumer(aSourceMap) {
     throw new Error('Unsupported version: ' + version);
   }
 
+  if (sourceRoot) {
+    sourceRoot = util.normalize(sourceRoot);
+  }
+
   sources = sources
     .map(String)
     // Some source maps produce relative source paths like "./foo.js" instead of
@@ -327,6 +340,7 @@ function BasicSourceMapConsumer(aSourceMap) {
   this.sourceRoot = sourceRoot;
   this.sourcesContent = sourcesContent;
   this._mappings = mappings;
+  this._sourceMapURL = aSourceMapURL;
   this.file = file;
 }
 
@@ -338,10 +352,12 @@ BasicSourceMapConsumer.prototype.consumer = SourceMapConsumer;
  *
  * @param SourceMapGenerator aSourceMap
  *        The source map that will be consumed.
+ * @param String aSourceMapURL
+ *        The URL at which the source map can be found (optional)
  * @returns BasicSourceMapConsumer
  */
 BasicSourceMapConsumer.fromSourceMap =
-  function SourceMapConsumer_fromSourceMap(aSourceMap) {
+  function SourceMapConsumer_fromSourceMap(aSourceMap, aSourceMapURL) {
     var smc = Object.create(BasicSourceMapConsumer.prototype);
 
     var names = smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);
@@ -350,6 +366,7 @@ BasicSourceMapConsumer.fromSourceMap =
     smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(),
                                                             smc.sourceRoot);
     smc.file = aSourceMap._file;
+    smc._sourceMapURL = aSourceMapURL;
 
     // Because we are modifying the entries (by converting string sources and
     // names to indices into the sources and names ArraySets), we have to make
@@ -397,7 +414,7 @@ BasicSourceMapConsumer.prototype._version = 3;
 Object.defineProperty(BasicSourceMapConsumer.prototype, 'sources', {
   get: function () {
     return this._sources.toArray().map(function (s) {
-      return this.sourceRoot != null ? util.join(this.sourceRoot, s) : s;
+      return util.computeSourceURL(this.sourceRoot, s, this._sourceMapURL);
     }, this);
   }
 });
@@ -579,8 +596,10 @@ BasicSourceMapConsumer.prototype.computeColumnSpans =
  * source's line and column positions provided. The only argument is an object
  * with the following properties:
  *
- *   - line: The line number in the generated source.
- *   - column: The column number in the generated source.
+ *   - line: The line number in the generated source.  The line number
+ *     is 1-based.
+ *   - column: The column number in the generated source.  The column
+ *     number is 0-based.
  *   - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or
  *     'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the
  *     closest element that is smaller than or greater than the one we are
@@ -590,8 +609,10 @@ BasicSourceMapConsumer.prototype.computeColumnSpans =
  * and an object is returned with the following properties:
  *
  *   - source: The original source file, or null.
- *   - line: The line number in the original source, or null.
- *   - column: The column number in the original source, or null.
+ *   - line: The line number in the original source, or null.  The
+ *     line number is 1-based.
+ *   - column: The column number in the original source, or null.  The
+ *     column number is 0-based.
  *   - name: The original identifier, or null.
  */
 BasicSourceMapConsumer.prototype.originalPositionFor =
@@ -617,9 +638,7 @@ BasicSourceMapConsumer.prototype.originalPositionFor =
         var source = util.getArg(mapping, 'source', null);
         if (source !== null) {
           source = this._sources.at(source);
-          if (this.sourceRoot != null) {
-            source = util.join(this.sourceRoot, source);
-          }
+          source = util.computeSourceURL(this.sourceRoot, source, this._sourceMapURL);
         }
         var name = util.getArg(mapping, 'name', null);
         if (name !== null) {
@@ -666,12 +685,23 @@ BasicSourceMapConsumer.prototype.sourceContentFor =
       return null;
     }
 
+    var relativeSource = aSource;
     if (this.sourceRoot != null) {
-      aSource = util.relative(this.sourceRoot, aSource);
+      relativeSource = util.relative(this.sourceRoot, relativeSource);
+    }
+
+    if (this._sources.has(relativeSource)) {
+      return this.sourcesContent[this._sources.indexOf(relativeSource)];
     }
 
-    if (this._sources.has(aSource)) {
-      return this.sourcesContent[this._sources.indexOf(aSource)];
+    // Maybe aSource is an absolute URL as returned by |sources|.  In
+    // this case we can't simply undo the transform.
+    var sourceArray = this.sources;
+    var i;
+    for (i = 0; i < sourceArray.length; ++i) {
+      if (sourceArray[i] == aSource) {
+        return this.sourcesContent[i];
+      }
     }
 
     var url;
@@ -681,15 +711,15 @@ BasicSourceMapConsumer.prototype.sourceContentFor =
       // many users. We can help them out when they expect file:// URIs to
       // behave like it would if they were running a local HTTP server. See
       // https://bugzilla.mozilla.org/show_bug.cgi?id=885597.
-      var fileUriAbsPath = aSource.replace(/^file:\/\//, "");
+      var fileUriAbsPath = relativeSource.replace(/^file:\/\//, "");
       if (url.scheme == "file"
           && this._sources.has(fileUriAbsPath)) {
         return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)]
       }
 
       if ((!url.path || url.path == "/")
-          && this._sources.has("/" + aSource)) {
-        return this.sourcesContent[this._sources.indexOf("/" + aSource)];
+          && this._sources.has("/" + relativeSource)) {
+        return this.sourcesContent[this._sources.indexOf("/" + relativeSource)];
       }
     }
 
@@ -701,7 +731,7 @@ BasicSourceMapConsumer.prototype.sourceContentFor =
       return null;
     }
     else {
-      throw new Error('"' + aSource + '" is not in the SourceMap.');
+      throw new Error('"' + relativeSource + '" is not in the SourceMap.');
     }
   };
 
@@ -711,8 +741,10 @@ BasicSourceMapConsumer.prototype.sourceContentFor =
  * the following properties:
  *
  *   - source: The filename of the original source.
- *   - line: The line number in the original source.
- *   - column: The column number in the original source.
+ *   - line: The line number in the original source.  The line number
+ *     is 1-based.
+ *   - column: The column number in the original source.  The column
+ *     number is 0-based.
  *   - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or
  *     'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the
  *     closest element that is smaller than or greater than the one we are
@@ -721,8 +753,10 @@ BasicSourceMapConsumer.prototype.sourceContentFor =
  *
  * and an object is returned with the following properties:
  *
- *   - line: The line number in the generated source, or null.
+ *   - line: The line number in the generated source, or null.  The
+ *     line number is 1-based.
  *   - column: The column number in the generated source, or null.
+ *     The column number is 0-based.
  */
 BasicSourceMapConsumer.prototype.generatedPositionFor =
   function SourceMapConsumer_generatedPositionFor(aArgs) {
@@ -781,7 +815,7 @@ exports.BasicSourceMapConsumer = BasicSourceMapConsumer;
  * that it takes "indexed" source maps (i.e. ones with a "sections" field) as
  * input.
  *
- * The only parameter is a raw source map (either as a JSON string, or already
+ * The first parameter is a raw source map (either as a JSON string, or already
  * parsed to an object). According to the spec for indexed source maps, they
  * have the following attributes:
  *
@@ -818,12 +852,16 @@ exports.BasicSourceMapConsumer = BasicSourceMapConsumer;
  *    }],
  *  }
  *
+ * The second parameter, if given, is a string whose value is the URL
+ * at which the source map was found.  This URL is used to compute the
+ * sources array.
+ *
  * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.535es3xeprgt
  */
-function IndexedSourceMapConsumer(aSourceMap) {
+function IndexedSourceMapConsumer(aSourceMap, aSourceMapURL) {
   var sourceMap = aSourceMap;
   if (typeof aSourceMap === 'string') {
-    sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+    sourceMap = util.parseSourceMapInput(aSourceMap);
   }
 
   var version = util.getArg(sourceMap, 'version');
@@ -863,7 +901,7 @@ function IndexedSourceMapConsumer(aSourceMap) {
         generatedLine: offsetLine + 1,
         generatedColumn: offsetColumn + 1
       },
-      consumer: new SourceMapConsumer(util.getArg(s, 'map'))
+      consumer: new SourceMapConsumer(util.getArg(s, 'map'), aSourceMapURL)
     }
   });
 }
@@ -896,14 +934,18 @@ Object.defineProperty(IndexedSourceMapConsumer.prototype, 'sources', {
  * source's line and column positions provided. The only argument is an object
  * with the following properties:
  *
- *   - line: The line number in the generated source.
- *   - column: The column number in the generated source.
+ *   - line: The line number in the generated source.  The line number
+ *     is 1-based.
+ *   - column: The column number in the generated source.  The column
+ *     number is 0-based.
  *
  * and an object is returned with the following properties:
  *
  *   - source: The original source file, or null.
- *   - line: The line number in the original source, or null.
- *   - column: The column number in the original source, or null.
+ *   - line: The line number in the original source, or null.  The
+ *     line number is 1-based.
+ *   - column: The column number in the original source, or null.  The
+ *     column number is 0-based.
  *   - name: The original identifier, or null.
  */
 IndexedSourceMapConsumer.prototype.originalPositionFor =
@@ -987,13 +1029,17 @@ IndexedSourceMapConsumer.prototype.sourceContentFor =
  * the following properties:
  *
  *   - source: The filename of the original source.
- *   - line: The line number in the original source.
- *   - column: The column number in the original source.
+ *   - line: The line number in the original source.  The line number
+ *     is 1-based.
+ *   - column: The column number in the original source.  The column
+ *     number is 0-based.
  *
  * and an object is returned with the following properties:
  *
- *   - line: The line number in the generated source, or null.
+ *   - line: The line number in the generated source, or null.  The
+ *     line number is 1-based. 
  *   - column: The column number in the generated source, or null.
+ *     The column number is 0-based.
  */
 IndexedSourceMapConsumer.prototype.generatedPositionFor =
   function IndexedSourceMapConsumer_generatedPositionFor(aArgs) {
@@ -1041,15 +1087,16 @@ IndexedSourceMapConsumer.prototype._parseMappings =
         var mapping = sectionMappings[j];
 
         var source = section.consumer._sources.at(mapping.source);
-        if (section.consumer.sourceRoot !== null) {
-          source = util.join(section.consumer.sourceRoot, source);
-        }
+        source = util.computeSourceURL(section.consumer.sourceRoot, source, this._sourceMapURL);
         this._sources.add(source);
         source = this._sources.indexOf(source);
 
-        var name = section.consumer._names.at(mapping.name);
-        this._names.add(name);
-        name = this._names.indexOf(name);
+        var name = null;
+        if (mapping.name) {
+          name = section.consumer._names.at(mapping.name);
+          this._names.add(name);
+          name = this._names.indexOf(name);
+        }
 
         // The mappings coming from the consumer for the section have
         // generated positions relative to the start of the section, so we
diff --git a/lib/source-map-generator.js b/lib/source-map-generator.js
index aff1e7f..508bcfb 100644
--- a/lib/source-map-generator.js
+++ b/lib/source-map-generator.js
@@ -72,6 +72,15 @@ SourceMapGenerator.fromSourceMap =
       generator.addMapping(newMapping);
     });
     aSourceMapConsumer.sources.forEach(function (sourceFile) {
+      var sourceRelative = sourceFile;
+      if (sourceRoot !== null) {
+        sourceRelative = util.relative(sourceRoot, sourceFile);
+      }
+
+      if (!generator._sources.has(sourceRelative)) {
+        generator._sources.add(sourceRelative);
+      }
+
       var content = aSourceMapConsumer.sourceContentFor(sourceFile);
       if (content != null) {
         generator.setSourceContent(sourceFile, content);
diff --git a/lib/source-node.js b/lib/source-node.js
index d196a53..8bcdbe3 100644
--- a/lib/source-node.js
+++ b/lib/source-node.js
@@ -97,7 +97,7 @@ SourceNode.fromStringWithSourceMap =
           // There is no new line in between.
           // Associate the code between "lastGeneratedColumn" and
           // "mapping.generatedColumn" with "lastMapping"
-          var nextLine = remainingLines[remainingLinesIndex];
+          var nextLine = remainingLines[remainingLinesIndex] || '';
           var code = nextLine.substr(0, mapping.generatedColumn -
                                         lastGeneratedColumn);
           remainingLines[remainingLinesIndex] = nextLine.substr(mapping.generatedColumn -
@@ -117,7 +117,7 @@ SourceNode.fromStringWithSourceMap =
         lastGeneratedLine++;
       }
       if (lastGeneratedColumn < mapping.generatedColumn) {
-        var nextLine = remainingLines[remainingLinesIndex];
+        var nextLine = remainingLines[remainingLinesIndex] || '';
         node.add(nextLine.substr(0, mapping.generatedColumn));
         remainingLines[remainingLinesIndex] = nextLine.substr(mapping.generatedColumn);
         lastGeneratedColumn = mapping.generatedColumn;
diff --git a/lib/util.js b/lib/util.js
index 44e0e45..3ca92e5 100644
--- a/lib/util.js
+++ b/lib/util.js
@@ -26,7 +26,7 @@ function getArg(aArgs, aName, aDefaultValue) {
 }
 exports.getArg = getArg;
 
-var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/;
+var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/;
 var dataUrlRegexp = /^data:.+\,.+$/;
 
 function urlParse(aUrl) {
@@ -182,7 +182,7 @@ function join(aRoot, aPath) {
 exports.join = join;
 
 exports.isAbsolute = function (aPath) {
-  return aPath.charAt(0) === '/' || !!aPath.match(urlRegexp);
+  return aPath.charAt(0) === '/' || urlRegexp.test(aPath);
 };
 
 /**
@@ -302,7 +302,7 @@ function isProtoString(s) {
  * stubbed out mapping.
  */
 function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
-  var cmp = mappingA.source - mappingB.source;
+  var cmp = strcmp(mappingA.source, mappingB.source);
   if (cmp !== 0) {
     return cmp;
   }
@@ -327,7 +327,7 @@ function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
     return cmp;
   }
 
-  return mappingA.name - mappingB.name;
+  return strcmp(mappingA.name, mappingB.name);
 }
 exports.compareByOriginalPositions = compareByOriginalPositions;
 
@@ -351,7 +351,7 @@ function compareByGeneratedPositionsDeflated(mappingA, mappingB, onlyCompareGene
     return cmp;
   }
 
-  cmp = mappingA.source - mappingB.source;
+  cmp = strcmp(mappingA.source, mappingB.source);
   if (cmp !== 0) {
     return cmp;
   }
@@ -366,7 +366,7 @@ function compareByGeneratedPositionsDeflated(mappingA, mappingB, onlyCompareGene
     return cmp;
   }
 
-  return mappingA.name - mappingB.name;
+  return strcmp(mappingA.name, mappingB.name);
 }
 exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated;
 
@@ -375,6 +375,14 @@ function strcmp(aStr1, aStr2) {
     return 0;
   }
 
+  if (aStr1 === null) {
+    return 1; // aStr2 !== null
+  }
+
+  if (aStr2 === null) {
+    return -1; // aStr1 !== null
+  }
+
   if (aStr1 > aStr2) {
     return 1;
   }
@@ -415,3 +423,66 @@ function compareByGeneratedPositionsInflated(mappingA, mappingB) {
   return strcmp(mappingA.name, mappingB.name);
 }
 exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated;
+
+/**
+ * Strip any JSON XSSI avoidance prefix from the string (as documented
+ * in the source maps specification), and then parse the string as
+ * JSON.
+ */
+function parseSourceMapInput(str) {
+  return JSON.parse(str.replace(/^\)]}'[^\n]*\n/, ''));
+}
+exports.parseSourceMapInput = parseSourceMapInput;
+
+/**
+ * Compute the URL of a source given the the source root, the source's
+ * URL, and the source map's URL.
+ */
+function computeSourceURL(sourceRoot, sourceURL, sourceMapURL) {
+  sourceURL = sourceURL || '';
+
+  if (sourceRoot) {
+    // This follows what Chrome does.
+    if (sourceRoot[sourceRoot.length - 1] !== '/' && sourceURL[0] !== '/') {
+      sourceRoot += '/';
+    }
+    // The spec says:
+    //   Line 4: An optional source root, useful for relocating source
+    //   files on a server or removing repeated values in the
+    //   “sources” entry.  This value is prepended to the individual
+    //   entries in the “source” field.
+    sourceURL = sourceRoot + sourceURL;
+  }
+
+  // Historically, SourceMapConsumer did not take the sourceMapURL as
+  // a parameter.  This mode is still somewhat supported, which is why
+  // this code block is conditional.  However, it's preferable to pass
+  // the source map URL to SourceMapConsumer, so that this function
+  // can implement the source URL resolution algorithm as outlined in
+  // the spec.  This block is basically the equivalent of:
+  //    new URL(sourceURL, sourceMapURL).toString()
+  // ... except it avoids using URL, which wasn't available in the
+  // older releases of node still supported by this library.
+  //
+  // The spec says:
+  //   If the sources are not absolute URLs after prepending of the
+  //   “sourceRoot”, the sources are resolved relative to the
+  //   SourceMap (like resolving script src in a html document).
+  if (sourceMapURL) {
+    var parsed = urlParse(sourceMapURL);
+    if (!parsed) {
+      throw new Error("sourceMapURL could not be parsed");
+    }
+    if (parsed.path) {
+      // Strip the last path component, but keep the "/".
+      var index = parsed.path.lastIndexOf('/');
+      if (index >= 0) {
+        parsed.path = parsed.path.substring(0, index + 1);
+      }
+    }
+    sourceURL = join(urlGenerate(parsed), sourceURL);
+  }
+
+  return normalize(sourceURL);
+}
+exports.computeSourceURL = computeSourceURL;
diff --git a/package.json b/package.json
index 048e3ae..7e64fd2 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "source-map",
   "description": "Generates and consumes source maps",
-  "version": "0.5.7",
+  "version": "0.6.0",
   "homepage": "https://github.com/mozilla/source-map",
   "author": "Nick Fitzgerald <nfitzgerald at mozilla.com>",
   "contributors": [
@@ -49,6 +49,7 @@
   "main": "./source-map.js",
   "files": [
     "source-map.js",
+    "source-map.d.ts",
     "lib/",
     "dist/source-map.debug.js",
     "dist/source-map.js",
diff --git a/test/test-source-map-consumer.js b/test/test-source-map-consumer.js
index 16b5915..8b59ce7 100644
--- a/test/test-source-map-consumer.js
+++ b/test/test-source-map-consumer.js
@@ -236,7 +236,7 @@ exports['test mappings and end of lines'] = function (assert) {
 
 exports['test creating source map consumers with )]}\' prefix'] = function (assert) {
   assert.doesNotThrow(function () {
-    var map = new SourceMapConsumer(")]}'" + JSON.stringify(util.testMap));
+    var map = new SourceMapConsumer(")]}'\n" + JSON.stringify(util.testMap));
   });
 };
 
@@ -821,26 +821,27 @@ exports['test github issue #56'] = function (assert) {
   assert.equal(sources[0], 'http://www.example.com/original.js');
 };
 
-exports['test github issue #43'] = function (assert) {
+// Was github issue #43, but that's no longer valid.
+exports['test source resolution with sourceMapURL'] = function (assert) {
   var map = new SourceMapGenerator({
-    sourceRoot: 'http://example.com',
+    sourceRoot: '',
     file: 'foo.js'
   });
   map.addMapping({
     original: { line: 1, column: 1 },
     generated: { line: 2, column: 2 },
-    source: 'http://cdn.example.com/original.js'
+    source: 'original.js',
   });
-  map = new SourceMapConsumer(map.toString());
+  map = new SourceMapConsumer(map.toString(), 'http://cdn.example.com');
 
   var sources = map.sources;
   assert.equal(sources.length, 1,
                'Should only be one source.');
   assert.equal(sources[0], 'http://cdn.example.com/original.js',
-               'Should not be joined with the sourceRoot.');
+               'Should be joined with the source map URL.');
 };
 
-exports['test absolute path, but same host sources'] = function (assert) {
+exports['test sourceRoot prepending'] = function (assert) {
   var map = new SourceMapGenerator({
     sourceRoot: 'http://example.com/foo/bar',
     file: 'foo.js'
@@ -855,8 +856,8 @@ exports['test absolute path, but same host sources'] = function (assert) {
   var sources = map.sources;
   assert.equal(sources.length, 1,
                'Should only be one source.');
-  assert.equal(sources[0], 'http://example.com/original.js',
-               'Source should be relative the host of the source root.');
+  assert.equal(sources[0], 'http://example.com/foo/bar/original.js',
+               'Source include the source root.');
 };
 
 exports['test indexed source map errors when sections are out of order by line'] = function(assert) {
@@ -888,6 +889,22 @@ exports['test github issue #64'] = function (assert) {
   assert.equal(map.sourceContentFor("/a"), "foo");
 };
 
+exports['test full source content with sourceMapURL'] = function (assert) {
+  var map = new SourceMapConsumer({
+    'version': 3,
+    'file': 'foo.js',
+    'sourceRoot': '',
+    'sources': ['original.js'],
+    'names': [],
+    'mappings': 'AACA',
+    'sourcesContent': ['yellow warbler']
+  }, 'http://cdn.example.com');
+
+  var sources = map.sources;
+  assert.equal(map.sourceContentFor('http://cdn.example.com/original.js'), 'yellow warbler',
+               'Source content should be found using full URL');
+};
+
 exports['test bug 885597'] = function (assert) {
   var map = new SourceMapConsumer({
     "version": 3,
@@ -1119,3 +1136,93 @@ exports['test consuming names and sources that are numbers'] = function (assert)
   });
   assert.equal(i, 1);
 };
+
+exports['test non-normalized sourceRoot (from issue #227)'] = function (assert) {
+  var consumer = new SourceMapConsumer({
+    version: 3,
+    sources: [ 'index.js' ],
+    names: [],
+    mappings: ';;AAAA,IAAI,OAAO,MAAP',
+    file: 'index.js',
+    sourceRoot: './src/',
+    sourcesContent: [ 'var name = "Mark"\n' ]
+  });
+  assert.equal(consumer.sourceRoot, 'src/', 'sourceRoot was normalized');
+  // Before the fix, this threw an exception.
+  consumer.sourceContentFor(consumer.sources[0]);
+};
+
+exports['test webpack URL resolution'] = function (assert) {
+  var map = {
+    version: 3,
+    sources:  ["webpack:///webpack/bootstrap 67e184f9679733298d44"],
+    names: [],
+    mappings: "CAAS",
+    file: "static/js/manifest.b7cf97680f7a50fa150f.js",
+    sourceRoot: ""
+  };
+  var consumer = new SourceMapConsumer(map);
+
+  assert.equal(consumer.sources.length, 1);
+  assert.equal(consumer.sources[0], "webpack:///webpack/bootstrap 67e184f9679733298d44");
+};
+
+exports['test webpack URL resolution with sourceMapURL'] = function (assert) {
+  var map = {
+    version: 3,
+    sources:  ["webpack:///webpack/bootstrap 67e184f9679733298d44"],
+    names: [],
+    mappings: "CAAS",
+    file: "static/js/manifest.b7cf97680f7a50fa150f.js",
+    sourceRoot: ""
+  };
+  var consumer = new SourceMapConsumer(map, 'http://www.example.com/q.js.map');
+
+  assert.equal(consumer.sources.length, 1);
+  assert.equal(consumer.sources[0], "webpack:///webpack/bootstrap 67e184f9679733298d44");
+};
+
+exports['test relative webpack URL resolution with sourceMapURL'] = function (assert) {
+  var map = {
+    version: 3,
+    sources:  ["webpack/bootstrap.js"],
+    names: [],
+    mappings: "CAAS",
+    file: "static/js/manifest.b7cf97680f7a50fa150f.js",
+    sourceRoot: "webpack:///"
+  };
+  var consumer = new SourceMapConsumer(map, 'http://www.example.com/q.js.map');
+
+  assert.equal(consumer.sources.length, 1);
+  assert.equal(consumer.sources[0], "webpack:///webpack/bootstrap.js");
+};
+
+exports['test basic URL resolution with sourceMapURL'] = function (assert) {
+  var map = {
+    version: 3,
+    sources:  ["something.js"],
+    names: [],
+    mappings: "CAAS",
+    file: "static/js/manifest.b7cf97680f7a50fa150f.js",
+    sourceRoot: "src"
+  };
+  var consumer = new SourceMapConsumer(map, 'http://www.example.com/x/q.js.map');
+
+  assert.equal(consumer.sources.length, 1);
+  assert.equal(consumer.sources[0], 'http://www.example.com/x/src/something.js');
+};
+
+exports['test absolute sourceURL resolution with sourceMapURL'] = function (assert) {
+  var map = {
+    version: 3,
+    sources:  ["something.js"],
+    names: [],
+    mappings: "CAAS",
+    file: "static/js/manifest.b7cf97680f7a50fa150f.js",
+    sourceRoot: "http://www.example.com/src"
+  };
+  var consumer = new SourceMapConsumer(map, 'http://www.example.com/x/q.js.map');
+
+  assert.equal(consumer.sources.length, 1);
+  assert.equal(consumer.sources[0], 'http://www.example.com/src/something.js');
+};
diff --git a/test/test-source-map-generator.js b/test/test-source-map-generator.js
index 2d37daa..d05d9fe 100644
--- a/test/test-source-map-generator.js
+++ b/test/test-source-map-generator.js
@@ -252,6 +252,30 @@ exports['test .fromSourceMap with sourcesContent'] = function (assert) {
   util.assertEqualMaps(assert, map.toJSON(), util.testMapWithSourcesContent);
 };
 
+exports['test .fromSourceMap with single source'] = function (assert) {
+  var map = SourceMapGenerator.fromSourceMap(
+      new SourceMapConsumer(util.testMapSingleSource));
+  util.assertEqualMaps(assert, map.toJSON(), util.testMapSingleSource);
+};
+
+exports['test .fromSourceMap with empty mappings'] = function (assert) {
+  var map = SourceMapGenerator.fromSourceMap(
+    new SourceMapConsumer(util.testMapEmptyMappings));
+  util.assertEqualMaps(assert, map.toJSON(), util.testMapEmptyMappings);
+};
+
+exports['test .fromSourceMap with empty mappings and relative sources'] = function (assert) {
+  var map = SourceMapGenerator.fromSourceMap(
+    new SourceMapConsumer(util.testMapEmptyMappingsRelativeSources));
+  util.assertEqualMaps(assert, map.toJSON(), util.testMapEmptyMappingsRelativeSources_generatedExpected);
+};
+
+exports['test .fromSourceMap with multiple sources where mappings refers only to single source'] = function (assert) {
+    var map = SourceMapGenerator.fromSourceMap(
+        new SourceMapConsumer(util.testMapMultiSourcesMappingRefersSingleSourceOnly));
+    util.assertEqualMaps(assert, map.toJSON(), util.testMapMultiSourcesMappingRefersSingleSourceOnly);
+};
+
 exports['test applySourceMap'] = function (assert) {
   var node = new SourceNode(null, null, null, [
     new SourceNode(2, 0, 'fileX', 'lineX2\n'),
diff --git a/test/test-source-node.js b/test/test-source-node.js
index a9351ac..0bc258c 100644
--- a/test/test-source-node.js
+++ b/test/test-source-node.js
@@ -278,7 +278,6 @@ exports['test .fromStringWithSourceMap() third argument'] = function (assert) {
   // Assume the following directory structure:
   //
   // http://foo.org/
-  //   bar.coffee
   //   app/
   //     coffee/
   //       foo.coffee
@@ -290,14 +289,9 @@ exports['test .fromStringWithSourceMap() third argument'] = function (assert) {
   //     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: '..'
@@ -325,8 +319,6 @@ exports['test .fromStringWithSourceMap() third argument'] = function (assert) {
 
   test('../coffee/maps', [
     '../coffee/foo.coffee',
-    '/bar.coffee',
-    'http://www.example.com/baz.coffee',
     'foo.js'
   ]);
 
@@ -335,29 +327,21 @@ exports['test .fromStringWithSourceMap() third argument'] = function (assert) {
 
   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'
   ]);
 };
@@ -606,3 +590,18 @@ exports['test walkSourceContents'] = function (assert) {
   assert.equal(results[1][0], 'b.js');
   assert.equal(results[1][1], 'otherContent');
 };
+
+exports['test from issue 258'] = function (assert) {
+  var node = new SourceNode();
+
+  var reactCode =
+      ";require(0);\n//# sourceMappingURL=/index.ios.map?platform=ios&dev=false&minify=true";
+
+  var reactMap =
+      "{\"version\":3,\"file\":\"/index.ios.bundle?platform=ios&dev=false&minify=true\",\"sections\":[{\"offset\":{\"line\":0,\"column\":0},\"map\":{\"version\":3,\"sources\":[\"require-0.js\"],\"names\":[],\"mappings\":\"AAAA;\",\"file\":\"require-0.js\",\"sourcesContent\":[\";require(0);\"]}}]}";
+
+  node.add(SourceNode.fromStringWithSourceMap(
+    reactCode,
+    new SourceMapConsumer(reactMap)
+  ));
+}
diff --git a/test/test-util.js b/test/test-util.js
index 5c007e8..8e7517e 100644
--- a/test/test-util.js
+++ b/test/test-util.js
@@ -32,6 +32,18 @@ exports['test urls'] = function (assert) {
   assert.equal(libUtil.urlParse('a//b'), null);
   assert.equal(libUtil.urlParse('/a'), null);
   assert.equal(libUtil.urlParse('data:foo,bar'), null);
+
+  var parsed = libUtil.urlParse('http://x-y.com/bar');
+  assert.equal(parsed.scheme, 'http');
+  assert.equal(parsed.host, 'x-y.com');
+  assert.equal(parsed.path, '/bar');
+
+  var webpackURL = 'webpack:///webpack/bootstrap 67e184f9679733298d44'
+  parsed = libUtil.urlParse(webpackURL);
+  assert.equal(parsed.scheme, 'webpack');
+  assert.equal(parsed.host, '');
+  assert.equal(parsed.path, '/webpack/bootstrap 67e184f9679733298d44');
+  assert.equal(webpackURL, libUtil.urlGenerate(parsed));
 };
 
 exports['test normalize()'] = function (assert) {
@@ -212,3 +224,35 @@ exports['test relative()'] = function (assert) {
   assert.equal(libUtil.relative('/', '/the/root/one.js'), 'the/root/one.js');
   assert.equal(libUtil.relative('/', 'the/root/one.js'), 'the/root/one.js');
 };
+
+exports['test computeSourceURL'] = function (assert) {
+  // Tests with sourceMapURL.
+  assert.equal(libUtil.computeSourceURL('', 'src/test.js', 'http://example.com'),
+               'http://example.com/src/test.js');
+  assert.equal(libUtil.computeSourceURL(undefined, 'src/test.js', 'http://example.com'),
+               'http://example.com/src/test.js');
+  assert.equal(libUtil.computeSourceURL('src', 'test.js', 'http://example.com'),
+               'http://example.com/src/test.js');
+  assert.equal(libUtil.computeSourceURL('src/', 'test.js', 'http://example.com'),
+               'http://example.com/src/test.js');
+  assert.equal(libUtil.computeSourceURL('src', '/test.js', 'http://example.com'),
+               'http://example.com/src/test.js');
+  assert.equal(libUtil.computeSourceURL('http://mozilla.com', 'src/test.js', 'http://example.com'),
+               'http://mozilla.com/src/test.js');
+  assert.equal(libUtil.computeSourceURL('', 'test.js', 'http://example.com/src/test.js.map'),
+               'http://example.com/src/test.js');
+
+  // Legacy code won't pass in the sourceMapURL.
+  assert.equal(libUtil.computeSourceURL('', 'src/test.js'), 'src/test.js');
+  assert.equal(libUtil.computeSourceURL(undefined, 'src/test.js'), 'src/test.js');
+  assert.equal(libUtil.computeSourceURL('src', 'test.js'), 'src/test.js');
+  assert.equal(libUtil.computeSourceURL('src/', 'test.js'), 'src/test.js');
+  assert.equal(libUtil.computeSourceURL('src', '/test.js'), 'src/test.js');
+  assert.equal(libUtil.computeSourceURL('src', '../test.js'), 'test.js');
+  assert.equal(libUtil.computeSourceURL('src/dir', '../././../test.js'), 'test.js');
+
+  // This gives different results with the old algorithm and the new
+  // spec-compliant algorithm.
+  assert.equal(libUtil.computeSourceURL('http://example.com/dir', '/test.js'),
+               'http://example.com/dir/test.js');
+};
diff --git a/test/util.js b/test/util.js
index 7bb3ab1..74b0dfc 100644
--- a/test/util.js
+++ b/test/util.js
@@ -51,6 +51,58 @@ exports.testMapEmptySourceRoot = {
   sourceRoot: '',
   mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
 };
+exports.testMapSingleSource = {
+  version: 3,
+  file: 'min.js',
+  names: ['bar', 'baz'],
+  sources: ['one.js'],
+  sourceRoot: '',
+  mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID'
+};
+exports.testMapEmptyMappings = {
+  version: 3,
+  file: 'min.js',
+  names: [],
+  sources: ['one.js', 'two.js'],
+  sourcesContent: [
+    ' ONE.foo = 1;',
+    ' TWO.inc = 2;'
+  ],
+  sourceRoot: '',
+  mappings: ''
+};
+exports.testMapEmptyMappingsRelativeSources = {
+  version: 3,
+  file: 'min.js',
+  names: [],
+  sources: ['./one.js', './two.js'],
+  sourcesContent: [
+    ' ONE.foo = 1;',
+    ' TWO.inc = 2;'
+  ],
+  sourceRoot: '/the/root',
+  mappings: ''
+};
+exports.testMapEmptyMappingsRelativeSources_generatedExpected = {
+  version: 3,
+  file: 'min.js',
+  names: [],
+  sources: ['one.js', 'two.js'],
+  sourcesContent: [
+    ' ONE.foo = 1;',
+    ' TWO.inc = 2;'
+  ],
+  sourceRoot: '/the/root',
+  mappings: ''
+};
+exports.testMapMultiSourcesMappingRefersSingleSourceOnly = {
+    version: 3,
+    file: 'min.js',
+    names: ['bar', 'baz'],
+    sources: ['one.js', 'withoutMappings.js'],
+    sourceRoot: '',
+    mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID'
+};
 // This mapping is identical to above, but uses the indexed format instead.
 exports.indexedTestMap = {
   version: 3,

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-source-map.git



More information about the Pkg-javascript-commits mailing list