Bug 852792 - load sources from the 'sourcesContent' field in a source map, if available; r=rcampbell
authorNick Fitzgerald <fitzgen@gmail.com>
Mon, 13 May 2013 14:16:00 +0300
changeset 143894 fa63467ea33034052826d7fd9f5f85c07e037d0f
parent 143893 69598cd0437c2a8d72483402fd8e7862d3fac247
child 143895 0c312bf9446a310f5f6080a214ba12e9589b862b
push id2697
push userbbajaj@mozilla.com
push dateMon, 05 Aug 2013 18:49:53 +0000
treeherdermozilla-beta@dfec938c7b63 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrcampbell
bugs852792
milestone24.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 852792 - load sources from the 'sourcesContent' field in a source map, if available; r=rcampbell
toolkit/devtools/debugger/server/dbg-script-actors.js
toolkit/devtools/debugger/tests/unit/test_sourcemaps-06.js
toolkit/devtools/debugger/tests/unit/xpcshell.ini
--- a/toolkit/devtools/debugger/server/dbg-script-actors.js
+++ b/toolkit/devtools/debugger/server/dbg-script-actors.js
@@ -1376,20 +1376,23 @@ PauseScopedActor.prototype = {
 
 /**
  * A SourceActor provides information about the source of a script.
  *
  * @param aUrl String
  *        The url of the source we are representing.
  * @param aThreadActor ThreadActor
  *        The current thread actor.
+ * @param aSourceContent String
+ *        Optional. The contents of the source, if we already know it.
  */
-function SourceActor(aUrl, aThreadActor) {
+function SourceActor(aUrl, aThreadActor, aSourceContent=null) {
   this._threadActor = aThreadActor;
   this._url = aUrl;
+  this._sourceContent = aSourceContent;
 }
 
 SourceActor.prototype = {
   constructor: SourceActor,
   actorPrefix: "source",
 
   get threadActor() this._threadActor,
   get url() this._url,
@@ -1407,16 +1410,24 @@ SourceActor.prototype = {
       delete this.registeredPool.sourceActors[this.actorID];
     }
   },
 
   /**
    * Handler for the "source" packet.
    */
   onSource: function SA_onSource(aRequest) {
+    if (this._sourceContent) {
+      return {
+        from: this.actorID,
+        source: this.threadActor.createValueGrip(
+          this._sourceContent, this.threadActor.threadLifetimePool)
+      };
+    }
+
     return fetch(this._url)
       .then(function(aSource) {
         return this.threadActor.createValueGrip(
           aSource, this.threadActor.threadLifetimePool);
       }.bind(this))
       .then(function (aSourceGrip) {
         return {
           from: this.actorID,
@@ -2430,29 +2441,32 @@ function ThreadSources(aThreadActor, aUs
 
 ThreadSources.prototype = {
   /**
    * Add a source to the current set of sources.
    *
    * Right now this takes a URL, but in the future it should
    * take a Debugger.Source. See bug 637572.
    *
-   * @param string the source URL.
+   * @param String aURL
+   *        The source URL.
+   * @param String aSourceContent
+   *        Optional. The content of the source, if we already know it.
    * @returns a SourceActor representing the source or null.
    */
-  source: function TS_source(aURL) {
+  source: function TS_source(aURL, aSourceContent=null) {
     if (!this._allow(aURL)) {
       return null;
     }
 
     if (aURL in this._sourceActors) {
       return this._sourceActors[aURL];
     }
 
-    let actor = new SourceActor(aURL, this._thread);
+    let actor = new SourceActor(aURL, this._thread, aSourceContent);
     this._thread.threadLifetimePool.addActor(actor);
     this._sourceActors[aURL] = actor;
     try {
       this._onNewSource(actor);
     } catch (e) {
       reportError(e);
     }
     return actor;
@@ -2464,17 +2478,18 @@ ThreadSources.prototype = {
   sourcesForScript: function TS_sourcesForScript(aScript) {
     if (!this._useSourceMaps || !aScript.sourceMapURL) {
       return resolve([this.source(aScript.url)].filter(isNotNull));
     }
 
     return this.sourceMap(aScript)
       .then((aSourceMap) => {
         return [
-          this.source(s) for (s of aSourceMap.sources)
+          this.source(s, aSourceMap.sourceContentFor(s))
+          for (s of aSourceMap.sources)
         ];
       }, (e) => {
         reportError(e);
         delete this._sourceMaps[this._normalize(aScript.sourceMapURL, aScript.url)];
         delete this._sourceMapsByGeneratedSource[aScript.url];
         return [this.source(aScript.url)];
       })
       .then(function (aSources) {
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/debugger/tests/unit/test_sourcemaps-06.js
@@ -0,0 +1,86 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Check that we can load sources whose content is embedded in the
+ * "sourcesContent" field of a source map.
+ */
+
+var gDebuggee;
+var gClient;
+var gThreadClient;
+
+Components.utils.import("resource:///modules/devtools/SourceMap.jsm");
+
+function run_test()
+{
+  initTestDebuggerServer();
+  gDebuggee = addTestGlobal("test-source-map");
+  gClient = new DebuggerClient(DebuggerServer.connectPipe());
+  gClient.connect(function() {
+    attachTestTabAndResume(gClient, "test-source-map", function(aResponse, aTabClient, aThreadClient) {
+      gThreadClient = aThreadClient;
+      test_source_content();
+    });
+  });
+  do_test_pending();
+}
+
+function test_source_content()
+{
+  let numNewSources = 0;
+
+  gClient.addListener("newSource", function _onNewSource(aEvent, aPacket) {
+    if (++numNewSources !== 3) {
+      return;
+    }
+    gClient.removeListener("newSource", _onNewSource);
+
+    gThreadClient.getSources(function (aResponse) {
+      do_check_true(!aResponse.error, "Should not get an error");
+
+      testContents(aResponse.sources, () => {
+        finishClient(gClient);
+      });
+    });
+  });
+
+  let node = new SourceNode(null, null, null, [
+    new SourceNode(1, 0, "a.js", "function a() { return 'a'; }\n"),
+    new SourceNode(1, 0, "b.js", "function b() { return 'b'; }\n"),
+    new SourceNode(1, 0, "c.js", "function c() { return 'c'; }\n"),
+  ]);
+
+  node.setSourceContent("a.js", "content for a.js");
+  node.setSourceContent("b.js", "content for b.js");
+  node.setSourceContent("c.js", "content for c.js");
+
+  let { code, map } = node.toStringWithSourceMap({
+    file: "abc.js"
+  });
+
+  code += "//@ sourceMappingURL=data:text/json;base64," + btoa(map.toString());
+
+  Components.utils.evalInSandbox(code, gDebuggee, "1.8",
+                                 "http://example.com/www/js/abc.js", 1);
+}
+
+function testContents(aSources, aCallback) {
+  if (aSources.length === 0) {
+    return aCallback();
+  }
+
+  let source = aSources[0];
+  let sourceClient = gThreadClient.source(aSources[0]);
+
+  sourceClient.source((aResponse) => {
+    do_check_true(!aResponse.error,
+                  "Should not get an error loading the source from sourcesContent");
+
+    let expectedContent = "content for " + source.url;
+    do_check_eq(aResponse.source, expectedContent,
+                "Should have the expected source content");
+
+    testContents(aSources.slice(1), aCallback);
+  });
+}
--- a/toolkit/devtools/debugger/tests/unit/xpcshell.ini
+++ b/toolkit/devtools/debugger/tests/unit/xpcshell.ini
@@ -82,16 +82,17 @@ reason = bug 820380
 [test_sourcemaps-02.js]
 [test_sourcemaps-03.js]
 [test_sourcemaps-04.js]
 skip-if = toolkit == "gonk"
 reason = bug 820380
 [test_sourcemaps-05.js]
 skip-if = toolkit == "gonk"
 reason = bug 820380
+[test_sourcemaps-06.js]
 [test_objectgrips-01.js]
 [test_objectgrips-02.js]
 [test_objectgrips-03.js]
 [test_objectgrips-04.js]
 [test_interrupt.js]
 [test_stepping-01.js]
 [test_stepping-02.js]
 [test_stepping-03.js]