Bug 723563 - Use findScripts to retrieve the list of scripts known to the debugger; r=rcampbell
authorPanos Astithas <past@mozilla.com>
Fri, 30 Mar 2012 11:25:52 +0300
changeset 90528 401804642db1e608f336c085aeff473af1cdd8c7
parent 90527 7cc16b3f172d4f7a1d89dcfaab18b2f41cc112a7
child 90529 51e463e400d0b06e6c36d91ec41b71386868f5ed
push id649
push userpastithas@mozilla.com
push dateFri, 30 Mar 2012 08:58:31 +0000
treeherderfx-team@51e463e400d0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrcampbell
bugs723563
milestone14.0a1
Bug 723563 - Use findScripts to retrieve the list of scripts known to the debugger; r=rcampbell
browser/devtools/debugger/debugger.js
browser/devtools/debugger/test/browser_dbg_bug723069_editor-breakpoints.js
browser/devtools/debugger/test/browser_dbg_bug731394_editor-contextmenu.js
browser/devtools/debugger/test/browser_dbg_debuggerstatement.js
browser/devtools/debugger/test/browser_dbg_listtabs.js
browser/devtools/debugger/test/browser_dbg_propertyview-01.js
browser/devtools/debugger/test/browser_dbg_propertyview-07.js
browser/devtools/debugger/test/browser_dbg_propertyview-08.js
browser/devtools/debugger/test/browser_dbg_script-switching.js
browser/devtools/debugger/test/browser_dbg_stack-05.js
browser/devtools/debugger/test/browser_dbg_update-editor-mode.js
browser/devtools/webconsole/test/browser_gcli_break.js
browser/locales/en-US/chrome/browser/devtools/debugger.properties
toolkit/devtools/debugger/server/dbg-browser-actors.js
toolkit/devtools/debugger/server/dbg-script-actors.js
--- a/browser/devtools/debugger/debugger.js
+++ b/browser/devtools/debugger/debugger.js
@@ -306,16 +306,20 @@ var StackFrames = {
       objClient.getSignature(function SF_getSignature(aResponse) {
         for (let i = 0; i < aResponse.parameters.length; i++) {
           let param = aResponse.parameters[i];
           let paramVar = localScope.addVar(param);
           let paramVal = frame.arguments[i];
           paramVar.setGrip(paramVal);
           this._addExpander(paramVar, paramVal);
         }
+        // Signal that call parameters have been fetched.
+        let evt = document.createEvent("Event");
+        evt.initEvent("Debugger:FetchedParameters", true, false);
+        document.documentElement.dispatchEvent(evt);
       }.bind(this));
     }
   },
 
   /**
    * Update the source editor current debug location based on the selected frame
    * and script.
    */
