Back out b2900e347f5c (bug 783393) for Windows xpcshell hangs in test_listscripts-01.js on a CLOSED TREE
authorMatt Brubeck <mbrubeck@mozilla.com>
Thu, 23 Aug 2012 14:37:29 -0700
changeset 105172 cace7cc25814f222284ade58e875f11c5f32c36c
parent 105171 3c39442f9f19c5c3e5db6453242dc38ca39fd4e7
child 105258 e509c7472f301a805ee6430dd175daef55b98987
child 105286 14f0f28ad1a99989b4885d376dbd0ea6794d57ee
push id55
push usershu@rfrn.org
push dateThu, 30 Aug 2012 01:33:09 +0000
bugs783393
milestone17.0a1
backs outb2900e347f5c30f26d5a57d83182d65b5702341b
Back out b2900e347f5c (bug 783393) for Windows xpcshell hangs in test_listscripts-01.js on a CLOSED TREE
browser/devtools/debugger/debugger-controller.js
browser/devtools/debugger/test/browser_dbg_bfcache.js
browser/devtools/debugger/test/browser_dbg_location-changes-blank.js
browser/devtools/debugger/test/browser_dbg_location-changes-new.js
browser/devtools/debugger/test/browser_dbg_location-changes.js
toolkit/devtools/debugger/dbg-client.jsm
toolkit/devtools/debugger/server/dbg-browser-actors.js
toolkit/devtools/debugger/server/dbg-script-actors.js
toolkit/devtools/debugger/server/dbg-server.js
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -180,24 +180,31 @@ let DebuggerController = {
     this.client.close();
 
     this.client = null;
     this.tabClient = null;
     this.activeThread = null;
   },
 
   /**
-   * This function is called on each location change in this tab.
+   * Starts debugging the current tab. This function is called on each location
+   * change in this tab.
    */
   _onTabNavigated: function DC__onTabNavigated(aNotification, aPacket) {
-    DebuggerController.ThreadState._handleTabNavigation(function() {
-      DebuggerController.StackFrames._handleTabNavigation(function() {
-        DebuggerController.SourceScripts._handleTabNavigation();
-      });
-    });
+    let client = this.client;
+
+    client.activeThread.detach(function() {
+      client.activeTab.detach(function() {
+        client.listTabs(function(aResponse) {
+          let tab = aResponse.tabs[aResponse.selected];
+          this._startDebuggingTab(client, tab);
+          this.dispatchEvent("Debugger:Connecting");
+        }.bind(this));
+      }.bind(this));
+    }.bind(this));
   },
 
   /**
    * Stops debugging the current tab.
    */
   _onTabDetached: function DC__onTabDetached() {
     this.dispatchEvent("Debugger:Close");
   },
