Bug 1583550 - Highlight toolbox paused state. r=jdescottes draft
authorJason Laster <jlaster@mozilla.com>
Thu, 05 Dec 2019 18:39:19 +0000
changeset 2519019 c3e353afc2d337a4d3b01369c3c4b68d487efaab
parent 2510977 e33d46f958b127135ef628f7b93e62b95e97f69b
child 2519020 32ce4a04c1e3d685975331dda6c2a6911de18fd8
push id461001
push userreviewbot
push dateThu, 05 Dec 2019 18:39:46 +0000
treeherdertry@32ce4a04c1e3 [default view] [failures only]
reviewersjdescottes
bugs1583550
milestone73.0a1
Bug 1583550 - Highlight toolbox paused state. r=jdescottes Differential Revision: https://phabricator.services.mozilla.com/D55848 Differential Diff: PHID-DIFF-pji4vj2kybr3q2a4xcek
devtools/client/framework/browser-toolbox/window.js
devtools/client/framework/toolbox.js
--- a/devtools/client/framework/browser-toolbox/window.js
+++ b/devtools/client/framework/browser-toolbox/window.js
@@ -248,42 +248,21 @@ async function bindToolboxHandlers() {
   gToolbox.once("destroyed", quitApp);
   window.addEventListener("unload", onUnload);
 
   if (Services.appinfo.OS == "Darwin") {
     // Badge the dock icon to differentiate this process from the main application
     // process.
     updateBadgeText(false);
 
-    // Once the debugger panel opens listen for thread pause / resume.
-    const panel = await gToolbox.getPanelWhenReady("jsdebugger");
-    setupThreadListeners(panel);
+    gToolbox.on("paused", () => updateBadgeText(true));
+    gToolbox.on("resumed", () => updateBadgeText(false));
   }
 }
 
-function setupThreadListeners(panel) {
-  updateBadgeText(panel.isPaused());
-
-  const onPaused = packet => {
-    if (packet.why.type === "interrupted") {
-      return;
-    }
-    updateBadgeText(true);
-  };
-  const onResumed = updateBadgeText.bind(null, false);
-  const threadFront = gToolbox.target.threadFront;
-  threadFront.on("paused", onPaused);
-  threadFront.on("resumed", onResumed);
-
-  panel.once("destroyed", () => {
-    threadFront.off("paused", onPaused);
-    threadFront.off("resumed", onResumed);
-  });
-}
-
 function updateBadgeText(paused) {
   const dockSupport = Cc["@mozilla.org/widget/macdocksupport;1"].getService(
     Ci.nsIMacDockSupport
   );
   dockSupport.badgeText = paused ? "▐▐ " : " ▶";
 }
 
 function onUnload() {
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -231,16 +231,17 @@ function Toolbox(
   this._toolStartups = new Map();
   this._inspectorExtensionSidebars = new Map();
 
   this._netMonitorAPI = null;
 
   // Map of frames (id => frame-info) and currently selected frame id.
   this.frameMap = new Map();
   this.selectedFrameId = null;
+  this._pausedThreads = new Set();
 
   /**
    * KeyShortcuts instance specific to WINDOW host type.
    * This is the key shortcuts that are only register when the toolbox
    * is loaded in its own window. Otherwise, these shortcuts are typically
    * registered by devtools-startup.js module.
    */
   this._windowHostShortcuts = null;
@@ -584,39 +585,50 @@ Toolbox.prototype = {
     const focusedWin = Services.focus.focusedWindow;
     return (
       focusedWin &&
       focusedWin ===
         this.doc.querySelector("#toolbox-panel-iframe-webconsole").contentWindow
     );
   },
 
-  _onPausedState: function(packet) {
+  _onPausedState: function(packet, threadFront) {
     // Suppress interrupted events by default because the thread is
     // paused/resumed a lot for various actions.
     if (packet.why.type === "interrupted") {
       return;
     }
 
+    this._pausedThreads.add(threadFront);
     this.highlightTool("jsdebugger");
 
     if (
       packet.why.type === "debuggerStatement" ||
       packet.why.type === "mutationBreakpoint" ||
       packet.why.type === "eventBreakpoint" ||
       packet.why.type === "breakpoint" ||
       packet.why.type === "exception"
     ) {
       this.raise();
       this.selectTool("jsdebugger", packet.why.type);
+      this.emit("paused");
     }
   },
 
-  _onResumedState: function() {
-    this.unhighlightTool("jsdebugger");
+  _onResumedState: function(threadFront) {
+    this._pausedThreads.delete(threadFront);
+
+    if (!this.isPaused()) {
+      this.emit("resumed");
+      this.unhighlightTool("jsdebugger");
+    }
+  },
+
+  isPaused() {
+    return this._pausedThreads.size > 0;
   },
 
   /**
    * This method will be called for the top-level target, as well as any potential
    * additional targets we may care about.
    */
   async _onTargetAvailable(type, targetFront, isTopLevel) {
     if (isTopLevel) {
@@ -663,29 +675,31 @@ Toolbox.prototype = {
     }
 
     const threadFront = await this._attachAndResumeThread(target);
     this._startThreadFrontListeners(threadFront);
     return threadFront;
   },
 
   _startThreadFrontListeners: function(threadFront) {
-    threadFront.on("paused", this._onPausedState);
-    threadFront.on("resumed", this._onResumedState);
+    threadFront.on("paused", packet =>
+      this._onPausedState(packet, threadFront)
+    );
+    threadFront.on("resumed", () => this._onResumedState(threadFront));
   },
 
   _stopThreadFrontListeners: function(threadFront) {
     // Only cleanup thread listeners if it has been attached.
     // We may have closed the toolbox before it attached, or
     // we may not have attached to the target at all.
     if (!threadFront) {
       return;
     }
-    threadFront.off("paused", this._onPausedState);
-    threadFront.off("resumed", this._onResumedState);
+    threadFront.off("paused");
+    threadFront.off("resumed");
   },
 
   _attachAndResumeThread: async function(target) {
     const options = defaultThreadOptions();
     const [, threadFront] = await target.attachThread(options);
 
     try {
       await threadFront.resume();
@@ -3578,16 +3592,17 @@ Toolbox.prototype = {
       this._applyServiceWorkersTestingSettings
     );
 
     // We normally handle toolClosed from selectTool() but in the event of the
     // toolbox closing we need to handle it here instead.
     this.telemetry.toolClosed(this.currentToolId, this.sessionId, this);
 
     this._lastFocusedElement = null;
+    this._pausedThreads = null;
 
     if (this._sourceMapURLService) {
       this._sourceMapURLService.destroy();
       this._sourceMapURLService = null;
     }
 
     if (this._sourceMapService) {
       this._sourceMapService.stopSourceMapWorker();