Bug 1455750 - Provide method for server to skip pausing. r=jimb
--- a/devtools/server/actors/breakpoint.js
+++ b/devtools/server/actors/breakpoint.js
@@ -144,16 +144,17 @@ let BreakpointActor = ActorClassWithSpec
originalSourceActor,
originalLine,
originalColumn
} = this.threadActor.unsafeSynchronize(
this.threadActor.sources.getOriginalLocation(generatedLocation));
let url = originalSourceActor.url;
if (this.threadActor.sources.isBlackBoxed(url)
+ || this.threadActor.skipBreakpoints
|| frame.onStep) {
return undefined;
}
// If we're trying to pop this frame, and we see a breakpoint at
// the spot at which popping started, ignore it. See bug 970469.
const locationAtFinish = frame.onPop && frame.onPop.originalLocation;
if (locationAtFinish &&
--- a/devtools/server/actors/thread.js
+++ b/devtools/server/actors/thread.js
@@ -1506,19 +1506,26 @@ const ThreadActor = ActorClassWithSpec(t
onDebuggerStatement: function(frame) {
// Don't pause if we are currently stepping (in or over) or the frame is
// black-boxed.
const generatedLocation = this.sources.getFrameLocation(frame);
const { originalSourceActor } = this.unsafeSynchronize(
this.sources.getOriginalLocation(generatedLocation));
const url = originalSourceActor ? originalSourceActor.url : null;
- return this.sources.isBlackBoxed(url) || frame.onStep
- ? undefined
- : this._pauseAndRespond(frame, { type: "debuggerStatement" });
+ if (this.skipBreakpoints || this.sources.isBlackBoxed(url) || frame.onStep) {
+ return undefined;
+ }
+
+ return this._pauseAndRespond(frame, { type: "debuggerStatement" });
+ },
+
+ onSkipBreakpoints: function({ skip }) {
+ this.skipBreakpoints = skip;
+ return { skip };
},
/**
* A function that the engine calls when an exception has been thrown and has
* propagated to the specified frame.
*
* @param youngestFrame Debugger.Frame
* The youngest remaining stack frame.
@@ -1547,17 +1554,17 @@ const ThreadActor = ActorClassWithSpec(t
const generatedLocation = this.sources.getFrameLocation(youngestFrame);
const { originalSourceActor } = this.unsafeSynchronize(
this.sources.getOriginalLocation(generatedLocation));
const url = originalSourceActor ? originalSourceActor.url : null;
// We ignore sources without a url because we do not
// want to pause at console evaluations or watch expressions.
- if (!url || this.sources.isBlackBoxed(url)) {
+ if (!url || this.skipBreakpoints || this.sources.isBlackBoxed(url)) {
return undefined;
}
try {
let packet = this._paused(youngestFrame);
if (!packet) {
return undefined;
}
@@ -1750,17 +1757,18 @@ Object.assign(ThreadActor.prototype.requ
"resume": ThreadActor.prototype.onResume,
"clientEvaluate": ThreadActor.prototype.onClientEvaluate,
"frames": ThreadActor.prototype.onFrames,
"interrupt": ThreadActor.prototype.onInterrupt,
"eventListeners": ThreadActor.prototype.onEventListeners,
"releaseMany": ThreadActor.prototype.onReleaseMany,
"sources": ThreadActor.prototype.onSources,
"threadGrips": ThreadActor.prototype.onThreadGrips,
- "prototypesAndProperties": ThreadActor.prototype.onPrototypesAndProperties
+ "prototypesAndProperties": ThreadActor.prototype.onPrototypesAndProperties,
+ "skipBreakpoints": ThreadActor.prototype.onSkipBreakpoints
});
exports.ThreadActor = ThreadActor;
/**
* Creates a PauseActor.
*
* PauseActors exist for the lifetime of a given debuggee pause. Used to
--- a/devtools/server/tests/unit/head_dbg.js
+++ b/devtools/server/tests/unit/head_dbg.js
@@ -214,16 +214,26 @@ function findSource(sources, url) {
return null;
}
function waitForPause(threadClient) {
dump("Waiting for pause.\n");
return waitForEvent(threadClient, "paused");
}
+function waitForProperty(dbg, property) {
+ return new Promise(resolve => {
+ Object.defineProperty(dbg, property, {
+ set(newValue) {
+ resolve(newValue);
+ }
+ });
+ });
+}
+
function setBreakpoint(sourceClient, location) {
dump("Setting breakpoint.\n");
return sourceClient.setBreakpoint(location);
}
function getPrototypeAndProperties(objClient) {
dump("getting prototype and properties.\n");
new file mode 100644
--- /dev/null
+++ b/devtools/server/tests/unit/test_stepping-with-skip-breakpoints.js
@@ -0,0 +1,100 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* eslint-disable no-shadow, max-nested-callbacks */
+
+"use strict";
+
+/**
+ * Check basic step-over functionality with pause points
+ * for the first statement and end of the last statement.
+ */
+
+var gDebuggee;
+var gClient;
+var gCallback;
+
+function run_test() {
+ do_test_pending();
+ run_test_with_server(DebuggerServer, function() {
+ run_test_with_server(WorkerDebuggerServer, do_test_finished);
+ });
+}
+
+function run_test_with_server(server, callback) {
+ gCallback = callback;
+ initTestDebuggerServer(server);
+ gDebuggee = addTestGlobal("test-stepping", server);
+ gClient = new DebuggerClient(server.connectPipe());
+ gClient.connect(test_simple_stepping);
+}
+
+async function test_simple_stepping() {
+ const [attachResponse,, threadClient] = await attachTestTabAndResume(gClient,
+ "test-stepping");
+ ok(!attachResponse.error, "Should not get an error attaching");
+
+ dumpn("Evaluating test code and waiting for first debugger statement");
+ const dbgStmt = await executeOnNextTickAndWaitForPause(evaluateTestCode, gClient);
+ equal(dbgStmt.frame.where.line, 2, "Should be at debugger statement on line 2");
+ equal(gDebuggee.a, undefined);
+ equal(gDebuggee.b, undefined);
+
+ const source = await getSource(threadClient, "test_stepping-01-test-code.js");
+
+ // Add pause points for the first and end of the last statement.
+ // Note: we intentionally ignore the second statement.
+ source.setPausePoints([{
+ location: {line: 3, column: 8},
+ types: {breakpoint: true, stepOver: true}
+ },
+ {
+ location: {line: 4, column: 14},
+ types: {breakpoint: true, stepOver: true}
+ }]);
+
+ dumpn("Step Over to line 3");
+ const step1 = await stepOver(gClient, threadClient);
+ equal(step1.type, "paused");
+ equal(step1.why.type, "resumeLimit");
+ equal(step1.frame.where.line, 3);
+ equal(step1.frame.where.column, 8);
+
+ equal(gDebuggee.a, undefined);
+ equal(gDebuggee.b, undefined);
+
+ dumpn("Step Over to line 4");
+ const step2 = await stepOver(gClient, threadClient);
+ equal(step2.type, "paused");
+ equal(step2.why.type, "resumeLimit");
+ equal(step2.frame.where.line, 4);
+ equal(step2.frame.where.column, 8);
+
+ equal(gDebuggee.a, 1);
+ equal(gDebuggee.b, undefined);
+
+ dumpn("Step Over to the end of line 4");
+ const step3 = await stepOver(gClient, threadClient);
+ equal(step3.type, "paused");
+ equal(step3.why.type, "resumeLimit");
+ equal(step3.frame.where.line, 4);
+ equal(step3.frame.where.column, 14);
+ equal(gDebuggee.a, 1);
+ equal(gDebuggee.b, 2);
+
+ finishClient(gClient, gCallback);
+}
+
+function evaluateTestCode() {
+ /* eslint-disable */
+ Cu.evalInSandbox(
+ ` // 1
+ debugger; // 2
+ var a = 1; // 3
+ var b = 2;`, // 4
+ gDebuggee,
+ "1.8",
+ "test_stepping-01-test-code.js",
+ 1
+ );
+ /* eslint-disable */
+}
--- a/devtools/server/tests/unit/xpcshell.ini
+++ b/devtools/server/tests/unit/xpcshell.ini
@@ -184,16 +184,17 @@ reason = bug 1104838
[test_stepping-02.js]
[test_stepping-03.js]
[test_stepping-04.js]
[test_stepping-05.js]
[test_stepping-06.js]
[test_stepping-07.js]
[test_stepping-08.js]
[test_stepping-with-pause-points.js]
+[test_stepping-with-skip-breakpoints.js]
[test_framebindings-01.js]
[test_framebindings-02.js]
[test_framebindings-03.js]
[test_framebindings-04.js]
[test_framebindings-05.js]
[test_framebindings-06.js]
[test_framebindings-07.js]
[test_pause_exceptions-01.js]
--- a/devtools/shared/client/thread-client.js
+++ b/devtools/shared/client/thread-client.js
@@ -391,16 +391,27 @@ ThreadClient.prototype = {
*/
getFrames: DebuggerClient.requester({
type: "frames",
start: arg(0),
count: arg(1)
}),
/**
+ * Toggle pausing via breakpoints in the server.
+ *
+ * @param skip boolean
+ * Whether the server should skip pausing via breakpoints
+ */
+ skipBreakpoints: DebuggerClient.requester({
+ type: "skipBreakpoints",
+ skip: arg(0),
+ }),
+
+ /**
* An array of cached frames. Clients can observe the framesadded and
* framescleared event to keep up to date on changes to this cache,
* and can fill it using the fillFrames method.
*/
get cachedFrames() {
return this._frameCache;
},