Bug 792867 - Debugger mochitests leak when run separately. r=rcampbell
authorPanos Astithas <past@mozilla.com>
Sat, 22 Sep 2012 18:44:00 +0300
changeset 107953 4e6eb2cb00b5246bd71af68b56faf67667bd6dff
parent 107952 4900ae2923153aa1a3ddbe25f6353d56c52fa319
child 107954 87a7a1c60a3ad344ae9e1b8f0f70997e4ea91e1c
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
reviewersrcampbell
bugs792867
milestone18.0a1
Bug 792867 - Debugger mochitests leak when run separately. r=rcampbell
browser/devtools/debugger/DebuggerUI.jsm
toolkit/devtools/debugger/server/dbg-server.js
toolkit/devtools/debugger/tests/unit/test_dbgsocket.js
--- a/browser/devtools/debugger/DebuggerUI.jsm
+++ b/browser/devtools/debugger/DebuggerUI.jsm
@@ -197,18 +197,26 @@ DebuggerUI.prototype = {
         targetWindow.gBrowser.selectedTab = scriptDebugger.ownerTab;
         targetWindow.focus();
       }.bind(this)
     }, {
       id: "debugger.confirmTabSwitch.buttonOpen",
       label: L10N.getStr("confirmTabSwitch.buttonOpen"),
       accessKey: L10N.getStr("confirmTabSwitch.buttonOpen.accessKey"),
       callback: function DUI_notificationButtonOpen() {
-        this.findDebugger().close();
-        this.toggleDebugger();
+        let scriptDebugger = this.findDebugger();
+        let targetWindow = scriptDebugger.globalUI.chromeWindow;
+        scriptDebugger.close();
+        let self = this;
+        targetWindow.addEventListener("Debugger:Shutdown", function toggle() {
+          targetWindow.removeEventListener("Debugger:Shutdown", toggle, false);
+          Services.tm.currentThread.dispatch({ run: function() {
+            self.toggleDebugger();
+          }}, 0);
+        }, false);
       }.bind(this)
     }];
 
     let message = L10N.getStr("confirmTabSwitch.message");
     let imageURL = "chrome://browser/skin/Info.png";
 
     notification = nbox.appendNotification(
       message, TAB_SWITCH_NOTIFICATION,
--- a/toolkit/devtools/debugger/server/dbg-server.js
+++ b/toolkit/devtools/debugger/server/dbg-server.js
@@ -67,21 +67,29 @@ var DebuggerServer = {
   globalActorFactories: null,
   // Map of tab actor names to actor constructors provided by extensions.
   tabActorFactories: null,
 
   LONG_STRING_LENGTH: 10000,
   LONG_STRING_INITIAL_LENGTH: 1000,
 
   /**
-   * Prompt the user to accept or decline the incoming connection.
+   * A handler function that prompts the user to accept or decline the incoming
+   * connection.
+   */
+  _allowConnection: null,
+
+  /**
+   * Prompt the user to accept or decline the incoming connection. This is the
+   * default implementation that products embedding the debugger server may
+   * choose to override.
    *
    * @return true if the connection should be permitted, false otherwise
    */
-  _allowConnection: function DH__allowConnection() {
+  _defaultAllowConnection: function DH__defaultAllowConnection() {
     let title = L10N.getStr("remoteIncomingPromptTitle");
     let msg = L10N.getStr("remoteIncomingPromptMessage");
     let disableButton = L10N.getStr("remoteIncomingPromptDisable");
     let prompt = Services.prompt;
     let flags = prompt.BUTTON_POS_0 * prompt.BUTTON_TITLE_OK +
                 prompt.BUTTON_POS_1 * prompt.BUTTON_TITLE_CANCEL +
                 prompt.BUTTON_POS_2 * prompt.BUTTON_TITLE_IS_STRING +
                 prompt.BUTTON_POS_1_DEFAULT;
@@ -128,35 +136,38 @@ var DebuggerServer = {
   initTransport: function DH_initTransport(aAllowConnectionCallback) {
     if (this._transportInitialized) {
       return;
     }
 
     this._connections = {};
     this._nextConnID = 0;
     this._transportInitialized = true;
-    if (aAllowConnectionCallback) {
-      this._allowConnection = aAllowConnectionCallback;
-    }
+    this._allowConnection = aAllowConnectionCallback ?
+                            aAllowConnectionCallback :
+                            this._defaultAllowConnection;
   },
 
   get initialized() { return !!this.globalActorFactories; },
 
   /**
    * Performs cleanup tasks before shutting down the debugger server, if no
    * connections are currently open. Such tasks include clearing any actor
    * constructors added at runtime. This method should be called whenever a
    * debugger server is no longer useful, to avoid memory leaks. After this
    * method returns, the debugger server must be initialized again before use.
    */
   destroy: function DH_destroy() {
     if (Object.keys(this._connections).length == 0) {
-      dumpn("Shutting down debugger server.");
+      this.closeListener();
       delete this.globalActorFactories;
       delete this.tabActorFactories;
+      delete this._allowConnection;
+      this._transportInitialized = false;
+      dumpn("Debugger server is shut down.");
     }
   },
 
   /**
    * Load a subscript into the debugging global.
    *
    * @param aURL string A url that will be loaded as a subscript into the
    *        debugging global.  The user must load at least one script
@@ -215,18 +226,16 @@ var DebuggerServer = {
   /**
    * Close a previously-opened TCP listener.
    *
    * @param aForce boolean [optional]
    *        If set to true, then the socket will be closed, regardless of the
    *        number of open connections.
    */
   closeListener: function DH_closeListener(aForce) {
-    this._checkInit();
-
     if (!this._listener || this._socketConnections == 0) {
       return false;
     }
 
     // Only close the listener when the last connection is closed, or if the
     // aForce flag is passed.
     if (--this._socketConnections == 0 || aForce) {
       this._listener.close();
--- a/toolkit/devtools/debugger/tests/unit/test_dbgsocket.js
+++ b/toolkit/devtools/debugger/tests/unit/test_dbgsocket.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource:///modules/devtools/dbg-server.jsm");
 Cu.import("resource:///modules/devtools/dbg-client.jsm");
 
 function run_test()
 {
   // Allow incoming connections.
-  DebuggerServer.init(function () { return true; });
+  DebuggerServer.init(function () true);
   DebuggerServer.addActors("resource://test/testactors.js");
 
   add_test(test_socket_conn);
   add_test(test_socket_shutdown);
   add_test(test_pipe_conn);
 
   run_next_test();
 }
@@ -54,20 +54,45 @@ function test_socket_conn()
       run_next_test();
     },
   };
   transport.ready();
 }
 
 function test_socket_shutdown()
 {
-  do_check_eq(DebuggerServer._socketConnections, 1);
-  do_check_true(DebuggerServer.closeListener());
+  let count = 0;
+  wait_for_server_shutdown(count);
+}
+
+function wait_for_server_shutdown(aCount)
+{
+  do_timeout(100, function() {
+    dump("count: "+aCount+" ");
+    if (++aCount > 20) {
+      do_throw("Timed out waiting for the server to shut down.");
+      return;
+    }
+    if (DebuggerServer.initialized) {
+      wait_for_server_shutdown(aCount);
+      return;
+    }
+    real_test_socket_shutdown(aCount);
+  });
+}
+
+function real_test_socket_shutdown()
+{
+  // After the last conection was closed, the server must be initialized again.
+  // Allow incoming connections.
+  DebuggerServer.init(function () true);
+  DebuggerServer.addActors("resource://test/testactors.js");
+
   do_check_eq(DebuggerServer._socketConnections, 0);
-  // Make sure closing the listener twice does nothing.
+  // Make sure closing a non-started listener does nothing.
   do_check_false(DebuggerServer.closeListener());
   do_check_eq(DebuggerServer._socketConnections, 0);
 
   let transport = debuggerSocketConnect("127.0.0.1", 2929);
   transport.hooks = {
     onPacket: function(aPacket) {
       // Shouldn't reach this, should never connect.
       do_check_true(false);