@@ -464,44 +468,35 @@ var SourceScripts = {
    *        The thread client.
    * @param function aCallback
    *        The next function in the initialization sequence.
    */
   connect: function SS_connect(aThreadClient, aCallback) {
     DebuggerView.Scripts.addChangeListener(this.onChange);
 
     this.activeThread = aThreadClient;
-    aThreadClient.addListener("paused", this.onPaused);
     aThreadClient.addListener("scriptsadded", this.onScripts);
     aThreadClient.addListener("scriptscleared", this.onScriptsCleared);
     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 TS_disconnect() {
-    this.activeThread.removeListener("paused", this.onPaused);
     this.activeThread.removeListener("scriptsadded", this.onScripts);
     this.activeThread.removeListener("scriptscleared", this.onScriptsCleared);
   },
 
   /**
-   * Handler for the thread client's paused notification. This is triggered only
-   * once, to retrieve the list of scripts known to the server from before the
-   * client was ready to handle new script notifications.
-   */
-  onPaused: function SS_onPaused() {
-    this.activeThread.removeListener("paused", this.onPaused);
-    this.activeThread.fillScripts();
-  },
-
-  /**
    * Handler for the debugger client's unsolicited newScript notification.
    */
   onNewScript: function SS_onNewScript(aNotification, aPacket) {
     this._addScript({ url: aPacket.url, startLine: aPacket.startLine });
   },
 
   /**
    * Handler for the thread client's scriptsadded notification.
@@ -652,16 +647,15 @@ var SourceScripts = {
       window.editor.setText(aScript.text);
       window.updateEditorBreakpoints();
       StackFrames.updateEditor();
     }
     window.editor.resetUndo();
   }
 };
 
-SourceScripts.onPaused = SourceScripts.onPaused.bind(SourceScripts);
 SourceScripts.onScripts = SourceScripts.onScripts.bind(SourceScripts);
 SourceScripts.onNewScript = SourceScripts.onNewScript.bind(SourceScripts);
 SourceScripts.onScriptsCleared = SourceScripts.onScriptsCleared.bind(SourceScripts);
 SourceScripts.onChange = SourceScripts.onChange.bind(SourceScripts);
 
 window.addEventListener("DOMContentLoaded", initDebugger, false);
 window.addEventListener("unload", shutdownDebugger, false);
--- a/browser/devtools/debugger/test/browser_dbg_bug723069_editor-breakpoints.js
+++ b/browser/devtools/debugger/test/browser_dbg_bug723069_editor-breakpoints.js
@@ -23,17 +23,17 @@ function test()
   let SourceEditor = tempScope.SourceEditor;
 
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
-    gPane.activeThread.addOneTimeListener("scriptsadded", function() {
+    gPane.activeThread.addOneTimeListener("framesadded", function() {
       Services.tm.currentThread.dispatch({ run: onScriptsAdded }, 0);
     });
     gDebuggee.firstCall();
   });
 
   function onScriptsAdded()
   {
     gScripts = gDebugger.DebuggerView.Scripts;
--- a/browser/devtools/debugger/test/browser_dbg_bug731394_editor-contextmenu.js
+++ b/browser/devtools/debugger/test/browser_dbg_bug731394_editor-contextmenu.js
@@ -21,17 +21,17 @@ function test()
   let contextMenu = null;
 
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
-    gPane.activeThread.addOneTimeListener("scriptsadded", function() {
+    gPane.activeThread.addOneTimeListener("framesadded", function() {
       Services.tm.currentThread.dispatch({ run: onScriptsAdded }, 0);
     });
     gDebuggee.firstCall();
   });
 
   function onScriptsAdded()
   {
     let scripts = gDebugger.DebuggerView.Scripts._scripts;
--- a/browser/devtools/debugger/test/browser_dbg_debuggerstatement.js
+++ b/browser/devtools/debugger/test/browser_dbg_debuggerstatement.js
@@ -27,18 +27,17 @@ function test_early_debugger_statement(a
 {
   let paused = function(aEvent, aPacket) {
     ok(false, "Pause shouldn't be called before we've attached!\n");
     finish_test();
   };
   gClient.addListener("paused", paused);
   // This should continue without nesting an event loop and calling
   // the onPaused hook, because we haven't attached yet.
-  // TODO: uncomment this when bug 723563 is fixed.
-  //gTab.linkedBrowser.contentWindow.wrappedJSObject.runDebuggerStatement();
+  gTab.linkedBrowser.contentWindow.wrappedJSObject.runDebuggerStatement();
 
   gClient.removeListener("paused", paused);
 
   // Now attach and resume...
   gClient.request({ to: aActor.threadActor, type: "attach" }, function(aResponse) {
     gClient.request({ to: aActor.threadActor, type: "resume" }, function(aResponse) {
       test_debugger_statement(aActor);
     });
--- a/browser/devtools/debugger/test/browser_dbg_listtabs.js
+++ b/browser/devtools/debugger/test/browser_dbg_listtabs.js
@@ -82,17 +82,17 @@ function test_attach_removed_tab()
   removeTab(gTab2);
   gTab2 = null;
   gClient.addListener("paused", function(aEvent, aPacket) {
     ok(false, "Attaching to an exited tab actor shouldn't generate a pause.");
     finish_test();
   });
 
   gClient.request({ to: gTab2Actor, type: "attach" }, function(aResponse) {
-    is(aResponse.error, "noSuchActor", "Tab should be gone.");
+    is(aResponse.type, "exited", "Tab should consider itself exited.");
     finish_test();
   });
 }
 
 function finish_test()
 {
   gClient.close(function() {
     finish();
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-01.js
@@ -80,16 +80,17 @@ function testSimpleCall() {
 
   gDebuggee.simpleCall();
 }
 
 function resumeAndFinish() {
   gDebugger.StackFrames.activeThread.resume(function() {
     let vs = gDebugger.DebuggerView.Scripts;
     let ss = gDebugger.SourceScripts;
+    ss.onScriptsCleared();
 
     is(ss._trimUrlQuery("a/b/c.d?test=1&random=4"), "a/b/c.d",
       "Trimming the url query isn't done properly.");
 
     let urls = [
       { href: "ici://some.address.com/random/", leaf: "subrandom/" },
       { href: "ni://another.address.org/random/subrandom/", leaf: "page.html" },
       { href: "san://interesting.address.gro/random/", leaf: "script.js" },
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-07.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-07.js
@@ -20,19 +20,18 @@ function test()
     gDebugger = gPane.debuggerWindow;
 
     testFrameParameters();
   });
 }
 
 function testFrameParameters()
 {
-  // scriptsadded is fired last when switching to a paused state, so the
-  // property view will have had a chance to fetch the call parameters.
-  gPane.activeThread.addOneTimeListener("scriptsadded", function() {
+  gDebugger.addEventListener("Debugger:FetchedParameters", function test() {
+    gDebugger.removeEventListener("Debugger:FetchedParameters", test, false);
     Services.tm.currentThread.dispatch({ run: function() {
 
       var frames = gDebugger.DebuggerView.Stackframes._frames,
           childNodes = frames.childNodes,
           localScope = gDebugger.DebuggerView.Properties.localScope,
           localNodes = localScope.querySelector(".details").childNodes;
 
       is(gDebugger.StackFrames.activeThread.state, "paused",
@@ -65,17 +64,17 @@ function testFrameParameters()
       is(localNodes[6].querySelector(".info").textContent, "null",
         "Should have the right property value for 'eArg'.");
 
       is(localNodes[7].querySelector(".info").textContent, "undefined",
         "Should have the right property value for 'fArg'.");
 
       resumeAndFinish();
     }}, 0);
-  });
+  }, false);
 
   EventUtils.sendMouseEvent({ type: "click" },
     content.document.querySelector("button"),
     content.window);
 }
 
 function resumeAndFinish() {
   gPane.activeThread.addOneTimeListener("framescleared", function() {
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-08.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-08.js
@@ -20,19 +20,18 @@ function test()
     gDebugger = gPane.debuggerWindow;
 
     testFrameParameters();
   });
 }
 
 function testFrameParameters()
 {
-  // scriptsadded is fired last when switching to a paused state, so the
-  // property view will have had a chance to fetch the call parameters.
-  gPane.activeThread.addOneTimeListener("scriptsadded", function() {
+  gDebugger.addEventListener("Debugger:FetchedParameters", function test() {
+    gDebugger.removeEventListener("Debugger:FetchedParameters", test, false);
     Services.tm.currentThread.dispatch({ run: function() {
 
       var frames = gDebugger.DebuggerView.Stackframes._frames,
           localScope = gDebugger.DebuggerView.Properties.localScope,
           localNodes = localScope.querySelector(".details").childNodes;
 
       is(gDebugger.StackFrames.activeThread.state, "paused",
         "Should only be getting stack frames while paused.");
@@ -81,17 +80,17 @@ function testFrameParameters()
 
         is(localNodes[1].querySelector(".property > .title > .value")
                         .textContent, 5,
           "Should have the right argument length.");
 
         resumeAndFinish();
       }, 100);
     }}, 0);
-  });
+  }, false);
 
   EventUtils.synthesizeMouseAtCenter(content.document.querySelector("button"),
                                      {},
                                      content.window);
 }
 
 function resumeAndFinish() {
   gPane.activeThread.addOneTimeListener("framescleared", function() {
--- a/browser/devtools/debugger/test/browser_dbg_script-switching.js
+++ b/browser/devtools/debugger/test/browser_dbg_script-switching.js
@@ -26,17 +26,17 @@ function test()
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
     testScriptsDisplay();
   });
 }
 
 function testScriptsDisplay() {
-  gPane.activeThread.addOneTimeListener("scriptsadded", function() {
+  gPane.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
       gScripts = gDebugger.DebuggerView.Scripts._scripts;
 
       is(gDebugger.StackFrames.activeThread.state, "paused",
         "Should only be getting stack frames while paused.");
 
       is(gScripts.itemCount, 2, "Found the expected number of scripts.");
 
--- a/browser/devtools/debugger/test/browser_dbg_stack-05.js
+++ b/browser/devtools/debugger/test/browser_dbg_stack-05.js
@@ -21,17 +21,17 @@ function test() {
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
     testRecurse();
   });
 }
 
 function testRecurse() {
-  gPane.activeThread.addOneTimeListener("scriptsadded", function() {
+  gPane.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
       let frames = gDebugger.DebuggerView.Stackframes._frames;
       let childNodes = frames.childNodes;
 
       is(frames.querySelectorAll(".dbg-stackframe").length, 4,
         "Correct number of frames.");
 
       is(childNodes.length, frames.querySelectorAll(".dbg-stackframe").length,
--- a/browser/devtools/debugger/test/browser_dbg_update-editor-mode.js
+++ b/browser/devtools/debugger/test/browser_dbg_update-editor-mode.js
@@ -26,17 +26,17 @@ function test()
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
     testScriptsDisplay();
   });
 }
 
 function testScriptsDisplay() {
-  gPane.activeThread.addOneTimeListener("scriptsadded", function() {
+  gPane.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
       gScripts = gDebugger.DebuggerView.Scripts._scripts;
 
       is(gDebugger.StackFrames.activeThread.state, "paused",
         "Should only be getting stack frames while paused.");
 
       is(gScripts.itemCount, 2, "Found the expected number of scripts.");
 
--- a/browser/devtools/webconsole/test/browser_gcli_break.js
+++ b/browser/devtools/webconsole/test/browser_gcli_break.js
@@ -68,17 +68,17 @@ function testCreateCommands() {
   type("break add line");
   is(requisition.getStatus().toString(), "ERROR", "break add line is ERROR");
 
   let pane = DebuggerUI.toggleDebugger();
   pane.onConnected = function test_onConnected(aPane) {
     // Wait for the initial resume.
     aPane.debuggerWindow.gClient.addOneTimeListener("resumed", function() {
       delete aPane.onConnected;
-      aPane.debuggerWindow.gClient.activeThread.addOneTimeListener("scriptsadded", function() {
+      aPane.debuggerWindow.gClient.activeThread.addOneTimeListener("framesadded", function() {
         type("break add line " + TEST_URI + " " + content.wrappedJSObject.line0);
         is(requisition.getStatus().toString(), "VALID", "break add line is VALID");
         requisition.exec();
 
         type("break list");
         is(requisition.getStatus().toString(), "VALID", "break list is VALID");
         requisition.exec();
 
--- a/browser/locales/en-US/chrome/browser/devtools/debugger.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/debugger.properties
@@ -25,17 +25,17 @@ runningState=Running
 # LOCALIZATION NOTE (localScope): The label that is displayed in the variables
 # pane as a header on the local scope container.
 localScope=Local
 
 # LOCALIZATION NOTE (globalScope): The label that is displayed in the variables
 # pane as a header on the globel scope container.
 globalScope=Global
 
-# LOCALIZATION NOTE (localScope): The label that is displayed in the variables
+# LOCALIZATION NOTE (withScope): The label that is displayed in the variables
 # pane as a header on the container for identifiers in a with block.
 withScope=With block
 
 # LOCALIZATION NOTE (closureScope): The label that is displayed in the variables
 # pane as a header on container for identifiers in a closure scope.
 closureScope=Closure
 
 # LOCALIZATION NOTE (emptyText): The text that is displayed in the stack frames
--- a/toolkit/devtools/debugger/server/dbg-browser-actors.js
+++ b/toolkit/devtools/debugger/server/dbg-browser-actors.js
@@ -62,29 +62,24 @@ function createRootActor(aConnection)
 function BrowserRootActor(aConnection)
 {
   this.conn = aConnection;
   this._tabActors = new WeakMap();
   this._tabActorPool = null;
   this._actorFactories = null;
 
   this.onTabClosed = this.onTabClosed.bind(this);
-  this._onWindowCreated = this.onWindowCreated.bind(this);
   windowMediator.addListener(this);
 }
 
 BrowserRootActor.prototype = {
   /**
    * Return a 'hello' packet as specified by the Remote Debugging Protocol.
    */
   sayHello: function BRA_sayHello() {
-    // Create the tab actor for the selected tab right away so that it gets a
-    // chance to listen to onNewScript notifications.
-    this._preInitTabActor();
-
     return { from: "root",
              applicationType: "browser",
              traits: [] };
   },
 
   /**
    * Disconnects the actor from the browser window.
    */
@@ -118,16 +113,20 @@ BrowserRootActor.prototype = {
     let actorList = [];
 
     // Walk over open browser windows.
     let e = windowMediator.getEnumerator("navigator:browser");
     let selected;
     while (e.hasMoreElements()) {
       let win = e.getNext();
 
+      // Watch the window for tab closes so we can invalidate
+      // actors as needed.
+      this.watchWindow(win);
+
       // List the tabs in this browser.
       let selectedBrowser = win.getBrowser().selectedBrowser;
       let browsers = win.getBrowser().browsers;
       for each (let browser in browsers) {
         if (browser == selectedBrowser) {
           selected = actorList.length;
         }
         let actor = this._tabActors.get(browser);
@@ -179,70 +178,25 @@ BrowserRootActor.prototype = {
    * When a tab is closed, exit its tab actor.  The actor
    * will be dropped at the next listTabs request.
    */
   onTabClosed: function BRA_onTabClosed(aEvent) {
     this.exitTabActor(aEvent.target.linkedBrowser);
   },
 
   /**
-   * Handle location changes, by preinitializing a tab actor.
-   */
-  onWindowCreated: function BRA_onWindowCreated(evt) {
-    if (evt.target === this.browser.contentDocument) {
-      this._preInitTabActor();
-    }
-  },
-
-  /**
    * Exit the tab actor of the specified tab.
    */
   exitTabActor: function BRA_exitTabActor(aWindow) {
-    this.browser.removeEventListener("DOMWindowCreated", this._onWindowCreated, true);
     let actor = this._tabActors.get(aWindow);
     if (actor) {
       actor.exit();
     }
   },
 
-  /**
-   * Create the tab actor in the selected tab right away so that it gets a
-   * chance to listen to onNewScript notifications.
-   */
-  _preInitTabActor: function BRA__preInitTabActor() {
-    let actorPool = new ActorPool(this.conn);
-
-    // Walk over open browser windows.
-    let e = windowMediator.getEnumerator("navigator:browser");
-    while (e.hasMoreElements()) {
-      let win = e.getNext();
-
-      // Watch the window for tab closes so we can invalidate
-      // actors as needed.
-      this.watchWindow(win);
-
-      this.browser = win.getBrowser().selectedBrowser;
-      let actor = this._tabActors.get(this.browser);
-      if (actor) {
-        actor._detach();
-      }
-      actor = new BrowserTabActor(this.conn, this.browser);
-      actor.parentID = this.actorID;
-      this._tabActors.set(this.browser, actor);
-
-      actorPool.addActor(actor);
-    }
-
-    this._tabActorPool = actorPool;
-    this.conn.addActorPool(this._tabActorPool);
-
-    // Watch for globals being created in this tab.
-    this.browser.addEventListener("DOMWindowCreated", this._onWindowCreated, true);
-  },
-
   // nsIWindowMediatorListener
   onWindowTitleChange: function BRA_onWindowTitleChange(aWindow, aTitle) { },
   onOpenWindow: function BRA_onOpenWindow(aWindow) { },
   onCloseWindow: function BRA_onCloseWindow(aWindow) {
     if (aWindow.getBrowser) {
       this.unwatchWindow(aWindow);
     }
   }
@@ -265,17 +219,16 @@ BrowserRootActor.prototype.requestTypes 
  *        The browser instance that contains this tab.
  */
 function BrowserTabActor(aConnection, aBrowser)
 {
   this.conn = aConnection;
   this._browser = aBrowser;
 
   this._onWindowCreated = this.onWindowCreated.bind(this);
-  this._attach();
 }
 
 // XXX (bug 710213): BrowserTabActor attach/detach/exit/disconnect is a
 // *complete* mess, needs to be rethought asap.
 
 BrowserTabActor.prototype = {
   get browser() { return this._browser; },
 
@@ -336,17 +289,17 @@ BrowserTabActor.prototype = {
     dbg_assert(!this._tabPool, "Shouldn't have a tab pool if we weren't attached.");
     this._tabPool = new ActorPool(this.conn);
     this.conn.addActorPool(this._tabPool);
 
     // ... and a pool for context-lifetime actors.
     this._pushContext();
 
     // Watch for globals being created in this tab.
-    this.browser.addEventListener("DOMWindowCreated", this._onWindowCreated, false);
+    this.browser.addEventListener("DOMWindowCreated", this._onWindowCreated, true);
 
     this._attached = true;
   },
 
   /**
    * Creates a thread actor and a pool for context-lifetime actors. It then sets
    * up the content window for debugging.
    */
@@ -388,17 +341,17 @@ BrowserTabActor.prototype = {
   /**
    * Does the actual work of detaching from a tab.
    */
   _detach: function BTA_detach() {
     if (!this.attached) {
       return;
     }
 
-    this.browser.removeEventListener("DOMWindowCreated", this._onWindowCreated, false);
+    this.browser.removeEventListener("DOMWindowCreated", this._onWindowCreated, true);
 
     this._popContext();
 
     // Shut down actors that belong to this tab's pool.
     this.conn.removeActorPool(this._tabPool);
     this._tabPool = null;
 
     this._attached = false;
--- a/toolkit/devtools/debugger/server/dbg-script-actors.js
+++ b/toolkit/devtools/debugger/server/dbg-script-actors.js
@@ -94,29 +94,22 @@ ThreadActor.prototype = {
     // 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();
     }
 
-    // TODO: Remove this horrible hack when bug 723563 is fixed.
-    // Make sure that a chrome window is not added as a debuggee when opening
-    // the debugger in an empty tab or during tests.
-    if (aGlobal.location &&
-        (aGlobal.location.protocol == "about:" ||
-         aGlobal.location.protocol == "chrome:")) {
-      return;
-    }
-
     this.dbg.addDebuggee(aGlobal);
     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);
@@ -498,16 +491,21 @@ ThreadActor.prototype = {
     // Location not found in children, this is the innermost containing script.
     return aScript;
   },
 
   /**
    * Handle a protocol request to return the list of loaded scripts.
    */
   onScripts: function TA_onScripts(aRequest) {
+    // Get the script list from the debugger.
+    for (let s of this.dbg.findScripts()) {
+      this._addScript(s);
+    }
+    // Build the cache.
     let scripts = [];
     for (let url in this._scripts) {
       for (let i = 0; i < this._scripts[url].length; i++) {
         if (!this._scripts[url][i]) {
           continue;
         }
         let script = {
           url: url,
@@ -854,38 +852,44 @@ ThreadActor.prototype = {
    * @param aFrame Debugger.Frame
    *        The stack frame that contained the debugger statement.
    */
   onDebuggerStatement: function TA_onDebuggerStatement(aFrame) {
     return this._pauseAndRespond(aFrame, { type: "debuggerStatement" });
   },
 
   /**
-   * A function that the engine calls when a new script has been loaded into a
-   * debuggee compartment. If the new code is part of a function, aFunction is
-   * a Debugger.Object reference to the function object. (Not all code is part
-   * of a function; for example, the code appearing in a <script> tag that is
-   * outside of any functions defined in that tag would be passed to
-   * onNewScript without an accompanying function argument.)
+   * A function that the engine calls when a new script has been loaded into the
+   * scope of the specified debuggee global.
    *
    * @param aScript Debugger.Script
    *        The source script that has been loaded into a debuggee compartment.
-   * @param aFunction Debugger.Object
-   *        The function object that the ew code is part of.
+   * @param aGlobal Debugger.Object
+   *        A Debugger.Object instance whose referent is the global object.
    */
-  onNewScript: function TA_onNewScript(aScript, aFunction) {
+  onNewScript: function TA_onNewScript(aScript, aGlobal) {
+    this._addScript(aScript);
+    // Notify the client.
+    this.conn.send({ from: this.actorID, type: "newScript",
+                     url: aScript.url, startLine: aScript.startLine });
+  },
+
+  /**
+   * Add the provided script to the server cache.
+   *
+   * @param aScript Debugger.Script
+   *        The source script that will be stored.
+   */
+  _addScript: function TA__addScript(aScript) {
     // Use a sparse array for storing the scripts for each URL in order to
     // optimize retrieval.
     if (!this._scripts[aScript.url]) {
       this._scripts[aScript.url] = [];
     }
     this._scripts[aScript.url][aScript.startLine] = aScript;
-    // Notify the client.
-    this.conn.send({ from: this.actorID, type: "newScript",
-                     url: aScript.url, startLine: aScript.startLine });
   }
 
 };
 
 ThreadActor.prototype.requestTypes = {
   "attach": ThreadActor.prototype.onAttach,
   "detach": ThreadActor.prototype.onDetach,
   "resume": ThreadActor.prototype.onResume,