Bug 715543 - Fix the debugger after the latest merge from fx-team; r=dcamp
authorPanos Astithas <past@mozilla.com>
Tue, 10 Jan 2012 11:18:10 +0200
changeset 83626 fe6aff0ff43a13845ed2b214c39638f720f698ef
parent 83625 bf0bae1ca857bc2f995b161efd42bf061bbedd58
child 83627 517cbf7e7cd4c050f50f27bd22a9ac6c380b8b6d
push id90
push userpastithas@mozilla.com
push dateTue, 10 Jan 2012 09:18:54 +0000
reviewersdcamp
bugs715543
milestone12.0a1
Bug 715543 - Fix the debugger after the latest merge from fx-team; r=dcamp
browser/devtools/debugger/DebuggerUI.jsm
browser/devtools/debugger/test/browser_dbg_script-switching.js
toolkit/devtools/debugger/server/dbg-script-actors.js
--- a/browser/devtools/debugger/DebuggerUI.jsm
+++ b/browser/devtools/debugger/DebuggerUI.jsm
@@ -259,71 +259,71 @@ DebuggerUI.prototype = {
     let gBrowser = this.aWindow.gBrowser;
 
     let url = aEvent.detail;
     let scheme = Services.io.extractScheme(url);
     switch (scheme) {
       case "file":
       case "chrome":
       case "resource":
-        NetUtil.asyncFetch(url, function onAsyncFetch(stream, status) {
-          if (!Components.isSuccessCode(status)) {
-            let view = this.getDebugger(gBrowser.selectedTab).DebuggerView;
-            Components.utils.reportError(view.getFormatStr("loadingError", [ url, status ]));
-            return;
-          }
-          let source = NetUtil.readInputStreamToString(stream, stream.available());
-          stream.close();
-          this.onSourceLoaded(url, source);
-        }.bind(this));
+        try {
+          NetUtil.asyncFetch(url, function onFetch(aStream, aStatus) {
+            if (!Components.isSuccessCode(aStatus)) {
+              return this.logError(url, aStatus);
+            }
+            let source = NetUtil.readInputStreamToString(aStream, aStream.available());
+            aStream.close();
+            this.onSourceLoaded(url, source);
+          }.bind(this));
+        } catch (ex) {
+          return this.logError(url, ex.name);
+        }
         break;
 
       default:
-        let cacheService = Cc["@mozilla.org/network/cache-service;1"].getService(Ci.nsICacheService);
-        let session = cacheService.createSession("HTTP", Ci.nsICache.STORE_ANYWHERE, true);
-        session.doomEntriesIfExpired = false;
-        session.asyncOpenCacheEntry(url, Ci.nsICache.ACCESS_READ, {
-          onCacheEntryAvailable: function onCacheEntryAvailable(entry, mode, status) {
-            if (!Components.isSuccessCode(status)) {
-              let view = this.getDebugger(gBrowser.selectedTab).DebuggerView;
-              Components.utils.reportError(view.getFormatStr("loadingError", [ url, status ]));
-              return;
+        let channel = Services.io.newChannel(url, null, null);
+        let chunks = [];
+        let streamListener = { // nsIStreamListener inherits nsIRequestObserver
+          onStartRequest: function (aRequest, aContext, aStatusCode) {
+            if (!Components.isSuccessCode(aStatusCode)) {
+              return this.logError(url, aStatusCode);
+            }
+          }.bind(this),
+          onDataAvailable: function (aRequest, aContext, aStream, aOffset, aCount) {
+            chunks.push(NetUtil.readInputStreamToString(aStream, aCount));
+          },
+          onStopRequest: function (aRequest, aContext, aStatusCode) {
+            if (!Components.isSuccessCode(aStatusCode)) {
+              return this.logError(url, aStatusCode);
             }
 
-            let source = "";
-            let stream = entry.openInputStream(0);
-            let head = entry.getMetaDataElement("response-head");
+            this.onSourceLoaded(url, chunks.join(""));
+          }.bind(this)
+        };
 
-            if (/Content-Encoding:\s*gzip/i.test(head)) {
-              let converter = Cc["@mozilla.org/streamconv;1?from=gzip&to=uncompressed"].createInstance(Ci.nsIStreamConverter);
-              converter.asyncConvertData("gzip", "uncompressed", {
-                onDataAvailable: function onDataAvailable(aRequest, aContext, uncompressedStream, aOffset, aCount) {
-                  source += NetUtil.readInputStreamToString(uncompressedStream, aCount);
-                }
-              }, this);
-              while (stream.available()) {
-                converter.onDataAvailable(null, this, stream, 0, stream.available());
-              }
-            } else {
-              // uncompressed data
-              while (stream.available()) {
-                source += NetUtil.readInputStreamToString(stream, stream.available());
-              }
-            }
-
-            stream.close();
-            entry.close();
-            this.onSourceLoaded(url, source);
-          }.bind(this)
-        });
+        channel.loadFlags = channel.LOAD_FROM_CACHE;
+        channel.asyncOpen(streamListener, null);
         break;
     }
   },
 
   /**
+   * Log an error message in the error console when a script fails to load.
+   *
+   * @param string aUrl
+   *        The URL of the source script.
+   * @param string aStatus
+   *        The failure status code.
+   */
+  logError: function DebuggerUI_logError(aUrl, aStatus) {
+    let view = this.getDebugger(gBrowser.selectedTab).DebuggerView;
+    Components.utils.reportError(view.getFormatStr("loadingError", [ aUrl, aStatus ]));
+  },
+
+  /**
    * Called when source has been loaded.
    *
    * @param string aSourceUrl
    *        The URL of the source script.
    * @param string aSourceText
    *        The text of the source script.
    */
   onSourceLoaded: function DebuggerUI_onSourceLoaded(aSourceUrl, aSourceText) {
--- a/browser/devtools/debugger/test/browser_dbg_script-switching.js
+++ b/browser/devtools/debugger/test/browser_dbg_script-switching.js
@@ -3,16 +3,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Make sure that switching the displayed script in the UI works as advertised.
  */
 
 const TAB_URL = "http://example.com/browser/browser/devtools/debugger/" +
                 "test/browser_dbg_script-switching.html";
+Cu.import("resource:///modules/source-editor.jsm");
 
 var gPane = null;
 var gTab = null;
 var gDebuggee = null;
 var gDebugger = null;
 var gScripts = null;
 
 function test()
@@ -25,52 +26,68 @@ function test()
 
     testScriptsDisplay();
   });
 }
 
 function testScriptsDisplay() {
   gPane.activeThread.addOneTimeListener("scriptsadded", 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.");
+      let count = 0;
+      gDebugger.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
+                                        function onScriptLoad() {
+        // Skip the first change event, since we're only interested in the
+        // second.
+        if (count++ < 1) {
+          return;
+        }
+        gDebugger.editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
+                                             onScriptLoad);
+        gScripts = gDebugger.DebuggerView.Scripts._scripts;
 
-      ok(gDebugger.editor.getText().search(/debugger/) != -1,
-        "The correct script was loaded initially.");
+        is(gDebugger.StackFrames.activeThread.state, "paused",
+          "Should only be getting stack frames while paused.");
+
+        is(gScripts.itemCount, 2, "Found the expected number of scripts.");
+
+        ok(gDebugger.editor.getText().search(/debugger/) != -1,
+          "The correct script was loaded initially.");
 
-      gScripts.selectedIndex = 0;
-      gDebugger.SourceScripts.onChange({ target: gScripts });
-      executeSoon(function() {
-        testSwitchPaused();
+        gDebugger.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
+                                          function onChange() {
+          gDebugger.editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
+                                               onChange);
+          testSwitchPaused();
+        });
+        gScripts.selectedIndex = 0;
+        gDebugger.SourceScripts.onChange({ target: gScripts });
       });
     }}, 0);
   });
 
   gDebuggee.firstCall();
 }
 
 function testSwitchPaused()
 {
   ok(gDebugger.editor.getText().search(/debugger/) == -1,
     "The second script is no longer displayed.");
 
   ok(gDebugger.editor.getText().search(/firstCall/) != -1,
     "The first script is displayed.");
 
   gDebugger.StackFrames.activeThread.resume(function() {
+    gDebugger.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
+                                      function onSecondChange() {
+      gDebugger.editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
+                                           onSecondChange);
+      testSwitchRunning();
+    });
     gScripts.selectedIndex = 1;
     gDebugger.SourceScripts.onChange({ target: gScripts });
-    executeSoon(function() {
-      testSwitchRunning();
-    });
   });
 }
 
 function testSwitchRunning()
 {
   ok(gDebugger.editor.getText().search(/debugger/) != -1,
     "The second script is displayed again.");
 
--- a/toolkit/devtools/debugger/server/dbg-script-actors.js
+++ b/toolkit/devtools/debugger/server/dbg-script-actors.js
@@ -978,16 +978,22 @@ function EnvironmentActor(aObject, aThre
   this.obj = aObject;
   this.threadActor = aThreadActor;
 }
 
 EnvironmentActor.prototype = {
   actorPrefix: "environment",
 
   grip: function EA_grip() {
+    // Debugger.Frame might be dead by the time we get here, which will cause
+    // accessing its properties to throw.
+    if (!this.obj.live) {
+      return undefined;
+    }
+
     let parent;
     if (this.obj.environment.parent) {
       parent = this.threadActor.environmentActor(this.obj.environment.parent, this.registeredPool);
     }
     let grip = { actor: this.actorID,
                  parent: parent ? parent.grip() : parent };
 
     if (this.obj.environment.type == "object") {
@@ -1009,16 +1015,22 @@ EnvironmentActor.prototype = {
   },
 
   /**
    * Return the identifier bindings object as required by the remote protocol
    * specification.
    */
   _bindings: function EA_bindings() {
     let bindings = { mutable: {}, immutable: {} };
+
+    // TODO: this will be redundant after bug 692984 is fixed.
+    if (typeof this.obj.environment.getVariableDescriptor != "function") {
+      return bindings;
+    }
+
     for (let name in this.obj.environment.names()) {
       let desc = this.obj.environment.getVariableDescriptor(name);
       // XXX: the spec doesn't say what to do with accessor properties.
       if (desc.writable) {
         grip.bindings.mutable[name] = desc.value;
       } else {
         grip.bindings.immutable[name] = desc.value;
       }