diff --git a/browser/devtools/debugger/debugger-view.js b/browser/devtools/debugger/debugger-view.js
index c3d6abf8b74d..1e9790392021 100644
--- a/browser/devtools/debugger/debugger-view.js
+++ b/browser/devtools/debugger/debugger-view.js
@@ -1092,6 +1092,17 @@ DebuggerView.Scripts = {
return script;
},
+ /**
+ * Returns the list of URIs for scripts in the page.
+ */
+ scriptLocations: function DVS_scriptLocations() {
+ let locations = [];
+ for (let i = 0; i < this._scripts.itemCount; i++) {
+ locations.push(this._scripts.getItemAtIndex(i).value);
+ }
+ return locations;
+ },
+
/**
* The cached click listener for the scripts container.
*/
diff --git a/browser/devtools/webconsole/GcliCommands.jsm b/browser/devtools/webconsole/GcliCommands.jsm
index 68ae84e8c9f9..31532b9e0ae5 100644
--- a/browser/devtools/webconsole/GcliCommands.jsm
+++ b/browser/devtools/webconsole/GcliCommands.jsm
@@ -125,3 +125,139 @@ gcli.addCommand({
document.defaultView.InspectorUI.openInspectorUI(args.node);
}
});
+
+let breakpoints = [];
+
+/**
+ * 'break' command
+ */
+gcli.addCommand({
+ name: "break",
+ description: gcli.lookup("breakDesc"),
+ manual: gcli.lookup("breakManual")
+});
+
+
+/**
+ * 'break list' command
+ */
+gcli.addCommand({
+ name: "break list",
+ description: gcli.lookup("breaklistDesc"),
+ returnType: "html",
+ exec: function(args, context) {
+ if (breakpoints.length === 0) {
+ return gcli.lookup("breaklistNone");
+ }
+
+ let reply = gcli.lookup("breaklistIntro");
+ reply += "
";
+ breakpoints.forEach(function(breakpoint) {
+ let text = gcli.lookupFormat("breaklistLineEntry",
+ [breakpoint.file, breakpoint.line]);
+ reply += "- " + text + "
";
+ });
+ reply += "
";
+ return reply;
+ }
+});
+
+
+/**
+ * 'break add' command
+ */
+gcli.addCommand({
+ name: "break add",
+ description: gcli.lookup("breakaddDesc"),
+ manual: gcli.lookup("breakaddManual")
+});
+
+/**
+ * 'break add line' command
+ */
+gcli.addCommand({
+ name: "break add line",
+ description: gcli.lookup("breakaddlineDesc"),
+ params: [
+ {
+ name: "file",
+ type: {
+ name: "selection",
+ data: function() {
+ let win = HUDService.currentContext();
+ let dbg = win.DebuggerUI.getDebugger(win.gBrowser.selectedTab);
+ let files = [];
+ if (dbg) {
+ let scriptsView = dbg.frame.contentWindow.DebuggerView.Scripts;
+ for each (let script in scriptsView.scriptLocations()) {
+ files.push(script);
+ }
+ }
+ return files;
+ }
+ },
+ description: gcli.lookup("breakaddlineFileDesc")
+ },
+ {
+ name: "line",
+ type: { name: "number", min: 1, step: 10 },
+ description: gcli.lookup("breakaddlineLineDesc")
+ }
+ ],
+ returnType: "html",
+ exec: function(args, context) {
+ args.type = "line";
+ let win = HUDService.currentContext();
+ let dbg = win.DebuggerUI.getDebugger(win.gBrowser.selectedTab);
+ if (!dbg) {
+ return gcli.lookup("breakaddDebuggerStopped");
+ }
+ var promise = context.createPromise();
+ let position = { url: args.file, line: args.line };
+ dbg.activeThread.setBreakpoint(position, function(aResponse, aBpClient) {
+ if (aResponse.error) {
+ promise.resolve(gcli.lookupFormat("breakaddFailed",
+ [ aResponse.error ]));
+ return;
+ }
+ args.client = aBpClient;
+ breakpoints.push(args);
+ promise.resolve(gcli.lookup("breakaddAdded"));
+ });
+ return promise;
+ }
+});
+
+
+/**
+ * 'break del' command
+ */
+gcli.addCommand({
+ name: "break del",
+ description: gcli.lookup("breakdelDesc"),
+ params: [
+ {
+ name: "breakid",
+ type: {
+ name: "number",
+ min: 0,
+ max: function() { return breakpoints.length - 1; }
+ },
+ description: gcli.lookup("breakdelBreakidDesc")
+ }
+ ],
+ returnType: "html",
+ exec: function(args, context) {
+ let breakpoint = breakpoints.splice(args.breakid, 1)[0];
+ var promise = context.createPromise();
+ try {
+ breakpoint.client.remove(function(aResponse) {
+ promise.resolve(gcli.lookup("breakdelRemoved"));
+ });
+ } catch (ex) {
+ // If the debugger has been closed already, don't scare the user.
+ promise.resolve(gcli.lookup("breakdelRemoved"));
+ }
+ return promise;
+ }
+});
diff --git a/browser/devtools/webconsole/test/Makefile.in b/browser/devtools/webconsole/test/Makefile.in
index 39dfc61d0875..6c0b1d0c12d8 100644
--- a/browser/devtools/webconsole/test/Makefile.in
+++ b/browser/devtools/webconsole/test/Makefile.in
@@ -154,6 +154,7 @@ _BROWSER_TEST_FILES = \
browser_webconsole_bug_622303_persistent_filters.js \
browser_webconsole_window_zombie.js \
browser_cached_messages.js \
+ browser_gcli_break.js \
head.js \
$(NULL)
@@ -228,6 +229,7 @@ _BROWSER_TEST_PAGES = \
test-bug-658368-time-methods.html \
test-webconsole-error-observer.html \
test-for-of.html \
+ browser_gcli_break.html \
$(NULL)
libs:: $(_BROWSER_TEST_FILES)
diff --git a/browser/devtools/webconsole/test/browser_gcli_break.html b/browser/devtools/webconsole/test/browser_gcli_break.html
new file mode 100644
index 000000000000..fabbf1e3ec08
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_gcli_break.html
@@ -0,0 +1,18 @@
+
+
+
+ Browser GCLI break command test
+
+
+
+
+
+
diff --git a/browser/devtools/webconsole/test/browser_gcli_break.js b/browser/devtools/webconsole/test/browser_gcli_break.js
new file mode 100644
index 000000000000..c1192a2674ff
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_gcli_break.js
@@ -0,0 +1,104 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// For more information on GCLI see:
+// - https://github.com/mozilla/gcli/blob/master/docs/index.md
+// - https://wiki.mozilla.org/DevTools/Features/GCLI
+
+// Tests that the break command works as it should
+
+let tempScope = {};
+Components.utils.import("resource:///modules/gcli.jsm", tempScope);
+let gcli = tempScope.gcli;
+
+const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/browser_gcli_break.html";
+registerCleanupFunction(function() {
+ gcliterm = undefined;
+ requisition = undefined;
+
+ Services.prefs.clearUserPref("devtools.gcli.enable");
+});
+
+function test() {
+ Services.prefs.setBoolPref("devtools.gcli.enable", true);
+ addTab(TEST_URI);
+ browser.addEventListener("DOMContentLoaded", onLoad, false);
+}
+
+let gcliterm;
+let requisition;
+
+function onLoad() {
+ browser.removeEventListener("DOMContentLoaded", onLoad, false);
+
+ try {
+ openConsole();
+
+ let hud = HUDService.getHudByWindow(content);
+ gcliterm = hud.gcliterm;
+ requisition = gcliterm.opts.requisition;
+
+ testSetup();
+ testCreateCommands();
+ }
+ catch (ex) {
+ ok(false, "Caught exception: " + ex)
+ gcli._internal.console.error("Test Failure", ex);
+ closeConsole();
+ finishTest();
+ }
+}
+
+function testSetup() {
+ ok(gcliterm, "We have a GCLI term");
+ ok(requisition, "We have a Requisition");
+}
+
+function testCreateCommands() {
+ type("brea");
+ is(gcliterm.completeNode.textContent, " break", "Completion for 'brea'");
+ is(requisition.getStatus().toString(), "ERROR", "brea is ERROR");
+
+ type("break");
+ is(requisition.getStatus().toString(), "ERROR", "break is ERROR");
+
+ type("break add");
+ is(requisition.getStatus().toString(), "ERROR", "break add is ERROR");
+
+ type("break add line");
+ is(requisition.getStatus().toString(), "ERROR", "break add line is ERROR");
+
+ let pane = DebuggerUI.toggleDebugger();
+ pane.onConnected = function test_onConnected(aPane) {
+ // Wait for the initial resume.
+ aPane.debuggerWindow.gClient.addOneTimeListener("resumed", function() {
+ delete aPane.onConnected;
+ aPane.debuggerWindow.gClient.activeThread.addOneTimeListener("scriptsadded", function() {
+ type("break add line " + TEST_URI + " " + content.wrappedJSObject.line0);
+ is(requisition.getStatus().toString(), "VALID", "break add line is VALID");
+ requisition.exec();
+
+ type("break list");
+ is(requisition.getStatus().toString(), "VALID", "break list is VALID");
+ requisition.exec();
+
+ aPane.debuggerWindow.gClient.activeThread.resume(function() {
+ type("break del 0");
+ is(requisition.getStatus().toString(), "VALID", "break del 0 is VALID");
+ requisition.exec();
+
+ closeConsole();
+ finishTest();
+ });
+ });
+ // Trigger newScript notifications using eval.
+ content.wrappedJSObject.firstCall();
+ });
+ }
+}
+
+function type(command) {
+ gcliterm.inputNode.value = command.slice(0, -1);
+ gcliterm.inputNode.focus();
+ EventUtils.synthesizeKey(command.slice(-1), {});
+}
diff --git a/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties b/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
index ce59a4d8bdd4..bfa6a92669bf 100644
--- a/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
@@ -56,6 +56,75 @@ inspectNodeDesc=CSS selector
# on what it does.
inspectNodeManual=A CSS selector for use with Document.querySelector which identifies a single element
+# LOCALIZATION NOTE (breakDesc) A very short string used to describe the
+# function of the break command.
+breakDesc=Manage breakpoints
+
+# LOCALIZATION NOTE (breakManual) A longer description describing the
+# set of commands that control breakpoints.
+breakManual=Commands to list, add and remove breakpoints
+
+# LOCALIZATION NOTE (breaklistDesc) A very short string used to describe the
+# function of the 'break list' command.
+breaklistDesc=Display known breakpoints
+
+# LOCALIZATION NOTE (breaklistLineEntry) Used in the output of the 'break list'
+# command to display a single line breakpoint.
+# %1$S=script URL, %2$S=line number
+breaklistLineEntry=Line breakpoint at %1$S:%2$S
+
+# LOCALIZATION NOTE (breaklistNone) Used in the output of the 'break list'
+# command to explain that the list is empty.
+breaklistNone=No breakpoints set
+
+# LOCALIZATION NOTE (breaklistIntro) Used in the output of the 'break list'
+# command to preface the list contents.
+breaklistIntro=The following breakpoints are set:
+
+# LOCALIZATION NOTE (breakaddAdded) Used in the output of the 'break add'
+# command to explain that a breakpoint was added.
+breakaddAdded=Added breakpoint
+
+# LOCALIZATION NOTE (breakaddFailed) Used in the output of the 'break add'
+# command to explain that a breakpoint could not be added.
+breakaddFailed=Could not set breakpoint: %S
+
+# LOCALIZATION NOTE (breakaddDesc) A very short string used to describe the
+# function of the 'break add' command.
+breakaddDesc=Add a breakpoint
+
+# LOCALIZATION NOTE (breakaddManual) A longer description describing the
+# set of commands that are responsible for adding breakpoints.
+breakaddManual=Breakpoint types supported: line
+
+# LOCALIZATION NOTE (breakaddDebuggerStopped) Used in the output of the
+# 'break add' command to explain that the debugger must be opened first.
+breakaddDebuggerStopped=The debugger must be opened before setting breakpoints
+
+# LOCALIZATION NOTE (breakaddlineDesc) A very short string used to describe the
+# function of the 'break add line' command.
+breakaddlineDesc=Add a line breakpoint
+
+# LOCALIZATION NOTE (breakaddlineFileDesc) A very short string used to describe
+# the function of the file parameter in the 'break add line' command.
+breakaddlineFileDesc=JS file URI
+
+# LOCALIZATION NOTE (breakaddlineLineDesc) A very short string used to describe
+# the function of the line parameter in the 'break add line' command.
+breakaddlineLineDesc=Line number
+
+# LOCALIZATION NOTE (breakdelDesc) A very short string used to describe the
+# function of the 'break del' command.
+breakdelDesc=Remove a breakpoint
+
+# LOCALIZATION NOTE (breakdelBreakidDesc) A very short string used to describe
+# the function of the index parameter in the 'break del' command.
+breakdelBreakidDesc=Index of breakpoint
+
+# LOCALIZATION NOTE (breakdelRemoved) Used in the output of the 'break del'
+# command to explain that a breakpoint was removed.
+breakdelRemoved=Breakpoint removed
+
# LOCALIZATION NOTE (consolecloseDesc) A very short description of the
# 'console close' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.