Bug 1354679 - Show the paused debugger overlay when script gets paused; r=ochameau r=pbro draft
authoryulia <ystartsev@mozilla.com>
Thu, 19 Oct 2017 16:03:17 +0200
changeset 684715 8d79d2611fbd6e7c71bb1e1a13ace04b6c1084d3
parent 682187 c0349f5d4cf33396382ce7bd2b84dd27249a3741
child 699816 f82dd33be70956e7a1183cd71232f5d2735540be
child 699818 74ba27d85dce13d6df02d4bc8f291f9997dd6f32
push id85701
push userbmo:ystartsev@mozilla.com
push dateMon, 23 Oct 2017 13:33:38 +0000
reviewersochameau, pbro
bugs1354679
milestone58.0a1
Bug 1354679 - Show the paused debugger overlay when script gets paused; r=ochameau r=pbro MozReview-Commit-ID: JZkXtGJ2YIx
devtools/client/framework/attach-thread.js
devtools/server/actors/script.js
devtools/server/tests/unit/test_pausedOverlay.js
devtools/shared/client/thread-client.js
--- a/devtools/client/framework/attach-thread.js
+++ b/devtools/client/framework/attach-thread.js
@@ -24,16 +24,17 @@ function handleThreadState(toolbox, even
   if (event === "paused") {
     toolbox.highlightTool("jsdebugger");
 
     if (packet.why.type === "debuggerStatement" ||
        packet.why.type === "breakpoint" ||
        packet.why.type === "exception") {
       toolbox.raise();
       toolbox.selectTool("jsdebugger");
+      toolbox._threadClient.clientPaused();
     }
   } else if (event === "resumed") {
     toolbox.unhighlightTool("jsdebugger");
   }
 }
 
 function attachThread(toolbox) {
   let deferred = defer();
--- a/devtools/server/actors/script.js
+++ b/devtools/server/actors/script.js
@@ -1223,16 +1223,24 @@ const ThreadActor = ActorClassWithSpec(t
   _getNextStepFrame: function (frame) {
     let stepFrame = frame.reportedPop ? frame.older : frame;
     if (!stepFrame || !stepFrame.script) {
       stepFrame = null;
     }
     return stepFrame;
   },
 
+  /**
+   * Client informs the server that we have a user significant pause.
+   */
+  onClientPaused: function () {
+    this.emit("clientPaused");
+    return { type: "clientPaused" };
+  },
+
   onClientEvaluate: function (request) {
     if (this.state !== "paused") {
       return { error: "wrongState",
                message: "Debuggee must be paused to evaluate code." };
     }
 
     let frame = this._requestFrame(request.frame);
     if (!frame) {
@@ -1240,16 +1248,17 @@ const ThreadActor = ActorClassWithSpec(t
                message: "Evaluation frame not found" };
     }
 
     if (!frame.environment) {
       return { error: "notDebuggee",
                message: "cannot access the environment of this frame." };
     }
 
+    // TODO: this is not being used, paused takes no arguements
     let youngest = this.youngestFrame;
 
     // Put ourselves back in the running state and inform the client.
     let resumedPacket = this._resumed();
     this.conn.send(resumedPacket);
 
     // Run the expression.
     // XXX: test syntax errors
@@ -1537,17 +1546,16 @@ const ThreadActor = ActorClassWithSpec(t
     // We don't want to actually have nested pauses (although we
     // have nested event loops).  If code runs in the debuggee during
     // a pause, it should cause the actor to resume (dropping
     // pause-lifetime actors etc) and then repause when complete.
 
     if (this.state === "paused") {
       return undefined;
     }
-    this.emit("pausing", this.state);
 
     // Clear stepping hooks.
     this.dbg.onEnterFrame = undefined;
     this.dbg.onExceptionUnwind = undefined;
     if (frame) {
       frame.onStep = undefined;
       frame.onPop = undefined;
     }
@@ -1596,24 +1604,24 @@ const ThreadActor = ActorClassWithSpec(t
     if (poppedFrames) {
       packet.poppedFrames = poppedFrames;
     }
 
     return packet;
   },
 
   _resumed: function () {
+    this.emit('resumed');
     this._state = "running";
 
     // Drop the actors in the pause actor pool.
     this.conn.removeActorPool(this._pausePool);
 
     this._pausePool = null;
     this._pauseActor = null;
-    this.emit("resumed");
 
     return { from: this.actorID, type: "resumed" };
   },
 
   /**
    * Expire frame actors for frames that have been popped.
    *
    * @returns A list of actor IDs whose frames have been popped.
@@ -2059,16 +2067,17 @@ const ThreadActor = ActorClassWithSpec(t
 });
 
 Object.assign(ThreadActor.prototype.requestTypes, {
   "attach": ThreadActor.prototype.onAttach,
   "detach": ThreadActor.prototype.onDetach,
   "reconfigure": ThreadActor.prototype.onReconfigure,
   "resume": ThreadActor.prototype.onResume,
   "clientEvaluate": ThreadActor.prototype.onClientEvaluate,
+  "clientPaused": ThreadActor.prototype.onClientPaused,
   "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
 });
@@ -2388,22 +2397,17 @@ exports.unwrapDebuggerObjectGlobal = wra
   }
 };
 
 // Manage a collection of PausedDebuggerOverlays and their HighlighterEnvironments indexed
 // by TabActors (export it so it the paused state can be tested).
 const pausedStateOverlays = exports.pausedStateOverlays = new Map();
 
 function attachPauseStateListeners(threadActor) {
-  EventEmitter.on(threadActor, "pausing", (pausingState) => {
-    if (pausingState !== "attached") {
-      console.log(pausingState);
-      return showPausedStateOverlay(threadActor._parent);
-    }
-  });
+  EventEmitter.on(threadActor, "clientPaused", () => showPausedStateOverlay(threadActor._parent));
   EventEmitter.on(threadActor, "resumed",() => hidePausedStateOverlay(threadActor._parent));
   EventEmitter.on(threadActor, "exiting",() => destroyPausedStateOverlay(threadActor._parent));
 }
 
 /**
  * Get the instance of the PausedDebuggerOverlay for a TabActor.
  *
  * @param {TabActor} tabActor
--- a/devtools/server/tests/unit/test_pausedOverlay.js
+++ b/devtools/server/tests/unit/test_pausedOverlay.js
@@ -43,75 +43,32 @@ add_task(async function() {
     gThreadClient = threadClient;
     gTabClient = tabClient;
 
     // Inject a mock overlay here. We need the TabActor reference in order to inject the
     // mock into the pausedStateOverlays map (go over the transport boundary to do this).
     pausedStateOverlays.set(
       gClient._transport._serverConnection.getActor(gTabClient._actor), mockOverlay);
 
-    yield test_debugger_statement();
-    yield test_exception();
-    yield test_breakpoint();
+    yield test_client_paused();
 
     yield gClient.close();
     do_test_finished();
   });
 });
 
-function* test_debugger_statement() {
+function* test_client_paused() {
   ok(!isOverlayVisible, "The overlay is hidden at first");
 
+  // this is triggering a pause, this may be triggered in any way
+  // such as breakpoint, exception, etc
   yield executeOnNextTickAndWaitForPause(() => {
     Cu.evalInSandbox("debugger;", gDebuggee);
   }, gClient);
+  // we only show the paused overlay if the client determines this is an
+  // important pause for the user
+  yield gThreadClient.clientPaused();
 
-  ok(isOverlayVisible, "The overlay is visible on debugger statements");
+  ok(isOverlayVisible, "The overlay is visible on client paused");
 
   yield gThreadClient.resume();
   ok(!isOverlayVisible, "The overlay is hidden on resume");
 }
-
-function* test_exception() {
-  yield gThreadClient.pauseOnExceptions(true, false);
-
-  ok(!isOverlayVisible, "The overlay is hidden at first");
-
-  yield executeOnNextTickAndWaitForPause(() => {
-    Cu.evalInSandbox("try { throw new Error('test error'); } catch (e) {}", gDebuggee);
-  }, gClient);
-
-  ok(isOverlayVisible,
-    "The overlay is visible when break on exceptions is set and there is an error");
-
-  yield gThreadClient.resume();
-  ok(!isOverlayVisible, "The overlay is hidden on resume");
-
-  yield gThreadClient.pauseOnExceptions(false);
-}
-
-function* test_breakpoint() {
-  ok(!isOverlayVisible, "The overlay is hidden at first");
-
-  executeSoon(() => {
-    Cu.evalInSandbox(
-      `var line0 = new Error().lineNumber;
-      function breakInHere() {
-        return 2;
-      }`,
-      gDebuggee
-    );
-  });
-
-  let { sources } = yield gThreadClient.getSources();
-  let source = gThreadClient.source(sources[sources.length - 1]);
-
-  yield setBreakpoint(source, { line: gDebuggee.line0 + 2 });
-
-  yield executeOnNextTickAndWaitForPause(() => {
-    gDebuggee.breakInHere();
-  }, gClient);
-
-  ok(isOverlayVisible, "The overlay is visible when a breakpoint is found");
-
-  yield gThreadClient.resume();
-  ok(!isOverlayVisible, "The overlay is hidden on resume");
-}
--- a/devtools/shared/client/thread-client.js
+++ b/devtools/shared/client/thread-client.js
@@ -270,16 +270,24 @@ ThreadClient.prototype = {
         onResponse(response);
         return response;
       }
       return this.resume(onResponse);
     });
   },
 
   /**
+   * Notify the server that the client needs a pause overlay to be shown.
+   *
+   */
+  clientPaused: DebuggerClient.requester({
+    type: "clientPaused"
+  }),
+
+  /**
    * Send a clientEvaluate packet to the debuggee. Response
    * will be a resume packet.
    *
    * @param string frame
    *        The actor ID of the frame where the evaluation should take place.
    * @param string expression
    *        The expression that will be evaluated in the scope of the frame
    *        above.