diff --git a/toolkit/devtools/debugger/server/dbg-script-actors.js b/toolkit/devtools/debugger/server/dbg-script-actors.js index 30bdcaee7258..d2f0f4633017 100644 --- a/toolkit/devtools/debugger/server/dbg-script-actors.js +++ b/toolkit/devtools/debugger/server/dbg-script-actors.js @@ -1381,13 +1381,13 @@ PauseScopedActor.prototype = { * 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. + * @param aSourceMap SourceMapConsumer + * Optional. The source map that introduced this source, if available. */ -function SourceActor(aUrl, aThreadActor, aSourceContent=null) { +function SourceActor(aUrl, aThreadActor, aSourceMap=null) { this._threadActor = aThreadActor; this._url = aUrl; - this._sourceContent = aSourceContent; + this._sourceMap = aSourceMap; } SourceActor.prototype = { @@ -1415,25 +1415,33 @@ SourceActor.prototype = { * Handler for the "source" packet. */ onSource: function SA_onSource(aRequest) { - if (this._sourceContent) { + let sourceContent = null; + if (this._sourceMap) { + sourceContent = this._sourceMap.sourceContentFor(this._url); + } + + if (sourceContent) { return { from: this.actorID, source: this.threadActor.createValueGrip( - this._sourceContent, this.threadActor.threadLifetimePool) + sourceContent, this.threadActor.threadLifetimePool) }; } - return fetch(this._url) - .then(function(aSource) { + // XXX bug 865252: Don't load from the cache if this is a source mapped + // source because we can't guarantee that the cache has the most up to date + // content for this source like we can if it isn't source mapped. + return fetch(this._url, { loadFromCache: !this._sourceMap }) + .then((aSource) => { return this.threadActor.createValueGrip( aSource, this.threadActor.threadLifetimePool); - }.bind(this)) - .then(function (aSourceGrip) { + }) + .then((aSourceGrip) => { return { from: this.actorID, source: aSourceGrip }; - }.bind(this), function (aError) { + }, (aError) => { let msg = "Got an exception during SA_onSource: " + aError + "\n" + aError.stack; Cu.reportError(msg); @@ -1443,7 +1451,7 @@ SourceActor.prototype = { "error": "loadSourceError", "message": "Could not load the source for " + this._url + "." }; - }.bind(this)); + }); } }; @@ -2444,11 +2452,11 @@ ThreadSources.prototype = { * * @param String aURL * The source URL. - * @param String aSourceContent - * Optional. The content of the source, if we already know it. + * @param optional SourceMapConsumer aSourceMap + * The source map that introduced this source. * @returns a SourceActor representing the source or null. */ - source: function TS_source(aURL, aSourceContent=null) { + source: function TS_source(aURL, aSourceMap=null) { if (!this._allow(aURL)) { return null; } @@ -2457,7 +2465,7 @@ ThreadSources.prototype = { return this._sourceActors[aURL]; } - let actor = new SourceActor(aURL, this._thread, aSourceContent); + let actor = new SourceActor(aURL, this._thread, aSourceMap); this._thread.threadLifetimePool.addActor(actor); this._sourceActors[aURL] = actor; try { @@ -2479,8 +2487,7 @@ ThreadSources.prototype = { return this.sourceMap(aScript) .then((aSourceMap) => { return [ - this.source(s, aSourceMap.sourceContentFor(s)) - for (s of aSourceMap.sources) + this.source(s, aSourceMap) for (s of aSourceMap.sources) ]; }, (e) => { reportError(e); @@ -2654,7 +2661,7 @@ function isNotNull(aThing) { * without relying on caching when we can (not for eval, etc.): * http://www.softwareishard.com/blog/firebug/nsitraceablechannel-intercept-http-traffic/ */ -function fetch(aURL) { +function fetch(aURL, aOptions={ loadFromCache: true }) { let deferred = defer(); let scheme; let url = aURL.split(" -> ").pop(); @@ -2721,7 +2728,9 @@ function fetch(aURL) { } }; - channel.loadFlags = channel.LOAD_FROM_CACHE; + channel.loadFlags = aOptions.loadFromCache + ? channel.LOAD_FROM_CACHE + : channel.LOAD_BYPASS_CACHE; channel.asyncOpen(streamListener, null); break; } diff --git a/toolkit/devtools/debugger/tests/unit/head_dbg.js b/toolkit/devtools/debugger/tests/unit/head_dbg.js index 59906db28979..be6e9028f58b 100644 --- a/toolkit/devtools/debugger/tests/unit/head_dbg.js +++ b/toolkit/devtools/debugger/tests/unit/head_dbg.js @@ -207,3 +207,18 @@ function readFile(aFileName) { s.close(); } } + +function writeFile(aFileName, aContent) { + let file = do_get_file(aFileName, true); + let stream = Cc["@mozilla.org/network/file-output-stream;1"] + .createInstance(Ci.nsIFileOutputStream); + stream.init(file, -1, -1, 0); + try { + do { + let numWritten = stream.write(aContent, aContent.length); + aContent = aContent.slice(numWritten); + } while (aContent.length > 0); + } finally { + stream.close(); + } +} diff --git a/toolkit/devtools/debugger/tests/unit/test_sourcemaps-07.js b/toolkit/devtools/debugger/tests/unit/test_sourcemaps-07.js new file mode 100644 index 000000000000..f13fcb328211 --- /dev/null +++ b/toolkit/devtools/debugger/tests/unit/test_sourcemaps-07.js @@ -0,0 +1,67 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Test that we don't permanently cache sources from source maps. + */ + +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() { + attachTestGlobalClientAndResume(gClient, "test-source-map", function(aResponse, aThreadClient) { + gThreadClient = aThreadClient; + test_cached_original_sources(); + }); + }); + do_test_pending(); +} + +function test_cached_original_sources() +{ + writeFile("temp.foobar", "initial content"); + + gClient.addOneTimeListener("newSource", onNewSource); + + let node = new SourceNode(1, 0, + getFileUrl("temp.foobar"), + "function funcFromTemp() {}\n"); + 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 onNewSource(aEvent, aPacket) { + let sourceClient = gThreadClient.source(aPacket.source); + sourceClient.source(function (aResponse) { + do_check_true(!aResponse.error, + "Should not be an error grabbing the source"); + do_check_eq(aResponse.source, "initial content", + "The correct source content should be sent"); + + writeFile("temp.foobar", "new content"); + + sourceClient.source(function (aResponse) { + do_check_true(!aResponse.error, + "Should not be an error grabbing the source"); + do_check_eq(aResponse.source, "new content", + "The correct source content should not be cached, so we should get the new content"); + + do_get_file("temp.foobar").remove(false); + finishClient(gClient); + }); + }); +} diff --git a/toolkit/devtools/debugger/tests/unit/xpcshell.ini b/toolkit/devtools/debugger/tests/unit/xpcshell.ini index 5b314842616d..21bc733df10d 100644 --- a/toolkit/devtools/debugger/tests/unit/xpcshell.ini +++ b/toolkit/devtools/debugger/tests/unit/xpcshell.ini @@ -88,6 +88,9 @@ reason = bug 820380 skip-if = toolkit == "gonk" reason = bug 820380 [test_sourcemaps-06.js] +[test_sourcemaps-07.js] +skip-if = toolkit == "gonk" +reason = bug 820380 [test_objectgrips-01.js] [test_objectgrips-02.js] [test_objectgrips-03.js]