Bug 883649 - Console freezes when accessing a DeadObject's property; r=past
authorMihai Sucan <mihai.sucan@gmail.com>
Fri, 21 Jun 2013 20:40:00 +0300
changeset 147657 8174f98e2cdcb3ff13e8a9d7228113e0d6c48076
parent 147656 f59dc2b929503528ff8bd9dac20616cea86d89f7
child 147658 e557dc25838819ef3c3675e1a081a02c105ece8d
push id2697
push userbbajaj@mozilla.com
push dateMon, 05 Aug 2013 18:49:53 +0000
treeherdermozilla-beta@dfec938c7b63 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspast
bugs883649
milestone24.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 883649 - Console freezes when accessing a DeadObject's property; r=past
browser/devtools/webconsole/test/Makefile.in
browser/devtools/webconsole/test/browser_console_dead_objects.js
toolkit/devtools/server/actors/script.js
toolkit/devtools/webconsole/WebConsoleUtils.jsm
--- a/browser/devtools/webconsole/test/Makefile.in
+++ b/browser/devtools/webconsole/test/Makefile.in
@@ -134,16 +134,17 @@ MOCHITEST_BROWSER_FILES = \
 	browser_console_private_browsing.js \
 	browser_console_nsiconsolemessage.js \
 	browser_webconsole_bug_817834_add_edited_input_to_history.js \
 	browser_console_addonsdk_loader_exception.js \
 	browser_console_error_source_click.js \
 	browser_console_clear_on_reload.js \
 	browser_console_keyboard_accessibility.js \
 	browser_console_filters.js \
+	browser_console_dead_objects.js \
 	head.js \
 	$(NULL)
 
 ifeq ($(OS_ARCH), Darwin)
 MOCHITEST_BROWSER_FILES += \
 	browser_webconsole_bug_804845_ctrl_key_nav.js \
         $(NULL)
 endif
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_console_dead_objects.js
@@ -0,0 +1,79 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Check that Dead Objects do not break the Web/Browser Consoles. See bug 883649.
+
+const TEST_URI = "data:text/html;charset=utf8,<p>dead objects!";
+
+function test()
+{
+  let hud = null;
+
+  addTab(TEST_URI);
+  browser.addEventListener("load", function onLoad() {
+    browser.removeEventListener("load", onLoad, true);
+    info("open the browser console");
+    HUDConsoleUI.toggleBrowserConsole().then(onBrowserConsoleOpen);
+  }, true);
+
+  function onBrowserConsoleOpen(aHud)
+  {
+    hud = aHud;
+    ok(hud, "browser console opened");
+
+    hud.jsterm.clearOutput();
+    hud.jsterm.execute("foobarzTezt = content.document", onAddVariable);
+  }
+
+  function onAddVariable()
+  {
+    gBrowser.removeCurrentTab();
+
+    hud.jsterm.execute("foobarzTezt", onReadVariable);
+  }
+
+  function onReadVariable()
+  {
+    isnot(hud.outputNode.textContent.indexOf("[object DeadObject]"), -1,
+          "dead object found");
+
+    hud.jsterm.setInputValue("foobarzTezt");
+
+    for (let c of ".hello") {
+      EventUtils.synthesizeKey(c, {}, hud.iframeWindow);
+    }
+
+    hud.jsterm.execute(null, onReadProperty);
+  }
+
+  function onReadProperty()
+  {
+    isnot(hud.outputNode.textContent.indexOf("can't access dead object"), -1,
+          "'cannot access dead object' message found");
+
+    // Click the second execute output.
+    let clickable = hud.outputNode.querySelectorAll(".webconsole-msg-output")[1]
+                    .querySelector(".hud-clickable");
+    ok(clickable, "clickable object found");
+    isnot(clickable.textContent.indexOf("[object DeadObject]"), -1,
+          "message text check");
+
+    hud.jsterm.once("variablesview-fetched", onFetched);
+    EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow);
+  }
+
+  function onFetched()
+  {
+    hud.jsterm.execute("delete window.foobarzTezt; 2013-26", onCalcResult);
+  }
+
+  function onCalcResult()
+  {
+    isnot(hud.outputNode.textContent.indexOf("1987"), -1, "result message found");
+
+    // executeSoon() is needed to get out of the execute() event loop.
+    executeSoon(finishTest);
+  }
+}
--- a/toolkit/devtools/server/actors/script.js
+++ b/toolkit/devtools/server/actors/script.js
@@ -1611,17 +1611,28 @@ ObjectActor.prototype = {
    * Handle a protocol request to provide the prototype and own properties of
    * the object.
    *
    * @param aRequest object
    *        The protocol request object.
    */
   onPrototypeAndProperties: function OA_onPrototypeAndProperties(aRequest) {
     let ownProperties = Object.create(null);
-    for (let name of this.obj.getOwnPropertyNames()) {
+    let names;
+    try {
+      names = this.obj.getOwnPropertyNames();
+    } catch (ex) {
+      // The above can throw if this.obj points to a dead object.
+      // TODO: we should use Cu.isDeadWrapper() - see bug 885800.
+      return { from: this.actorID,
+               prototype: this.threadActor.createValueGrip(null),
+               ownProperties: ownProperties,
+               safeGetterValues: Object.create(null) };
+    }
+    for (let name of names) {
       ownProperties[name] = this._propertyDescriptor(name);
     }
     return { from: this.actorID,
              prototype: this.threadActor.createValueGrip(this.obj.proto),
              ownProperties: ownProperties,
              safeGetterValues: this._findSafeGetterValues(ownProperties) };
   },
 
--- a/toolkit/devtools/webconsole/WebConsoleUtils.jsm
+++ b/toolkit/devtools/webconsole/WebConsoleUtils.jsm
@@ -790,18 +790,25 @@ function JSPropertyProvider(aScope, aInp
     }
 
     // If obj is undefined or null (which is what "== null" does),
     // then there is no chance to run completion on it. Exit here.
     if (obj == null) {
       return null;
     }
 
-    // Skip Iterators and Generators.
-    if (WCU.isIteratorOrGenerator(obj)) {
+    try {
+      // Skip Iterators and Generators.
+      if (WCU.isIteratorOrGenerator(obj)) {
+        return null;
+      }
+    }
+    catch (ex) {
+      // The above can throw if |obj| is a dead object.
+      // TODO: we should use Cu.isDeadWrapper() - see bug 885800.
       return null;
     }
   }
 
   let matches = Object.keys(getMatchedProps(obj, {matchProp:matchProp}));
 
   return {
     matchProp: matchProp,