Bug 792867 - Debugger mochitests leak when run separately. r=rcampbell
authorPanos Astithas <past@mozilla.com>
Sat, 22 Sep 2012 18:44:00 +0300
changeset 107844 4e6eb2cb00b5246bd71af68b56faf67667bd6dff
parent 107843 4900ae2923153aa1a3ddbe25f6353d56c52fa319
child 107845 87a7a1c60a3ad344ae9e1b8f0f70997e4ea91e1c
push id23513
push userttaubert@mozilla.com
push dateSun, 23 Sep 2012 16:16:24 +0000
treeherdermozilla-central@95f1163d4fe3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrcampbell
bugs792867
milestone18.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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);