@@ -315,17 +322,17 @@ ThreadState.prototype = {
    * @param function aCallback
    *        The next function in the initialization sequence.
    */
   connect: function TS_connect(aCallback) {
     this.activeThread.addListener("paused", this._update);
     this.activeThread.addListener("resumed", this._update);
     this.activeThread.addListener("detached", this._update);
 
-    this._handleTabNavigation();
+    this._update();
 
     aCallback && aCallback();
   },
 
   /**
    * Disconnect from the client.
    */
   disconnect: function TS_disconnect() {
@@ -333,25 +340,16 @@ ThreadState.prototype = {
       return;
     }
     this.activeThread.removeListener("paused", this._update);
     this.activeThread.removeListener("resumed", this._update);
     this.activeThread.removeListener("detached", this._update);
   },
 
   /**
-   * Handles any initialization on a tab navigation event issued by the client.
-   */
-  _handleTabNavigation: function TS__handleTabNavigation(aCallback) {
-    DebuggerView.StackFrames.updateState(this.activeThread.state);
-
-    aCallback && aCallback();
-  },
-
-  /**
    * Update the UI after a thread state change.
    */
   _update: function TS__update(aEvent) {
     DebuggerView.StackFrames.updateState(this.activeThread.state);
   }
 };
 
 /**
@@ -394,64 +392,55 @@ StackFrames.prototype = {
   /**
    * Watch the given thread client.
    *
    * @param function aCallback
    *        The next function in the initialization sequence.
    */
   connect: function SF_connect(aCallback) {
     window.addEventListener("Debugger:FetchedVariables", this._onFetchedVars, false);
+
     this.activeThread.addListener("paused", this._onPaused);
     this.activeThread.addListener("resumed", this._onResume);
     this.activeThread.addListener("framesadded", this._onFrames);
     this.activeThread.addListener("framescleared", this._onFramesCleared);
 
-    this._handleTabNavigation();
     this.updatePauseOnExceptions(this.pauseOnExceptions);
 
     aCallback && aCallback();
   },
 
   /**
    * Disconnect from the client.
    */
   disconnect: function SF_disconnect() {
+    window.removeEventListener("Debugger:FetchedVariables", this._onFetchedVars, false);
+
     if (!this.activeThread) {
       return;
     }
-    window.removeEventListener("Debugger:FetchedVariables", this._onFetchedVars, false);
     this.activeThread.removeListener("paused", this._onPaused);
     this.activeThread.removeListener("resumed", this._onResume);
     this.activeThread.removeListener("framesadded", this._onFrames);
     this.activeThread.removeListener("framescleared", this._onFramesCleared);
   },
 
   /**
-   * Handles any initialization on a tab navigation event issued by the client.
-   */
-  _handleTabNavigation: function SF__handleTabNavigation(aCallback) {
-    // Nothing to do here yet.
-
-    aCallback && aCallback();
-  },
-
-  /**
    * Handler for the thread client's paused notification.
    *
    * @param string aEvent
    *        The name of the notification ("paused" in this case).
    * @param object aPacket
    *        The response packet.
    */
   _onPaused: function SF__onPaused(aEvent, aPacket) {
     // In case the pause was caused by an exception, store the exception value.
     if (aPacket.why.type == "exception") {
       this.exception = aPacket.why.exception;
     }
-
     this.activeThread.fillFrames(this.pageSize);
     DebuggerView.editor.focus();
   },
 
   /**
    * Handler for the thread client's resumed notification.
    */
   _onResume: function SF__onResume() {
@@ -840,16 +829,17 @@ StackFrames.prototype = {
 
 /**
  * Keeps the source script list up-to-date, using the thread client's
  * source script cache.
  */
 function SourceScripts() {
   this._onNewScript = this._onNewScript.bind(this);
   this._onScriptsAdded = this._onScriptsAdded.bind(this);
+  this._onScriptsCleared = this._onScriptsCleared.bind(this);
   this._onShowScript = this._onShowScript.bind(this);
   this._onLoadSource = this._onLoadSource.bind(this);
   this._onLoadSourceFinished = this._onLoadSourceFinished.bind(this);
 }
 
 SourceScripts.prototype = {
 
   /**
@@ -874,107 +864,91 @@ SourceScripts.prototype = {
   /**
    * Watch the given thread client.
    *
    * @param function aCallback
    *        The next function in the initialization sequence.
    */
   connect: function SS_connect(aCallback) {
     window.addEventListener("Debugger:LoadSource", this._onLoadSource, false);
+
     this.debuggerClient.addListener("newScript", this._onNewScript);
+    this.activeThread.addListener("scriptsadded", this._onScriptsAdded);
+    this.activeThread.addListener("scriptscleared", this._onScriptsCleared);
 
-    this._handleTabNavigation();
+    this._clearLabelsCache();
+    this._onScriptsCleared();
+
+    // Retrieve the list of scripts known to the server from before the client
+    // was ready to handle new script notifications.
+    this.activeThread.fillScripts();
 
     aCallback && aCallback();
   },
 
   /**
    * Disconnect from the client.
    */
-  disconnect: function SS_disconnect() {
+  disconnect: function TS_disconnect() {
+    window.removeEventListener("Debugger:LoadSource", this._onLoadSource, false);
+
     if (!this.activeThread) {
       return;
     }
-    window.removeEventListener("Debugger:LoadSource", this._onLoadSource, false);
     this.debuggerClient.removeListener("newScript", this._onNewScript);
-  },
-
-  /**
-   * Handles any initialization on a tab navigation event issued by the client.
-   */
-  _handleTabNavigation: function SS__handleTabNavigation(aCallback) {
-    this._clearLabelsCache();
-    this._onScriptsCleared();
-
-    // Retrieve the list of scripts known to the server from before the client
-    // was ready to handle new script notifications.
-    this.activeThread.getScripts(this._onScriptsAdded);
-
-    aCallback && aCallback();
+    this.activeThread.removeListener("scriptsadded", this._onScriptsAdded);
+    this.activeThread.removeListener("scriptscleared", this._onScriptsCleared);
   },
 
   /**
    * Handler for the debugger client's unsolicited newScript notification.
    */
   _onNewScript: function SS__onNewScript(aNotification, aPacket) {
     // Ignore scripts generated from 'clientEvaluate' packets.
     if (aPacket.url == "debugger eval code") {
       return;
     }
 
     this._addScript({ url: aPacket.url, startLine: aPacket.startLine }, true);
 
-    let preferredScriptUrl = DebuggerView.Scripts.preferredScriptUrl;
-
-    // Select this script if it's the preferred one.
+    // Select the script if it's the preferred one.
     if (aPacket.url === DebuggerView.Scripts.preferredScriptUrl) {
       DebuggerView.Scripts.selectScript(aPacket.url);
     }
-    // ..or the first entry if there's not one selected yet.
-    else if (!DebuggerView.Scripts.selected) {
-      DebuggerView.Scripts.selectIndex(0);
-    }
 
     // If there are any stored breakpoints for this script, display them again,
     // both in the editor and the pane.
     for each (let breakpoint in DebuggerController.Breakpoints.store) {
       if (breakpoint.location.url == aPacket.url) {
         DebuggerController.Breakpoints.displayBreakpoint(breakpoint);
       }
     }
-
-    DebuggerController.dispatchEvent("Debugger:AfterNewScript");
   },
 
   /**
-   * Callback for the getScripts() method.
+   * Handler for the thread client's scriptsadded notification.
    */
-  _onScriptsAdded: function SS__onScriptsAdded(aResponse) {
-    for each (let script in aResponse.scripts) {
+  _onScriptsAdded: function SS__onScriptsAdded() {
+    for each (let script in this.activeThread.cachedScripts) {
       this._addScript(script, false);
     }
     DebuggerView.Scripts.commitScripts();
     DebuggerController.Breakpoints.updatePaneBreakpoints();
 
+    // Select the preferred script if one exists, the first entry otherwise.
     let preferredScriptUrl = DebuggerView.Scripts.preferredScriptUrl;
-
-    // Select the preferred script if it exists and was part of the response.
     if (preferredScriptUrl && DebuggerView.Scripts.contains(preferredScriptUrl)) {
       DebuggerView.Scripts.selectScript(preferredScriptUrl);
-    }
-    // ..or the first entry if there's not one selected yet.
-    else if (!DebuggerView.Scripts.selected) {
+    } else {
       DebuggerView.Scripts.selectIndex(0);
     }
-
-    DebuggerController.dispatchEvent("Debugger:AfterScriptsAdded");
   },
 
   /**
-   * Called during navigation to clear the currently-loaded scripts.
+   * Handler for the thread client's scriptscleared notification.
    */
   _onScriptsCleared: function SS__onScriptsCleared() {
     DebuggerView.Scripts.empty();
     DebuggerView.Breakpoints.emptyText();
     DebuggerView.editor.setText("");
   },
 
   /**
--- a/browser/devtools/debugger/test/browser_dbg_bfcache.js
+++ b/browser/devtools/debugger/test/browser_dbg_bfcache.js
@@ -35,62 +35,50 @@ function testInitialLoad() {
   });
 
   gDebuggee.firstCall();
 }
 
 function testLocationChange()
 {
   gDebugger.DebuggerController.activeThread.resume(function() {
-    gDebugger.DebuggerController.client.addOneTimeListener("tabNavigated", function(aEvent, aPacket) {
-      ok(true, "tabNavigated event was fired.");
-      info("Still attached to the tab.");
-
-      gDebugger.addEventListener("Debugger:AfterScriptsAdded", function _onEvent(aEvent) {
-        gDebugger.removeEventListener(aEvent.type, _onEvent);
-
+    gDebugger.DebuggerController.client.addOneTimeListener("tabAttached", function(aEvent, aPacket) {
+      ok(true, "Successfully reattached to the tab again.");
+      gDebugger.DebuggerController.client.addOneTimeListener("resumed", function(aEvent, aPacket) {
         executeSoon(function() {
           validateSecondPage();
           testBack();
         });
       });
     });
     content.location = STACK_URL;
   });
 }
 
 function testBack()
 {
-  gDebugger.DebuggerController.client.addOneTimeListener("tabNavigated", function(aEvent, aPacket) {
-    ok(true, "tabNavigated event was fired after going back.");
-    info("Still attached to the tab.");
-
-    gDebugger.addEventListener("Debugger:AfterScriptsAdded", function _onEvent(aEvent) {
-      gDebugger.removeEventListener(aEvent.type, _onEvent);
-
+  gDebugger.DebuggerController.client.addOneTimeListener("tabAttached", function(aEvent, aPacket) {
+    ok(true, "Successfully reattached to the tab after going back.");
+    gDebugger.DebuggerController.client.addOneTimeListener("resumed", function(aEvent, aPacket) {
       executeSoon(function() {
         validateFirstPage();
         testForward();
       });
     });
   });
 
   info("Going back.");
   content.history.back();
 }
 
 function testForward()
 {
-  gDebugger.DebuggerController.client.addOneTimeListener("tabNavigated", function(aEvent, aPacket) {
-    ok(true, "tabNavigated event was fired after going forward.");
-    info("Still attached to the tab.");
-
-    gDebugger.addEventListener("Debugger:AfterScriptsAdded", function _onEvent(aEvent) {
-      gDebugger.removeEventListener(aEvent.type, _onEvent);
-
+  gDebugger.DebuggerController.client.addOneTimeListener("tabAttached", function(aEvent, aPacket) {
+    ok(true, "Successfully reattached to the tab after going forward.");
+    gDebugger.DebuggerController.client.addOneTimeListener("resumed", function(aEvent, aPacket) {
       executeSoon(function() {
         validateSecondPage();
         closeDebuggerAndFinish();
       });
     });
   });
 
   info("Going forward.");
--- a/browser/devtools/debugger/test/browser_dbg_location-changes-blank.js
+++ b/browser/devtools/debugger/test/browser_dbg_location-changes-blank.js
@@ -52,34 +52,35 @@ function testSimpleCall() {
   gDebuggee.simpleCall();
 }
 
 function testLocationChange()
 {
   gDebugger.DebuggerController.activeThread.resume(function() {
     gDebugger.DebuggerController.client.addOneTimeListener("tabNavigated", function(aEvent, aPacket) {
       ok(true, "tabNavigated event was fired.");
-      info("Still attached to the tab.");
+      gDebugger.DebuggerController.client.addOneTimeListener("tabAttached", function(aEvent, aPacket) {
+        ok(true, "Successfully reattached to the tab again.");
 
-      gDebugger.addEventListener("Debugger:AfterScriptsAdded", function _onEvent(aEvent) {
-        gDebugger.removeEventListener(aEvent.type, _onEvent);
+        // Wait for the initial resume...
+        gDebugger.gClient.addOneTimeListener("resumed", function() {
+          is(gDebugger.DebuggerView.Scripts.selected, null,
+            "There should be no selected script.");
+          is(gDebugger.editor.getText().length, 0,
+            "The source editor not have any text displayed.");
 
-        is(gDebugger.DebuggerView.Scripts.selected, null,
-          "There should be no selected script.");
-        is(gDebugger.editor.getText().length, 0,
-          "The source editor not have any text displayed.");
+          let menulist = gDebugger.DebuggerView.Scripts._scripts;
+          let noScripts = gDebugger.L10N.getStr("noScriptsText");
+          is(menulist.getAttribute("label"), noScripts,
+            "The menulist should display a notice that there are no scripts availalble.");
+          is(menulist.getAttribute("tooltiptext"), "",
+            "The menulist shouldn't have any tooltip text attributed when there are no scripts available.");
 
-        let menulist = gDebugger.DebuggerView.Scripts._scripts;
-        let noScripts = gDebugger.L10N.getStr("noScriptsText");
-        is(menulist.getAttribute("label"), noScripts,
-          "The menulist should display a notice that there are no scripts availalble.");
-        is(menulist.getAttribute("tooltiptext"), "",
-          "The menulist shouldn't have any tooltip text attributed when there are no scripts available.");
-
-        closeDebuggerAndFinish();
+          closeDebuggerAndFinish();
+        });
       });
     });
     content.location = "about:blank";
   });
 }
 
 registerCleanupFunction(function() {
   removeTab(gTab);
--- a/browser/devtools/debugger/test/browser_dbg_location-changes-new.js
+++ b/browser/devtools/debugger/test/browser_dbg_location-changes-new.js
@@ -52,34 +52,35 @@ function testSimpleCall() {
   gDebuggee.simpleCall();
 }
 
 function testLocationChange()
 {
   gDebugger.DebuggerController.activeThread.resume(function() {
     gDebugger.DebuggerController.client.addOneTimeListener("tabNavigated", function(aEvent, aPacket) {
       ok(true, "tabNavigated event was fired.");
-      info("Still attached to the tab.");
+      gDebugger.DebuggerController.client.addOneTimeListener("tabAttached", function(aEvent, aPacket) {
+        ok(true, "Successfully reattached to the tab again.");
 
-      gDebugger.addEventListener("Debugger:AfterScriptsAdded", function _onEvent(aEvent) {
-        gDebugger.removeEventListener(aEvent.type, _onEvent);
+        // Wait for the initial resume...
+        gDebugger.gClient.addOneTimeListener("resumed", function() {
+          isnot(gDebugger.DebuggerView.Scripts.selected, null,
+            "There should be a selected script.");
+          isnot(gDebugger.editor.getText().length, 0,
+            "The source editor should have some text displayed.");
 
-        isnot(gDebugger.DebuggerView.Scripts.selected, null,
-          "There should be a selected script.");
-        isnot(gDebugger.editor.getText().length, 0,
-          "The source editor should have some text displayed.");
+          let menulist = gDebugger.DebuggerView.Scripts._scripts;
+          let noScripts = gDebugger.L10N.getStr("noScriptsText");
+          isnot(menulist.getAttribute("label"), noScripts,
+            "The menulist should not display a notice that there are no scripts availalble.");
+          isnot(menulist.getAttribute("tooltiptext"), "",
+            "The menulist should have a tooltip text attributed.");
 
-        let menulist = gDebugger.DebuggerView.Scripts._scripts;
-        let noScripts = gDebugger.L10N.getStr("noScriptsText");
-        isnot(menulist.getAttribute("label"), noScripts,
-          "The menulist should not display a notice that there are no scripts availalble.");
-        isnot(menulist.getAttribute("tooltiptext"), "",
-          "The menulist should have a tooltip text attributed.");
-
-        closeDebuggerAndFinish();
+          closeDebuggerAndFinish();
+        });
       });
     });
     content.location = EXAMPLE_URL + "browser_dbg_iframes.html";
   });
 }
 
 registerCleanupFunction(function() {
   removeTab(gTab);
--- a/browser/devtools/debugger/test/browser_dbg_location-changes.js
+++ b/browser/devtools/debugger/test/browser_dbg_location-changes.js
@@ -47,19 +47,21 @@ function testSimpleCall() {
   gDebuggee.simpleCall();
 }
 
 function testLocationChange()
 {
   gDebugger.DebuggerController.activeThread.resume(function() {
     gDebugger.DebuggerController.client.addOneTimeListener("tabNavigated", function(aEvent, aPacket) {
       ok(true, "tabNavigated event was fired.");
-      info("Still attached to the tab.");
+      gDebugger.DebuggerController.client.addOneTimeListener("tabAttached", function(aEvent, aPacket) {
+        ok(true, "Successfully reattached to the tab again.");
 
-      closeDebuggerAndFinish();
+        closeDebuggerAndFinish();
+      });
     });
     content.location = TAB1_URL;
   });
 }
 
 registerCleanupFunction(function() {
   removeTab(gTab);
   gPane = null;
--- a/toolkit/devtools/debugger/dbg-client.jsm
+++ b/toolkit/devtools/debugger/dbg-client.jsm
@@ -173,27 +173,16 @@ const ThreadStateTypes = {
  */
 const UnsolicitedNotifications = {
   "newScript": "newScript",
   "tabDetached": "tabDetached",
   "tabNavigated": "tabNavigated"
 };
 
 /**
- * Set of pause types that are sent by the server and not as an immediate
- * response to a client request.
- */
-const UnsolicitedPauses = {
-  "resumeLimit": "resumeLimit",
-  "debuggerStatement": "debuggerStatement",
-  "breakpoint": "breakpoint",
-  "watchpoint": "watchpoint"
-};
-
-/**
  * Set of debug protocol request types that specify the protocol request being
  * sent to the server.
  */
 const DebugProtocolTypes = {
   "assign": "assign",
   "attach": "attach",
   "clientEvaluate": "clientEvaluate",
   "delete": "delete",
@@ -406,38 +395,28 @@ DebuggerClient.prototype = {
     try {
       if (!aPacket.from) {
         Cu.reportError("Server did not specify an actor, dropping packet: " +
                        JSON.stringify(aPacket));
         return;
       }
 
       let onResponse;
-      // Don't count unsolicited notifications or pauses as responses.
+      // Don't count unsolicited notifications as responses.
       if (aPacket.from in this._activeRequests &&
-          !(aPacket.type in UnsolicitedNotifications) &&
-          !(aPacket.type == ThreadStateTypes.paused &&
-            aPacket.why.type in UnsolicitedPauses)) {
+          !(aPacket.type in UnsolicitedNotifications)) {
         onResponse = this._activeRequests[aPacket.from].onResponse;
         delete this._activeRequests[aPacket.from];
       }
 
-      // Packets that indicate thread state changes get special treatment.
+      // paused/resumed/detached get special treatment...
       if (aPacket.type in ThreadStateTypes &&
           aPacket.from in this._threadClients) {
         this._threadClients[aPacket.from]._onThreadState(aPacket);
       }
-      // On navigation the server resumes, so the client must resume as well.
-      // We achive that by generating a fake resumption packet that triggers
-      // the client's thread state change listeners.
-      if (aPacket.type == UnsolicitedNotifications.tabNavigated &&
-          aPacket.from in this._tabClients) {
-        let resumption = { from: this.activeThread._actor, type: "resumed" };
-        this.activeThread._onThreadState(resumption);
-      }
       this.notify(aPacket.type, aPacket);
 
       if (onResponse) {
         onResponse(aPacket);
       }
     } catch(ex) {
       dumpn("Error handling response: " + ex + " - stack:\n" + ex.stack);
       Cu.reportError(ex);
@@ -741,30 +720,22 @@ ThreadClient.prototype = {
    * @param aOnResponse integer
    *        Called with the thread's response.
    */
   getScripts: function TC_getScripts(aOnResponse) {
     let packet = { to: this._actor, type: DebugProtocolTypes.scripts };
     this._client.request(packet, aOnResponse);
   },
 
-  _doInterrupted: function TC_doInterrupted(aAction, aError) {
-    if (this.paused) {
-      aAction();
-      return;
-    }
-    this.interrupt(function(aResponse) {
-      if (aResponse) {
-        aError(aResponse);
-        return;
-      }
-      aAction();
-      this.resume(function() {});
-    }.bind(this));
-  },
+  /**
+   * A cache of source scripts. Clients can observe the scriptsadded and
+   * scriptscleared event to keep up to date on changes to this cache,
+   * and can fill it using the fillScripts method.
+   */
+  get cachedScripts() { return this._scriptCache; },
 
   /**
    * Ensure that source scripts have been loaded in the
    * ThreadClient's source script cache. A scriptsadded event will be
    * sent when the source script cache is updated.
    *
    * @returns true if a scriptsadded notification should be expected.
    */
--- a/toolkit/devtools/debugger/server/dbg-browser-actors.js
+++ b/toolkit/devtools/debugger/server/dbg-browser-actors.js
@@ -441,29 +441,20 @@ BrowserTabActor.prototype = {
   onWindowCreated: function BTA_onWindowCreated(evt) {
     if (evt.target === this.browser.contentDocument) {
       // pageshow events for non-persisted pages have already been handled by a
       // prior DOMWindowCreated event.
       if (evt.type == "pageshow" && !evt.persisted) {
         return;
       }
       if (this._attached) {
-        this.threadActor.clearDebuggees();
-        this.threadActor.dbg.enabled = true;
-        if (this._progressListener) {
-          delete this._progressListener._needsTabNavigated;
-        }
         this.conn.send({ from: this.actorID, type: "tabNavigated",
                          url: this.browser.contentDocument.URL });
       }
     }
-
-    if (this._attached) {
-      this._addDebuggees(evt.target.defaultView.wrappedJSObject);
-    }
   }
 };
 
 /**
  * The request types this actor can handle.
  */
 BrowserTabActor.prototype.requestTypes = {
   "attach": BrowserTabActor.prototype.onAttach,
@@ -503,23 +494,20 @@ DebuggerProgressListener.prototype = {
       }
 
       // If the debuggee is not paused, then proceed normally.
       if (this._tabActor.threadActor.state != "paused") {
         return;
       }
 
       aRequest.suspend();
-      this._tabActor.threadActor.onResume();
-      this._tabActor.threadActor.dbg.enabled = false;
       this._tabActor._pendingNavigation = aRequest;
+      this._tabActor._detach();
       this._needsTabNavigated = true;
     } else if (isStop && isWindow && isNetwork && this._needsTabNavigated) {
-      delete this._needsTabNavigated;
-      this._tabActor.threadActor.dbg.enabled = true;
       this._tabActor.conn.send({
         from: this._tabActor.actorID,
         type: "tabNavigated",
         url: this._tabActor.browser.contentDocument.URL
       });
 
       this.destroy();
     }
--- a/toolkit/devtools/debugger/server/dbg-script-actors.js
+++ b/toolkit/devtools/debugger/server/dbg-script-actors.js
@@ -46,56 +46,35 @@ ThreadActor.prototype = {
   get threadLifetimePool() {
     if (!this._threadLifetimePool) {
       this._threadLifetimePool = new ActorPool(this.conn);
       this.conn.addActorPool(this._threadLifetimePool);
     }
     return this._threadLifetimePool;
   },
 
-  clearDebuggees: function TA_clearDebuggees() {
-    if (this._dbg) {
-      let debuggees = this._dbg.getDebuggees();
-      for (let debuggee of debuggees) {
-        this._dbg.removeDebuggee(debuggee);
-      }
-    }
-    this.conn.removeActorPool(this._threadLifetimePool || undefined);
-    this._threadLifetimePool = null;
-    // Unless we carefully take apart the scripts table this way, we end up
-    // leaking documents. It would be nice to track this down carefully, once
-    // we have the appropriate tools.
-    for (let url in this._scripts) {
-      delete this._scripts[url];
-    }
-    this._scripts = {};
-  },
-
   /**
    * Add a debuggee global to the Debugger object.
    */
   addDebuggee: function TA_addDebuggee(aGlobal) {
     // Use the inspector xpcom component to turn on debugging
     // for aGlobal's compartment.  Ideally this won't be necessary
     // medium- to long-term, and will be managed by the engine
     // instead.
 
     if (!this._dbg) {
       this._dbg = new Debugger();
-      this._dbg.uncaughtExceptionHook = this.uncaughtExceptionHook.bind(this);
-      this._dbg.onDebuggerStatement = this.onDebuggerStatement.bind(this);
-      this._dbg.onNewScript = this.onNewScript.bind(this);
-      // Keep the debugger disabled until a client attaches.
-      this.dbg.enabled = this._state != "detached";
     }
 
     this.dbg.addDebuggee(aGlobal);
-    for (let s of this.dbg.findScripts()) {
-      this._addScript(s);
-    }
+    this.dbg.uncaughtExceptionHook = this.uncaughtExceptionHook.bind(this);
+    this.dbg.onDebuggerStatement = this.onDebuggerStatement.bind(this);
+    this.dbg.onNewScript = this.onNewScript.bind(this);
+    // Keep the debugger disabled until a client attaches.
+    this.dbg.enabled = false;
   },
 
   /**
    * Remove a debuggee global from the JSInspector.
    */
   removeDebugee: function TA_removeDebuggee(aGlobal) {
     try {
       this.dbg.removeDebuggee(aGlobal);
@@ -106,24 +85,29 @@ ThreadActor.prototype = {
   },
 
   disconnect: function TA_disconnect() {
     if (this._state == "paused") {
       this.onResume();
     }
 
     this._state = "exited";
-
-    this.clearDebuggees();
-
-    if (!this._dbg) {
-      return;
+    if (this.dbg) {
+      this.dbg.enabled = false;
+      this._dbg = null;
     }
-    this._dbg.enabled = false;
-    this._dbg = null;
+    this.conn.removeActorPool(this._threadLifetimePool || undefined);
+    this._threadLifetimePool = null;
+    // Unless we carefully take apart the scripts table this way, we end up
+    // leaking documents. It would be nice to track this down carefully, once
+    // we have the appropriate tools.
+    for (let url in this._scripts) {
+      delete this._scripts[url];
+    }
+    this._scripts = {};
   },
 
   /**
    * Disconnect the debugger and put the actor in the exited state.
    */
   exit: function TA_exit() {
     this.disconnect();
   },
--- a/toolkit/devtools/debugger/server/dbg-server.js
+++ b/toolkit/devtools/debugger/server/dbg-server.js
@@ -361,20 +361,17 @@ DebuggerServerConnection.prototype = {
   addActorPool: function DSC_addActorPool(aActorPool) {
     this._extraPools.push(aActorPool);
   },
 
   /**
    * Remove a previously-added pool of actors to the connection.
    */
   removeActorPool: function DSC_removeActorPool(aActorPool) {
-    let index = this._extraPools.lastIndexOf(aActorPool);
-    if (index > -1) {
-      this._extraPools.splice(index, 1);
-    }
+    let index = this._extraPools.splice(this._extraPools.lastIndexOf(aActorPool), 1);
   },
 
   /**
    * Add an actor to the default actor pool for this connection.
    */
   addActor: function DSC_addActor(aActor) {
     this._actorPool.addActor(aActor);
   },