diff --git a/browser/devtools/debugger/test/browser.ini b/browser/devtools/debugger/test/browser.ini index 6ad9916f4d18..f8de4d322a0b 100644 --- a/browser/devtools/debugger/test/browser.ini +++ b/browser/devtools/debugger/test/browser.ini @@ -9,6 +9,7 @@ support-files = code_blackboxing_one.js code_blackboxing_three.js code_blackboxing_two.js + code_breakpoints-break-on-last-line-of-script-on-reload.js code_function-search-01.js code_function-search-02.js code_function-search-03.js @@ -34,6 +35,7 @@ support-files = doc_auto-pretty-print-02.html doc_binary_search.html doc_blackboxing.html + doc_breakpoints-break-on-last-line-of-script-on-reload.html doc_closures.html doc_cmd-break.html doc_cmd-dbg.html @@ -97,6 +99,7 @@ support-files = [browser_dbg_break-on-dom-07.js] [browser_dbg_break-on-dom-08.js] [browser_dbg_breakpoints-actual-location.js] +[browser_dbg_breakpoints-break-on-last-line-of-script-on-reload.js] [browser_dbg_breakpoints-button-01.js] [browser_dbg_breakpoints-button-02.js] [browser_dbg_breakpoints-contextmenu.js] diff --git a/browser/devtools/debugger/test/browser_dbg_breakpoints-break-on-last-line-of-script-on-reload.js b/browser/devtools/debugger/test/browser_dbg_breakpoints-break-on-last-line-of-script-on-reload.js new file mode 100644 index 000000000000..1b4e7d1719ae --- /dev/null +++ b/browser/devtools/debugger/test/browser_dbg_breakpoints-break-on-last-line-of-script-on-reload.js @@ -0,0 +1,79 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Bug 978019: Setting a breakpoint on the last line of a Debugger.Script and + * reloading should still hit the breakpoint. + */ + +const TAB_URL = EXAMPLE_URL + "doc_breakpoints-break-on-last-line-of-script-on-reload.html"; +const CODE_URL = EXAMPLE_URL + "code_breakpoints-break-on-last-line-of-script-on-reload.js"; + +const { promiseInvoke } = require("devtools/async-utils"); + +function test() { + let gPanel, gDebugger, gThreadClient, gEvents; + + initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => { + gPanel = aPanel; + gDebugger = gPanel.panelWin; + gThreadClient = gDebugger.gThreadClient; + gEvents = gDebugger.EVENTS; + + Task.spawn(function* () { + yield waitForSourceShown(gPanel, CODE_URL); + + // Pause and set our breakpoints. + yield doInterrupt(); + yield promise.all([ + gPanel.addBreakpoint({ + url: CODE_URL, + line: 2 + }), + gPanel.addBreakpoint({ + url: CODE_URL, + line: 3 + }), + gPanel.addBreakpoint({ + url: CODE_URL, + line: 4 + }) + ]); + + // Should hit the first breakpoint on reload. + yield promise.all([ + reloadActiveTab(gPanel, gEvents.SOURCE_SHOWN), + waitForCaretUpdated(gPanel, 2) + ]); + + // And should hit the other breakpoints as we resume. + yield promise.all([ + doResume(), + waitForCaretUpdated(gPanel, 3) + ]); + yield promise.all([ + doResume(), + waitForCaretUpdated(gPanel, 4) + ]); + + yield resumeDebuggerThenCloseAndFinish(gPanel); + }); + }); + + function rdpInvoke(obj, method) { + return promiseInvoke(obj, method) + .then(({error, message }) => { + if (error) { + throw new Error(error + ": " + message); + } + }); + } + + function doResume() { + return rdpInvoke(gThreadClient, gThreadClient.resume); + } + + function doInterrupt() { + return rdpInvoke(gThreadClient, gThreadClient.interrupt); + } +} diff --git a/browser/devtools/debugger/test/code_breakpoints-break-on-last-line-of-script-on-reload.js b/browser/devtools/debugger/test/code_breakpoints-break-on-last-line-of-script-on-reload.js new file mode 100644 index 000000000000..f2078e9c40b2 --- /dev/null +++ b/browser/devtools/debugger/test/code_breakpoints-break-on-last-line-of-script-on-reload.js @@ -0,0 +1,5 @@ +var a = (function(){ + var b = 9; + console.log("x", b); + return b; +})(); diff --git a/browser/devtools/debugger/test/doc_breakpoints-break-on-last-line-of-script-on-reload.html b/browser/devtools/debugger/test/doc_breakpoints-break-on-last-line-of-script-on-reload.html new file mode 100644 index 000000000000..c1730e5064b7 --- /dev/null +++ b/browser/devtools/debugger/test/doc_breakpoints-break-on-last-line-of-script-on-reload.html @@ -0,0 +1,8 @@ + + + + + Debugger Break on Last Line of Script on Reload Test Page + + diff --git a/browser/devtools/debugger/test/head.js b/browser/devtools/debugger/test/head.js index bf0ed6d1c099..743eb8c81013 100644 --- a/browser/devtools/debugger/test/head.js +++ b/browser/devtools/debugger/test/head.js @@ -16,6 +16,7 @@ let { Task } = Cu.import("resource://gre/modules/Task.jsm", {}); let { Promise: promise } = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {}); let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {}); let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}); +let { require } = devtools; let { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {}); let { BrowserToolboxProcess } = Cu.import("resource:///modules/devtools/ToolboxProcess.jsm", {}); let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {}); diff --git a/toolkit/devtools/server/actors/script.js b/toolkit/devtools/server/actors/script.js index 1ea86c66c1de..8ee9c102f23b 100644 --- a/toolkit/devtools/server/actors/script.js +++ b/toolkit/devtools/server/actors/script.js @@ -1892,8 +1892,7 @@ ThreadActor.prototype = { * @return The EnvironmentActor for aEnvironment or undefined for host * functions or functions scoped to a non-debuggee global. */ - createEnvironmentActor: - function (aEnvironment, aPool) { + createEnvironmentActor: function (aEnvironment, aPool) { if (!aEnvironment) { return undefined; } @@ -1954,8 +1953,7 @@ ThreadActor.prototype = { * Return a protocol completion value representing the given * Debugger-provided completion value. */ - createProtocolCompletionValue: - function (aCompletion) { + createProtocolCompletionValue: function (aCompletion) { let protoValue = {}; if ("return" in aCompletion) { protoValue.return = this.createValueGrip(aCompletion.return); @@ -2194,6 +2192,14 @@ ThreadActor.prototype = { */ onNewScript: function (aScript, aGlobal) { this._addScript(aScript); + + // |onNewScript| is only fired for top level scripts (AKA staticLevel == 0), + // so we have to make sure to call |_addScript| on every child script as + // well to restore breakpoints in those scripts. + for (let s of aScript.getChildScripts()) { + this._addScript(s); + } + this.sources.sourcesForScript(aScript); },