Bug 783499 - Web Console should use the debugger API; r=past
authorMihai Sucan <mihai.sucan@gmail.com>
Sat, 30 Mar 2013 13:31:10 +0200
changeset 128199 f5d6c95a9de90e6b2ea07f3ff1b502e36da4597b
parent 128198 2fdd3d16ed3b0984e940b87c21203e7ca569eb56
child 128200 dfc808a01756195e3ebbc315bf6f87e6cf95e489
push id26193
push userryanvm@gmail.com
push dateTue, 09 Apr 2013 19:29:36 +0000
treeherdermozilla-inbound@9db46ddfb517 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspast
bugs783499
milestone23.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 783499 - Web Console should use the debugger API; r=past
browser/devtools/webconsole/HUDService.jsm
toolkit/devtools/debugger/dbg-client.jsm
toolkit/devtools/debugger/server/dbg-script-actors.js
toolkit/devtools/webconsole/WebConsoleClient.jsm
toolkit/devtools/webconsole/WebConsoleUtils.jsm
toolkit/devtools/webconsole/dbg-webconsole-actors.js
toolkit/devtools/webconsole/test/test_bug819670_getter_throws.html
toolkit/devtools/webconsole/test/test_consoleapi.html
toolkit/devtools/webconsole/test/test_jsterm.html
toolkit/devtools/webconsole/test/test_object_actor.html
--- a/browser/devtools/webconsole/HUDService.jsm
+++ b/browser/devtools/webconsole/HUDService.jsm
@@ -400,16 +400,50 @@ WebConsole.prototype = {
         return;
       }
       panelWin.removeEventListener("Debugger:SourceShown", onSource, false);
       panelWin.DebuggerView.editor.setCaretPosition(aSourceLine - 1);
     }
   },
 
   /**
+   * Retrieve information about the JavaScript debugger's stackframes list. This
+   * is used to allow the Web Console to evaluate code in the selected
+   * stackframe.
+   *
+   * @return object|null
+   *         An object which holds:
+   *         - frames: the active ThreadClient.cachedFrames array.
+   *         - selected: depth/index of the selected stackframe in the debugger
+   *         UI.
+   *         If the debugger is not open or if it's not paused, then |null| is
+   *         returned.
+   */
+  getDebuggerFrames: function WC_getDebuggerFrames()
+  {
+    let toolbox = gDevTools.getToolbox(this.target);
+    if (!toolbox) {
+      return null;
+    }
+    let panel = toolbox.getPanel("jsdebugger");
+    if (!panel) {
+      return null;
+    }
+    let framesController = panel.panelWin.gStackFrames;
+    let thread = framesController.activeThread;
+    if (thread && thread.paused) {
+      return {
+        frames: thread.cachedFrames,
+        selected: framesController.currentFrame,
+      };
+    }
+    return null;
+  },
+
+  /**
    * Destroy the object. Call this method to avoid memory leaks when the Web
    * Console is closed.
    *
    * @return object
    *         A Promise object that is resolved once the Web Console is closed.
    */
   destroy: function WC_destroy()
   {
--- a/toolkit/devtools/debugger/dbg-client.jsm
+++ b/toolkit/devtools/debugger/dbg-client.jsm
@@ -8,17 +8,18 @@
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 this.EXPORTED_SYMBOLS = ["DebuggerTransport",
                          "DebuggerClient",
                          "debuggerSocketConnect",
-                         "LongStringClient"];
+                         "LongStringClient",
+                         "GripClient"];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
 const { defer, resolve, reject } = Promise;
 
 XPCOMUtils.defineLazyServiceGetter(this, "socketTransportService",
@@ -168,17 +169,16 @@ const ThreadStateTypes = {
 /**
  * Set of protocol messages that are sent by the server without a prior request
  * by the client.
  */
 const UnsolicitedNotifications = {
   "consoleAPICall": "consoleAPICall",
   "eventNotification": "eventNotification",
   "fileActivity": "fileActivity",
-  "locationChange": "locationChange",
   "networkEvent": "networkEvent",
   "networkEventUpdate": "networkEventUpdate",
   "newGlobal": "newGlobal",
   "newScript": "newScript",
   "newSource": "newSource",
   "tabDetached": "tabDetached",
   "tabNavigated": "tabNavigated",
   "pageError": "pageError",
@@ -509,17 +509,17 @@ DebuggerClient.prototype = {
           this.notify(aPacket.type, aPacket);
         }
 
         if (onResponse) {
           onResponse(aPacket);
         }
       } catch(ex) {
         dumpn("Error handling response: " + ex + " - stack:\n" + ex.stack);
-        Cu.reportError(ex.message + "\n" + ex.stack);
+        Cu.reportError(ex + "\n" + ex.stack);
       }
 
       this._sendRequests();
     }.bind(this));
   },
 
   /**
    * Called by DebuggerTransport when the underlying stream is closed.
--- a/toolkit/devtools/debugger/server/dbg-script-actors.js
+++ b/toolkit/devtools/debugger/server/dbg-script-actors.js
@@ -895,17 +895,17 @@ ThreadActor.prototype = {
     }
 
     if (aPool.objectActors.has(aValue)) {
       return aPool.objectActors.get(aValue).grip();
     } else if (this.threadLifetimePool.objectActors.has(aValue)) {
       return this.threadLifetimePool.objectActors.get(aValue).grip();
     }
 
-    let actor = new ObjectActor(aValue, this);
+    let actor = new PauseScopedObjectActor(aValue, this);
     aPool.addActor(actor);
     aPool.objectActors.set(aValue, actor);
     return actor.grip();
   },
 
   /**
    * Create a grip for the given debuggee object with a pause lifetime.
    *
@@ -1510,20 +1510,17 @@ SourceActor.prototype.requestTypes = {
  *        The parent thread actor for this object.
  */
 function ObjectActor(aObj, aThreadActor)
 {
   this.obj = aObj;
   this.threadActor = aThreadActor;
 }
 
-ObjectActor.prototype = Object.create(PauseScopedActor.prototype);
-
-update(ObjectActor.prototype, {
-  constructor: ObjectActor,
+ObjectActor.prototype = {
   actorPrefix: "obj",
 
   /**
    * Returns a grip for this actor for returning in a protocol message.
    */
   grip: function OA_grip() {
     let g = { "type": "object",
               "class": this.obj.class,
@@ -1547,47 +1544,47 @@ update(ObjectActor.prototype, {
 
     return g;
   },
 
   /**
    * Releases this actor from the pool.
    */
   release: function OA_release() {
-    this.registeredPool.objectActors.delete(this.obj);
+    if (this.registeredPool.objectActors) {
+      this.registeredPool.objectActors.delete(this.obj);
+    }
     this.registeredPool.removeActor(this);
     this.disconnect();
   },
 
   disconnect: function OA_disconnect() {
     this.threadActor._removeFromProtoChain(this.obj);
   },
 
   /**
    * Handle a protocol request to provide the names of the properties defined on
    * the object and not its prototype.
    *
    * @param aRequest object
    *        The protocol request object.
    */
-  onOwnPropertyNames:
-  PauseScopedActor.withPaused(function OA_onOwnPropertyNames(aRequest) {
+  onOwnPropertyNames: function OA_onOwnPropertyNames(aRequest) {
     return { from: this.actorID,
              ownPropertyNames: this.obj.getOwnPropertyNames() };
-  }),
+  },
 
   /**
    * Handle a protocol request to provide the prototype and own properties of
    * the object.
    *
    * @param aRequest object
    *        The protocol request object.
    */
-  onPrototypeAndProperties:
-  PauseScopedActor.withPaused(function OA_onPrototypeAndProperties(aRequest) {
+  onPrototypeAndProperties: function OA_onPrototypeAndProperties(aRequest) {
     if (this.obj.proto) {
       // Store the object and its prototype to the prototype chain cache, so that
       // we can evaluate native getter methods for WebIDL attributes that are
       // meant to be called on the instace and not on the prototype.
       //
       // TODO: after bug 801084, we could restrict the cache to objects where
       // this.obj.hostAnnotations.isWebIDLObject == true
       let chain = this.threadActor._findProtoChain(this.obj);
@@ -1603,45 +1600,45 @@ update(ObjectActor.prototype, {
 
     let ownProperties = {};
     for (let name of this.obj.getOwnPropertyNames()) {
       ownProperties[name] = this._propertyDescriptor(name);
     }
     return { from: this.actorID,
              prototype: this.threadActor.createValueGrip(this.obj.proto),
              ownProperties: ownProperties };
-  }),
+  },
 
   /**
    * Handle a protocol request to provide the prototype of the object.
    *
    * @param aRequest object
    *        The protocol request object.
    */
-  onPrototype: PauseScopedActor.withPaused(function OA_onPrototype(aRequest) {
+  onPrototype: function OA_onPrototype(aRequest) {
     return { from: this.actorID,
              prototype: this.threadActor.createValueGrip(this.obj.proto) };
-  }),
+  },
 
   /**
    * Handle a protocol request to provide the property descriptor of the
    * object's specified property.
    *
    * @param aRequest object
    *        The protocol request object.
    */
-  onProperty: PauseScopedActor.withPaused(function OA_onProperty(aRequest) {
+  onProperty: function OA_onProperty(aRequest) {
     if (!aRequest.name) {
       return { error: "missingParameter",
                message: "no property name was specified" };
     }
 
     return { from: this.actorID,
              descriptor: this._propertyDescriptor(aRequest.name) };
-  }),
+  },
 
   /**
    * A helper method that creates a property descriptor for the provided object,
    * properly formatted for sending in a protocol response.
    *
    * @param string aName
    *        The property that the descriptor is generated for.
    */
@@ -1719,26 +1716,94 @@ update(ObjectActor.prototype, {
   },
 
   /**
    * Handle a protocol request to provide the source code of a function.
    *
    * @param aRequest object
    *        The protocol request object.
    */
-  onDecompile: PauseScopedActor.withPaused(function OA_onDecompile(aRequest) {
+  onDecompile: function OA_onDecompile(aRequest) {
     if (this.obj.class !== "Function") {
       return { error: "objectNotFunction",
                message: "decompile request is only valid for object grips " +
                         "with a 'Function' class." };
     }
 
     return { from: this.actorID,
              decompiledCode: this.obj.decompile(!!aRequest.pretty) };
-  }),
+  },
+
+  /**
+   * Handle a protocol request to provide the parameters of a function.
+   *
+   * @param aRequest object
+   *        The protocol request object.
+   */
+  onParameterNames: function OA_onParameterNames(aRequest) {
+    if (this.obj.class !== "Function") {
+      return { error: "objectNotFunction",
+               message: "'parameterNames' request is only valid for object " +
+                        "grips with a 'Function' class." };
+    }
+
+    return { parameterNames: this.obj.parameterNames };
+  },
+
+  /**
+   * Handle a protocol request to release a thread-lifetime grip.
+   *
+   * @param aRequest object
+   *        The protocol request object.
+   */
+  onRelease: function OA_onRelease(aRequest) {
+    this.release();
+    return {};
+  },
+};
+
+ObjectActor.prototype.requestTypes = {
+  "parameterNames": ObjectActor.prototype.onParameterNames,
+  "prototypeAndProperties": ObjectActor.prototype.onPrototypeAndProperties,
+  "prototype": ObjectActor.prototype.onPrototype,
+  "property": ObjectActor.prototype.onProperty,
+  "ownPropertyNames": ObjectActor.prototype.onOwnPropertyNames,
+  "decompile": ObjectActor.prototype.onDecompile,
+  "release": ObjectActor.prototype.onRelease,
+};
+
+
+/**
+ * Creates a pause-scoped  actor for the specified object.
+ * @see ObjectActor
+ */
+function PauseScopedObjectActor()
+{
+  ObjectActor.apply(this, arguments);
+}
+
+PauseScopedObjectActor.prototype = Object.create(PauseScopedActor.prototype);
+
+update(PauseScopedObjectActor.prototype, ObjectActor.prototype);
+
+update(PauseScopedObjectActor.prototype, {
+  constructor: PauseScopedObjectActor,
+
+  onOwnPropertyNames:
+    PauseScopedActor.withPaused(ObjectActor.prototype.onOwnPropertyNames),
+
+  onPrototypeAndProperties:
+    PauseScopedActor.withPaused(ObjectActor.prototype.onPrototypeAndProperties),
+
+  onPrototype: PauseScopedActor.withPaused(ObjectActor.prototype.onPrototype),
+  onProperty: PauseScopedActor.withPaused(ObjectActor.prototype.onProperty),
+  onDecompile: PauseScopedActor.withPaused(ObjectActor.prototype.onDecompile),
+
+  onParameterNames:
+    PauseScopedActor.withPaused(ObjectActor.prototype.onParameterNames),
 
   /**
    * Handle a protocol request to provide the lexical scope of a function.
    *
    * @param aRequest object
    *        The protocol request object.
    */
   onScope: PauseScopedActor.withPaused(function OA_onScope(aRequest) {
@@ -1754,32 +1819,16 @@ update(ObjectActor.prototype, {
       return { error: "notDebuggee",
                message: "cannot access the environment of this function." };
     }
 
     return { from: this.actorID, scope: envActor.form() };
   }),
 
   /**
-   * Handle a protocol request to provide the parameters of a function.
-   *
-   * @param aRequest object
-   *        The protocol request object.
-   */
-  onParameterNames: PauseScopedActor.withPaused(function OA_onParameterNames(aRequest) {
-    if (this.obj.class !== "Function") {
-      return { error: "objectNotFunction",
-               message: "'parameterNames' request is only valid for object " +
-                        "grips with a 'Function' class." };
-    }
-
-    return { parameterNames: this.obj.parameterNames };
-  }),
-
-  /**
    * Handle a protocol request to promote a pause-lifetime grip to a
    * thread-lifetime grip.
    *
    * @param aRequest object
    *        The protocol request object.
    */
   onThreadGrip: PauseScopedActor.withPaused(function OA_onThreadGrip(aRequest) {
     this.threadActor.threadObjectGrip(this);
@@ -1798,27 +1847,20 @@ update(ObjectActor.prototype, {
                message: "Only thread-lifetime actors can be released." };
     }
 
     this.release();
     return {};
   }),
 });
 
-ObjectActor.prototype.requestTypes = {
-  "parameterNames": ObjectActor.prototype.onParameterNames,
-  "prototypeAndProperties": ObjectActor.prototype.onPrototypeAndProperties,
-  "prototype": ObjectActor.prototype.onPrototype,
-  "property": ObjectActor.prototype.onProperty,
-  "ownPropertyNames": ObjectActor.prototype.onOwnPropertyNames,
-  "scope": ObjectActor.prototype.onScope,
-  "decompile": ObjectActor.prototype.onDecompile,
-  "threadGrip": ObjectActor.prototype.onThreadGrip,
-  "release": ObjectActor.prototype.onRelease,
-};
+update(PauseScopedObjectActor.prototype.requestTypes, {
+  "scope": PauseScopedObjectActor.prototype.onScope,
+  "threadGrip": PauseScopedObjectActor.prototype.onThreadGrip,
+});
 
 
 /**
  * Creates an actor for the specied "very long" string. "Very long" is specified
  * at the server's discretion.
  *
  * @param aString String
  *        The string.
--- a/toolkit/devtools/webconsole/WebConsoleClient.jsm
+++ b/toolkit/devtools/webconsole/WebConsoleClient.jsm
@@ -76,23 +76,45 @@ WebConsoleClient.prototype = {
 
   /**
    * Evaluate a JavaScript expression.
    *
    * @param string aString
    *        The code you want to evaluate.
    * @param function aOnResponse
    *        The function invoked when the response is received.
+   * @param object [aOptions={}]
+   *        Options for evaluation:
+   *
+   *        - bindObjectActor: an ObjectActor ID. The OA holds a reference to
+   *        a Debugger.Object that wraps a content object. This option allows
+   *        you to bind |_self| to the D.O of the given OA, during string
+   *        evaluation.
+   *
+   *        See: Debugger.Object.evalInGlobalWithBindings() for information
+   *        about bindings.
+   *
+   *        Use case: the variable view needs to update objects and it does so
+   *        by knowing the ObjectActor it inspects and binding |_self| to the
+   *        D.O of the OA. As such, variable view sends strings like these for
+   *        eval:
+   *          _self["prop"] = value;
+   *
+   *        - frameActor: a FrameActor ID. The FA holds a reference to
+   *        a Debugger.Frame. This option allows you to evaluate the string in
+   *        the frame of the given FA.
    */
-  evaluateJS: function WCC_evaluateJS(aString, aOnResponse)
+  evaluateJS: function WCC_evaluateJS(aString, aOnResponse, aOptions = {})
   {
     let packet = {
       to: this._actor,
       type: "evaluateJS",
       text: aString,
+      bindObjectActor: aOptions.bindObjectActor,
+      frameActor: aOptions.frameActor,
     };
     this._client.request(packet, aOnResponse);
   },
 
   /**
    * Autocomplete a JavaScript expression.
    *
    * @param string aString
--- a/toolkit/devtools/webconsole/WebConsoleUtils.jsm
+++ b/toolkit/devtools/webconsole/WebConsoleUtils.jsm
@@ -23,22 +23,27 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 
 XPCOMUtils.defineLazyModuleGetter(this, "NetworkHelper",
                                   "resource://gre/modules/devtools/NetworkHelper.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gActivityDistributor",
                                    "@mozilla.org/network/http-activity-distributor;1",
                                    "nsIHttpActivityDistributor");
 
+// TODO: Bug 842672 - toolkit/ imports modules from browser/.
+// Note that these are only used in JSTermHelpers, see $0 and pprint().
 XPCOMUtils.defineLazyModuleGetter(this, "gDevTools",
                                   "resource:///modules/devtools/gDevTools.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "TargetFactory",
                                   "resource:///modules/devtools/Target.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "VariablesView",
+                                  "resource:///modules/devtools/VariablesView.jsm");
+
 this.EXPORTED_SYMBOLS = ["WebConsoleUtils", "JSPropertyProvider", "JSTermHelpers",
                          "PageErrorListener", "ConsoleAPIListener",
                          "NetworkResponseListener", "NetworkMonitor",
                          "ConsoleProgressListener"];
 
 // Match the function name from the result of toString() or toSource().
 //
 // Examples:
@@ -781,17 +786,17 @@ this.WebConsoleUtils = {
    * Get the object class name. For example, the |window| object has the Window
    * class name (based on [object Window]).
    *
    * @param object aObject
    *        The object you want to get the class name for.
    * @return string
    *         The object class name.
    */
-  getObjectClassName: function WCF_getObjectClassName(aObject)
+  getObjectClassName: function WCU_getObjectClassName(aObject)
   {
     if (aObject === null) {
       return "null";
     }
     if (aObject === undefined) {
       return "undefined";
     }
 
@@ -854,16 +859,29 @@ this.WebConsoleUtils = {
 
     if (val.displayString && typeof val.displayString == "object" &&
         val.displayString.type == "longString") {
       return val.displayString.initial;
     }
 
     return val.displayString || val.type;
   },
+
+  /**
+   * Check if the given value is a grip with an actor.
+   *
+   * @param mixed aGrip
+   *        Value you want to check if it is a grip with an actor.
+   * @return boolean
+   *         True if the given value is a grip with an actor.
+   */
+  isActorGrip: function WCU_isActorGrip(aGrip)
+  {
+    return aGrip && typeof(aGrip) == "object" && aGrip.actor;
+  },
 };
 
 //////////////////////////////////////////////////////////////////////////
 // Localization
 //////////////////////////////////////////////////////////////////////////
 
 WebConsoleUtils.l10n = function WCU_l10n(aBundleURI)
 {
@@ -1537,60 +1555,53 @@ this.JSTermHelpers = function JSTermHelp
    * @param string aXPath
    *        xPath search query to execute.
    * @param [optional] nsIDOMNode aContext
    *        Context to run the xPath query on. Uses window.document if not set.
    * @return array of nsIDOMNode
    */
   aOwner.sandbox.$x = function JSTH_$x(aXPath, aContext)
   {
-    let nodes = [];
+    let nodes = new aOwner.window.wrappedJSObject.Array();
     let doc = aOwner.window.document;
     let aContext = aContext || doc;
 
-    try {
-      let results = doc.evaluate(aXPath, aContext, null,
-                                 Ci.nsIDOMXPathResult.ANY_TYPE, null);
-      let node;
-      while (node = results.iterateNext()) {
-        nodes.push(node);
-      }
-    }
-    catch (ex) {
-      aOwner.window.console.error(ex.message);
+    let results = doc.evaluate(aXPath, aContext, null,
+                               Ci.nsIDOMXPathResult.ANY_TYPE, null);
+    let node;
+    while (node = results.iterateNext()) {
+      nodes.push(node);
     }
 
     return nodes;
   };
 
   /**
    * Returns the currently selected object in the highlighter.
    *
    * TODO: this implementation crosses the client/server boundaries! This is not
    * usable within a remote browser. To implement this feature correctly we need
    * support for remote inspection capabilities within the Inspector as well.
    * See bug 787975.
    *
    * @return nsIDOMElement|null
    *         The DOM element currently selected in the highlighter.
    */
-  Object.defineProperty(aOwner.sandbox, "$0", {
+   Object.defineProperty(aOwner.sandbox, "$0", {
     get: function() {
-      try {
-        let window = aOwner.chromeWindow();
-        let target = TargetFactory.forTab(window.gBrowser.selectedTab);
-        let toolbox = gDevTools.getToolbox(target);
+      let window = aOwner.chromeWindow();
+      if (!window) {
+        return null;
+      }
+      let target = TargetFactory.forTab(window.gBrowser.selectedTab);
+      let toolbox = gDevTools.getToolbox(target);
+      let panel = toolbox ? toolbox.getPanel("inspector") : null;
+      let node = panel ? panel.selection.node : null;
 
-        return toolbox == null ?
-            undefined :
-            toolbox.getPanel("inspector").selection.node;
-      }
-      catch (ex) {
-        aOwner.window.console.error(ex.message);
-      }
+      return node ? aOwner.makeDebuggeeValue(node) : null;
     },
     enumerable: true,
     configurable: false
   });
 
   /**
    * Clears the output of the JSTerm.
    */
@@ -1605,38 +1616,33 @@ this.JSTermHelpers = function JSTermHelp
    * Returns the result of Object.keys(aObject).
    *
    * @param object aObject
    *        Object to return the property names from.
    * @return array of strings
    */
   aOwner.sandbox.keys = function JSTH_keys(aObject)
   {
-    return Object.keys(WebConsoleUtils.unwrap(aObject));
+    return aOwner.window.wrappedJSObject.Object.keys(WebConsoleUtils.unwrap(aObject));
   };
 
   /**
    * Returns the values of all properties on aObject.
    *
    * @param object aObject
    *        Object to display the values from.
    * @return array of string
    */
   aOwner.sandbox.values = function JSTH_values(aObject)
   {
-    let arrValues = [];
+    let arrValues = new aOwner.window.wrappedJSObject.Array();
     let obj = WebConsoleUtils.unwrap(aObject);
 
-    try {
-      for (let prop in obj) {
-        arrValues.push(obj[prop]);
-      }
-    }
-    catch (ex) {
-      aOwner.window.console.error(ex.message);
+    for (let prop in obj) {
+      arrValues.push(obj[prop]);
     }
 
     return arrValues;
   };
 
   /**
    * Opens a help window in MDN.
    */
@@ -1648,25 +1654,22 @@ this.JSTermHelpers = function JSTermHelp
   /**
    * Inspects the passed aObject. This is done by opening the PropertyPanel.
    *
    * @param object aObject
    *        Object to inspect.
    */
   aOwner.sandbox.inspect = function JSTH_inspect(aObject)
   {
-    let obj = WebConsoleUtils.unwrap(aObject);
-    if (!WebConsoleUtils.isObjectInspectable(obj)) {
-      return aObject;
-    }
-
+    let dbgObj = aOwner.makeDebuggeeValue(aObject);
+    let grip = aOwner.createValueGrip(dbgObj);
     aOwner.helperResult = {
       type: "inspectObject",
       input: aOwner.evalInput,
-      object: aOwner.createValueGrip(obj),
+      object: grip,
     };
   };
 
   /**
    * Prints aObject to the output.
    *
    * @param object aObject
    *        Object to print to the output.
@@ -1685,23 +1688,34 @@ this.JSTermHelpers = function JSTermHelp
 
     aOwner.helperResult = { rawOutput: true };
 
     if (typeof aObject == "function") {
       return aObject + "\n";
     }
 
     let output = [];
-    let getObjectGrip = WebConsoleUtils.getObjectGrip.bind(WebConsoleUtils);
+
     let obj = WebConsoleUtils.unwrap(aObject);
-    let props = WebConsoleUtils.inspectObject(obj, getObjectGrip);
-    props.forEach(function(aProp) {
-      output.push(aProp.name + ": " +
-                  WebConsoleUtils.getPropertyPanelValue(aProp));
-    });
+    for (let name in obj) {
+      let desc = WebConsoleUtils.getPropertyDescriptor(obj, name) || {};
+      if (desc.get || desc.set) {
+        // TODO: Bug 842672 - toolkit/ imports modules from browser/.
+        let getGrip = VariablesView.getGrip(desc.get);
+        let setGrip = VariablesView.getGrip(desc.set);
+        let getString = VariablesView.getString(getGrip);
+        let setString = VariablesView.getString(setGrip);
+        output.push(name + ":", "  get: " + getString, "  set: " + setString);
+      }
+      else {
+        let valueGrip = VariablesView.getGrip(obj[name]);
+        let valueString = VariablesView.getString(valueGrip);
+        output.push(name + ": " + valueString);
+      }
+    }
 
     return "  " + output.join("\n  ");
   };
 
   /**
    * Print a string to the output, as-is.
    *
    * @param string aString
--- a/toolkit/devtools/webconsole/dbg-webconsole-actors.js
+++ b/toolkit/devtools/webconsole/dbg-webconsole-actors.js
@@ -66,21 +66,33 @@ function WebConsoleActor(aConnection, aP
     this._window = Services.wm.getMostRecentWindow("navigator:browser");
     this._isGlobalActor = true;
   }
 
   this._actorPool = new ActorPool(this.conn);
   this.conn.addActorPool(this._actorPool);
 
   this._prefs = {};
+
+  this.dbg = new Debugger();
+  this._createGlobal();
+
+  this._protoChains = new Map();
 }
 
 WebConsoleActor.prototype =
 {
   /**
+   * Debugger instance.
+   *
+   * @see jsdebugger.jsm
+   */
+  dbg: null,
+
+  /**
    * Tells if this Web Console actor is a global actor or not.
    * @private
    * @type boolean
    */
   _isGlobalActor: false,
 
   /**
    * Actor pool for all of the actors we send to the client.
@@ -93,28 +105,49 @@ WebConsoleActor.prototype =
   /**
    * Web Console-related preferences.
    * @private
    * @type object
    */
   _prefs: null,
 
   /**
-   * Tells the current inner window associated to the sandbox. When the page
-   * is navigated, we recreate the sandbox.
+   * Tells the current inner window of the window of |this._dbgWindow|. When the
+   * page is navigated, we recreate the debugger object.
+   * @private
+   * @type object
+   */
+  _globalWindowId: 0,
+
+  /**
+   * The Debugger.Object that wraps the content window.
    * @private
    * @type object
    */
-  _sandboxWindowId: 0,
+  _dbgWindow: null,
 
   /**
-   * The JavaScript Sandbox where code is evaluated.
+   * Object that holds the API we give to the JSTermHelpers constructor. This is
+   * where the JSTerm helper functions are added.
+   *
+   * @see this._getJSTermHelpers()
+   * @private
    * @type object
    */
-  sandbox: null,
+  _jstermHelpers: null,
+
+  /**
+   * A cache of prototype chains for objects that have received a
+   * prototypeAndProperties request.
+   *
+   * @private
+   * @type Map
+   * @see dbg-script-actors.js, ThreadActor._protoChains
+   */
+  _protoChains: null,
 
   /**
    * The debugger server connection instance.
    * @type object
    */
   conn: null,
 
   /**
@@ -157,16 +190,21 @@ WebConsoleActor.prototype =
 
   grip: function WCA_grip()
   {
     return { actor: this.actorID };
   },
 
   hasNativeConsoleAPI: BrowserTabActor.prototype.hasNativeConsoleAPI,
 
+  _createValueGrip: ThreadActor.prototype.createValueGrip,
+  _stringIsLong: ThreadActor.prototype._stringIsLong,
+  _findProtoChain: ThreadActor.prototype._findProtoChain,
+  _removeFromProtoChain: ThreadActor.prototype._removeFromProtoChain,
+
   /**
    * Destroy the current WebConsoleActor instance.
    */
   disconnect: function WCA_disconnect()
   {
     if (this.pageErrorListener) {
       this.pageErrorListener.destroy();
       this.pageErrorListener = null;
@@ -180,74 +218,80 @@ WebConsoleActor.prototype =
       this.networkMonitor = null;
     }
     if (this.consoleProgressListener) {
       this.consoleProgressListener.destroy();
       this.consoleProgressListener = null;
     }
     this.conn.removeActorPool(this._actorPool);
     this._actorPool = null;
-    this.sandbox = null;
-    this._sandboxWindowId = 0;
+    this._protoChains.clear();
+    this.dbg.enabled = false;
+    this.dbg = null;
+    this._dbgWindow = null;
+    this._globalWindowId = 0;
     this.conn = this._window = null;
   },
 
   /**
-   * Create a grip for the given value. If the value is an object,
-   * a WebConsoleObjectActor will be created.
+   * Create a grip for the given value.
    *
    * @param mixed aValue
    * @return object
    */
   createValueGrip: function WCA_createValueGrip(aValue)
   {
-    return WebConsoleUtils.createValueGrip(aValue,
-                                           this.createObjectActor.bind(this));
+    return this._createValueGrip(aValue, this._actorPool);
+  },
+
+  /**
+   * Make a debuggee value for the given value.
+   *
+   * @param mixed aValue
+   *        The value you want to get a debuggee value for.
+   * @return object
+   *         Debuggee value for |aValue|.
+   */
+  makeDebuggeeValue: function WCA_makeDebuggeeValue(aValue)
+  {
+    return this._dbgWindow.makeDebuggeeValue(aValue);
   },
 
   /**
    * Create a grip for the given object.
    *
    * @param object aObject
    *        The object you want.
+   * @param object aPool
+   *        An ActorPool where the new actor instance is added.
    * @param object
    *        The object grip.
    */
-  createObjectActor: function WCA_createObjectActor(aObject)
+  objectGrip: function WCA_objectGrip(aObject, aPool)
   {
-    if (typeof aObject == "string") {
-      return this.createStringGrip(aObject);
-    }
-
-    // We need to unwrap the object, otherwise we cannot access the properties
-    // and methods added by the content scripts.
-    let obj = WebConsoleUtils.unwrap(aObject);
-    let actor = new WebConsoleObjectActor(obj, this);
-    this._actorPool.addActor(actor);
+    let actor = new ObjectActor(aObject, this);
+    aPool.addActor(actor);
     return actor.grip();
   },
 
   /**
-   * Create a grip for the given string. If the given string is a long string,
-   * then a LongStringActor grip will be used.
+   * Create a grip for the given string.
    *
    * @param string aString
    *        The string you want to create the grip for.
-   * @return string|object
-   *         The same string, as is, or a LongStringActor object that wraps the
-   *         given string.
+   * @param object aPool
+   *        An ActorPool where the new actor instance is added.
+   * @return object
+   *         A LongStringActor object that wraps the given string.
    */
-  createStringGrip: function WCA_createStringGrip(aString)
+  longStringGrip: function WCA_longStringGrip(aString, aPool)
   {
-    if (aString.length >= DebuggerServer.LONG_STRING_LENGTH) {
-      let actor = new LongStringActor(aString, this);
-      this._actorPool.addActor(actor);
-      return actor.grip();
-    }
-    return aString;
+    let actor = new LongStringActor(aString, this);
+    aPool.addActor(actor);
+    return actor.grip();
   },
 
   /**
    * Get an object actor by its ID.
    *
    * @param string aActorID
    * @return object
    */
@@ -447,54 +491,68 @@ WebConsoleActor.prototype =
    * @param object aRequest
    *        The JSON request object received from the Web Console client.
    * @return object
    *         The evaluation response packet.
    */
   onEvaluateJS: function WCA_onEvaluateJS(aRequest)
   {
     let input = aRequest.text;
-    let result, error = null;
-    let timestamp;
+    let timestamp = Date.now();
+
+    let evalOptions = {
+      bindObjectActor: aRequest.bindObjectActor,
+      frameActor: aRequest.frameActor,
+    };
+    let evalInfo = this.evalWithDebugger(input, evalOptions);
+    let evalResult = evalInfo.result;
+    let helperResult = this._jstermHelpers.helperResult;
+    delete this._jstermHelpers.helperResult;
 
-    this.helperResult = null;
-    this.evalInput = input;
-    try {
-      timestamp = Date.now();
-      result = this.evalInSandbox(input);
+    let result, error, errorMessage;
+    if (evalResult) {
+      if ("return" in evalResult) {
+        result = evalResult.return;
+      }
+      else if ("yield" in evalResult) {
+        result = evalResult.yield;
+      }
+      else if ("throw" in evalResult) {
+        error = evalResult.throw;
+        let errorToString = evalInfo.window
+                            .evalInGlobalWithBindings("ex + ''", {ex: error});
+        if (errorToString && typeof errorToString.return == "string") {
+          errorMessage = errorToString.return;
+        }
+      }
     }
-    catch (ex) {
-      error = ex;
-    }
-
-    let helperResult = this.helperResult;
-    delete this.helperResult;
-    delete this.evalInput;
 
     return {
       from: this.actorID,
       input: input,
       result: this.createValueGrip(result),
       timestamp: timestamp,
-      error: error,
-      errorMessage: error ? String(error) : null,
+      exception: error ? this.createValueGrip(error) : null,
+      exceptionMessage: errorMessage,
       helperResult: helperResult,
     };
   },
 
   /**
    * The Autocomplete request handler.
    *
    * @param object aRequest
    *        The request message - what input to autocomplete.
    * @return object
    *         The response message - matched properties.
    */
   onAutocomplete: function WCA_onAutocomplete(aRequest)
   {
+    // TODO: Bug 842682 - use the debugger API for autocomplete in the Web
+    // Console, and provide suggestions from the selected debugger stack frame.
     let result = JSPropertyProvider(this.window, aRequest.text) || {};
     return {
       from: this.actorID,
       matches: result.matches || [],
       matchProp: result.matchProp,
     };
   },
 
@@ -524,78 +582,211 @@ WebConsoleActor.prototype =
     return { updated: Object.keys(aRequest.preferences) };
   },
 
   //////////////////
   // End of request handlers.
   //////////////////
 
   /**
-   * Create the JavaScript sandbox where user input is evaluated.
+   * Create the Debugger.Object for the current window.
    * @private
    */
-  _createSandbox: function WCA__createSandbox()
+  _createGlobal: function WCA__createGlobal()
   {
-    this._sandboxWindowId = WebConsoleUtils.getInnerWindowId(this.window);
-    this.sandbox = new Cu.Sandbox(this.window, {
-      sandboxPrototype: this.window,
-      wantXrays: false,
-    });
+    let windowId = WebConsoleUtils.getInnerWindowId(this.window);
+    if (this._globalWindowId == windowId) {
+      return;
+    }
+
+    this._globalWindowId = windowId;
+
+    this._dbgWindow = this.dbg.addDebuggee(this.window);
+    this.dbg.removeDebuggee(this.window);
+
+    // Update the JSTerm helpers.
+    this._jstermHelpers = this._getJSTermHelpers(this._dbgWindow);
+  },
 
-    this.sandbox.console = this.window.console;
+  /**
+   * Create an object with the API we expose to the JSTermHelpers constructor.
+   * This object inherits properties and methods from the Web Console actor.
+   *
+   * @private
+   * @param object aDebuggerObject
+   *        A Debugger.Object that wraps a content global. This is used for the
+   *        JSTerm helpers.
+   * @return object
+   */
+  _getJSTermHelpers: function WCA__getJSTermHelpers(aDebuggerObject)
+  {
+    let helpers = Object.create(this);
+    helpers.sandbox = Object.create(null);
+    helpers._dbgWindow = aDebuggerObject;
+    JSTermHelpers(helpers);
 
-    JSTermHelpers(this);
+    // Make sure the helpers can be used during eval.
+    for (let name in helpers.sandbox) {
+      let desc = Object.getOwnPropertyDescriptor(helpers.sandbox, name);
+      if (desc.get || desc.set) {
+        continue;
+      }
+      helpers.sandbox[name] = helpers.makeDebuggeeValue(desc.value);
+    }
+    return helpers;
   },
 
   /**
-   * Evaluates a string in the sandbox.
+   * Evaluates a string using the debugger API.
+   *
+   * To allow the variables view to update properties from the web console we
+   * provide the "bindObjectActor" mechanism: the Web Console tells the
+   * ObjectActor ID for which it desires to evaluate an expression. The
+   * Debugger.Object pointed at by the actor ID is bound such that it is
+   * available during expression evaluation (evalInGlobalWithBindings()).
+   *
+   * Example:
+   *   _self['foobar'] = 'test'
+   * where |_self| refers to the desired object.
+   *
+   * The |frameActor| property allows the Web Console client to provide the
+   * frame actor ID, such that the expression can be evaluated in the
+   * user-selected stack frame.
+   *
+   * For the above to work we need the debugger and the web console to share
+   * a connection, otherwise the Web Console actor will not find the frame
+   * actor.
+   *
+   * The Debugger.Frame comes from the jsdebugger's Debugger instance, which
+   * is different from the Web Console's Debugger instance. This means that
+   * for evaluation to work, we need to create a new instance for  the jsterm
+   * helpers - they need to be Debugger.Objects coming from the jsdebugger's
+   * Debugger instance.
    *
    * @param string aString
-   *        String to evaluate in the sandbox.
-   * @return mixed
-   *         The result of the evaluation.
+   *        String to evaluate.
+   * @param object [aOptions]
+   *        Options for evaluation:
+   *        - bindObjectActor: the ObjectActor ID to use for evaluation.
+   *          |evalWithBindings()| will be called with one additional binding:
+   *          |_self| which will point to the Debugger.Object of the given
+   *          ObjectActor.
+   *        - frameActor: the FrameActor ID to use for evaluation. The given
+   *        debugger frame is used for evaluation, instead of the global window.
+   * @return object
+   *         An object that holds the following properties:
+   *         - dbg: the debugger where the string was evaluated.
+   *         - frame: (optional) the frame where the string was evaluated.
+   *         - window: the Debugger.Object for the global where the string was
+   *         evaluated.
+   *         - result: the result of the evaluation.
    */
-  evalInSandbox: function WCA_evalInSandbox(aString)
+  evalWithDebugger: function WCA_evalWithDebugger(aString, aOptions = {})
   {
-    // If the user changed to a different location, we need to update the
-    // sandbox.
-    if (this._sandboxWindowId !== WebConsoleUtils.getInnerWindowId(this.window)) {
-      this._createSandbox();
-    }
+    this._createGlobal();
 
-    // The help function needs to be easy to guess, so we make the () optional
+    // The help function needs to be easy to guess, so we make the () optional.
     if (aString.trim() == "help" || aString.trim() == "?") {
       aString = "help()";
     }
 
-    let window = WebConsoleUtils.unwrap(this.sandbox.window);
-    let $ = null, $$ = null;
+    let bindSelf = null;
+
+    if (aOptions.bindObjectActor) {
+      let objActor = this.getActorByID(aOptions.bindObjectActor);
+      if (objActor) {
+        bindSelf = objActor.obj;
+      }
+    }
 
-    // We prefer to execute the page-provided implementations for the $() and
-    // $$() functions.
-    if (typeof window.$ == "function") {
-      $ = this.sandbox.$;
-      delete this.sandbox.$;
+    let helpers = this._jstermHelpers;
+    let found$ = false, found$$ = false;
+    let frame = null, frameActor = null;
+    if (aOptions.frameActor) {
+      frameActor = this.conn.getActor(aOptions.frameActor);
+      if (frameActor) {
+        frame = frameActor.frame;
+      }
+      else {
+        Cu.reportError("Web Console Actor: the frame actor was not found: " +
+                       aOptions.frameActor);
+      }
     }
-    if (typeof window.$$ == "function") {
-      $$ = this.sandbox.$$;
-      delete this.sandbox.$$;
+
+    let dbg = this.dbg;
+    let dbgWindow = this._dbgWindow;
+
+    if (frame) {
+      // Avoid having bindings from a different Debugger. The Debugger.Frame
+      // comes from the jsdebugger's Debugger instance.
+      dbg = frameActor.threadActor.dbg;
+      dbgWindow = dbg.addDebuggee(this.window);
+      helpers = this._getJSTermHelpers(dbgWindow);
+
+      let env = frame.environment;
+      if (env) {
+        found$ = !!env.find("$");
+        found$$ = !!env.find("$$");
+      }
+    }
+    else {
+      found$ = !!this._dbgWindow.getOwnPropertyDescriptor("$");
+      found$$ = !!this._dbgWindow.getOwnPropertyDescriptor("$$");
     }
 
-    let result = Cu.evalInSandbox(aString, this.sandbox, "1.8",
-                                  "Web Console", 1);
+    let bindings = helpers.sandbox;
+    if (bindSelf) {
+      let jsObj = bindSelf.unsafeDereference();
+      bindings._self = helpers.makeDebuggeeValue(jsObj);
+    }
+
+    let $ = null, $$ = null;
+    if (found$) {
+      $ = bindings.$;
+      delete bindings.$;
+    }
+    if (found$$) {
+      $$ = bindings.$$;
+      delete bindings.$$;
+    }
+
+    helpers.helperResult = null;
+    helpers.evalInput = aString;
+
+    let result;
+    if (frame) {
+      result = frame.evalWithBindings(aString, bindings);
+    }
+    else {
+      result = this._dbgWindow.evalInGlobalWithBindings(aString, bindings);
+    }
+
+    delete helpers.evalInput;
+    if (helpers != this._jstermHelpers) {
+      this._jstermHelpers.helperResult = helpers.helperResult;
+      delete helpers.helperResult;
+    }
 
     if ($) {
-      this.sandbox.$ = $;
+      bindings.$ = $;
     }
     if ($$) {
-      this.sandbox.$$ = $$;
+      bindings.$$ = $$;
+    }
+
+    if (bindings._self) {
+      delete bindings._self;
     }
 
-    return result;
+    return {
+      result: result,
+      dbg: dbg,
+      frame: frame,
+      window: dbgWindow,
+    };
   },
 
   //////////////////
   // Event handlers for various listeners.
   //////////////////
 
   /**
    * Handler for page errors received from the PageErrorListener. This method
@@ -721,130 +912,58 @@ WebConsoleActor.prototype =
   prepareConsoleMessageForRemote:
   function WCA_prepareConsoleMessageForRemote(aMessage)
   {
     let result = WebConsoleUtils.cloneObject(aMessage);
     delete result.wrappedJSObject;
 
     result.arguments = Array.map(aMessage.arguments || [],
       function(aObj) {
-        return this.createValueGrip(aObj);
+        let dbgObj = this.makeDebuggeeValue(aObj);
+        return this.createValueGrip(dbgObj);
       }, this);
 
-    if (result.level == "dir") {
-      result.objectProperties = [];
-      let first = result.arguments[0];
-      if (typeof first == "object" && first && first.inspectable) {
-        let actor = this.getActorByID(first.actor);
-        result.objectProperties = actor.onInspectProperties().properties;
-      }
-    }
-
     return result;
   },
 
   /**
    * Find the XUL window that owns the content window.
    *
    * @return Window
    *         The XUL window that owns the content window.
    */
   chromeWindow: function WCA_chromeWindow()
   {
-    return this.window.QueryInterface(Ci.nsIInterfaceRequestor)
-           .getInterface(Ci.nsIWebNavigation).QueryInterface(Ci.nsIDocShell)
-           .chromeEventHandler.ownerDocument.defaultView;
+    let window = null;
+    try {
+      window = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
+             .getInterface(Ci.nsIWebNavigation).QueryInterface(Ci.nsIDocShell)
+             .chromeEventHandler.ownerDocument.defaultView;
+    }
+    catch (ex) {
+      // The above can fail because chromeEventHandler is not available for all
+      // kinds of |this.window|.
+    }
+
+    return window;
   },
 };
 
 WebConsoleActor.prototype.requestTypes =
 {
   startListeners: WebConsoleActor.prototype.onStartListeners,
   stopListeners: WebConsoleActor.prototype.onStopListeners,
   getCachedMessages: WebConsoleActor.prototype.onGetCachedMessages,
   evaluateJS: WebConsoleActor.prototype.onEvaluateJS,
   autocomplete: WebConsoleActor.prototype.onAutocomplete,
   clearMessagesCache: WebConsoleActor.prototype.onClearMessagesCache,
   setPreferences: WebConsoleActor.prototype.onSetPreferences,
 };
 
 /**
- * Creates an actor for the specified object.
- *
- * @constructor
- * @param object aObj
- *        The object you want.
- * @param object aWebConsoleActor
- *        The parent WebConsoleActor instance for this object.
- */
-function WebConsoleObjectActor(aObj, aWebConsoleActor)
-{
-  this.obj = aObj;
-  this.parent = aWebConsoleActor;
-}
-
-WebConsoleObjectActor.prototype =
-{
-  actorPrefix: "consoleObj",
-
-  /**
-   * Returns a grip for this actor for returning in a protocol message.
-   */
-  grip: function WCOA_grip()
-  {
-    let grip = WebConsoleUtils.getObjectGrip(this.obj);
-    grip.actor = this.actorID;
-    grip.displayString = this.parent.createStringGrip(grip.displayString);
-    return grip;
-  },
-
-  /**
-   * Releases this actor from the pool.
-   */
-  release: function WCOA_release()
-  {
-    this.parent.releaseActor(this);
-    this.parent = this.obj = null;
-  },
-
-  /**
-   * Handle a protocol request to inspect the properties of the object.
-   *
-   * @return object
-   *         Message to send to the client. This holds the 'properties' property
-   *         - an array with a descriptor for each property in the object.
-   */
-  onInspectProperties: function WCOA_onInspectProperties()
-  {
-    let createObjectActor = this.parent.createObjectActor.bind(this.parent);
-    let props = WebConsoleUtils.inspectObject(this.obj, createObjectActor);
-    return {
-      from: this.actorID,
-      properties: props,
-    };
-  },
-
-  /**
-   * Handle a protocol request to release a grip.
-   */
-  onRelease: function WCOA_onRelease()
-  {
-    this.release();
-    return {};
-  },
-};
-
-WebConsoleObjectActor.prototype.requestTypes =
-{
-  "inspectProperties": WebConsoleObjectActor.prototype.onInspectProperties,
-  "release": WebConsoleObjectActor.prototype.onRelease,
-};
-
-
-/**
  * Creates an actor for a network event.
  *
  * @constructor
  * @param object aNetworkEvent
  *        The network event you want to use the actor for.
  * @param object aWebConsoleActor
  *        The parent WebConsoleActor instance for this object.
  */
@@ -1078,17 +1197,17 @@ NetworkEventActor.prototype =
    * Add network request POST data.
    *
    * @param object aPostData
    *        The request POST data.
    */
   addRequestPostData: function NEA_addRequestPostData(aPostData)
   {
     this._request.postData = aPostData;
-    aPostData.text = this.parent.createStringGrip(aPostData.text);
+    aPostData.text = this._createStringGrip(aPostData.text);
     if (typeof aPostData.text == "object") {
       this._longStringActors.add(aPostData.text);
     }
 
     let packet = {
       from: this.actorID,
       type: "networkEventUpdate",
       updateType: "requestPostData",
@@ -1173,17 +1292,17 @@ NetworkEventActor.prototype =
    *        The response content.
    * @param boolean aDiscardedResponseBody
    *        Tells if the response content was recorded or not.
    */
   addResponseContent:
   function NEA_addResponseContent(aContent, aDiscardedResponseBody)
   {
     this._response.content = aContent;
-    aContent.text = this.parent.createStringGrip(aContent.text);
+    aContent.text = this._createStringGrip(aContent.text);
     if (typeof aContent.text == "object") {
       this._longStringActors.add(aContent.text);
     }
 
     let packet = {
       from: this.actorID,
       type: "networkEventUpdate",
       updateType: "responseContent",
@@ -1223,22 +1342,40 @@ NetworkEventActor.prototype =
    * LongStringActor for the header values, when needed.
    *
    * @private
    * @param array aHeaders
    */
   _prepareHeaders: function NEA__prepareHeaders(aHeaders)
   {
     for (let header of aHeaders) {
-      header.value = this.parent.createStringGrip(header.value);
+      header.value = this._createStringGrip(header.value);
       if (typeof header.value == "object") {
         this._longStringActors.add(header.value);
       }
     }
   },
+
+  /**
+   * Create a long string grip if needed for the given string.
+   *
+   * @private
+   * @param string aString
+   *        The string you want to create a long string grip for.
+   * @return string|object
+   *         A string is returned if |aString| is not a long string.
+   *         A LongStringActor grip is returned if |aString| is a long string.
+   */
+  _createStringGrip: function NEA__createStringGrip(aString)
+  {
+    if (this.parent._stringIsLong(aString)) {
+      return this.parent.longStringGrip(aString, this.parent._actorPool);
+    }
+    return aString;
+  },
 };
 
 NetworkEventActor.prototype.requestTypes =
 {
   "release": NetworkEventActor.prototype.onRelease,
   "getRequestHeaders": NetworkEventActor.prototype.onGetRequestHeaders,
   "getRequestCookies": NetworkEventActor.prototype.onGetRequestCookies,
   "getRequestPostData": NetworkEventActor.prototype.onGetRequestPostData,
--- a/toolkit/devtools/webconsole/test/test_bug819670_getter_throws.html
+++ b/toolkit/devtools/webconsole/test/test_bug819670_getter_throws.html
@@ -29,43 +29,43 @@ function onAttach(aState, aResponse)
 function onEvaluate(aState, aResponse)
 {
   checkObject(aResponse, {
     from: aState.actor,
     input: "document.__proto__",
     result: {
       type: "object",
       actor: /[a-z]/,
-      inspectable: true,
     },
   });
 
-  ok(!aResponse.error, "no js error");
+  ok(!aResponse.exception, "no eval exception");
   ok(!aResponse.helperResult, "no helper result");
 
   onInspect = onInspect.bind(null, aState);
-  aState.client.inspectObjectProperties(aResponse.result.actor, onInspect);
+  let client = new GripClient(aState.dbgClient, aResponse.result);
+  client.getPrototypeAndProperties(onInspect);
 }
 
 function onInspect(aState, aResponse)
 {
   ok(!aResponse.error, "no response error");
 
-  let expectedProps = [
-    { name: "ATTRIBUTE_NODE", value: 2 },
-    { name: "CDATA_SECTION_NODE", value: 4 },
-    { name: "COMMENT_NODE", value: 8 },
-    { name: "DOCUMENT_FRAGMENT_NODE", value: 11 },
-  ];
+  let expectedProps = {
+    "addBroadcastListenerFor": { value: { type: "object" } },
+    "commandDispatcher": { get: { type: "object" } },
+    "getBoxObjectFor": { value: { type: "object" } },
+    "getElementsByAttribute": { value: { type: "object" } },
+  };
 
-  let props = aResponse.properties;
+  let props = aResponse.ownProperties;
   ok(props, "response properties available");
 
   if (props) {
-    ok(props.length > expectedProps.length,
+    ok(Object.keys(props).length > Object.keys(expectedProps).length,
        "number of enumerable properties");
     checkObject(props, expectedProps);
   }
 
   closeDebugger(aState, function() {
     SimpleTest.finish();
   });
 }
--- a/toolkit/devtools/webconsole/test/test_consoleapi.html
+++ b/toolkit/devtools/webconsole/test/test_consoleapi.html
@@ -15,23 +15,23 @@
 SimpleTest.waitForExplicitFinish();
 
 let expectedConsoleCalls = [];
 
 function doConsoleCalls(aState)
 {
   let longString = (new Array(DebuggerServer.LONG_STRING_LENGTH + 2)).join("a");
 
-  console.log("foobarBaz-log", undefined);
-  console.info("foobarBaz-info", null);
-  console.warn("foobarBaz-warn", document.body);
-  console.debug(null);
-  console.trace();
-  console.dir(document, window);
-  console.log("foo", longString);
+  top.console.log("foobarBaz-log", undefined);
+  top.console.info("foobarBaz-info", null);
+  top.console.warn("foobarBaz-warn", top.document.documentElement);
+  top.console.debug(null);
+  top.console.trace();
+  top.console.dir(top.document, top.location);
+  top.console.log("foo", longString);
 
   expectedConsoleCalls = [
     {
       level: "log",
       filename: /test_consoleapi/,
       functionName: "doConsoleCalls",
       timeStamp: /^\d+$/,
       arguments: ["foobarBaz-log", { type: "undefined" }],
@@ -77,34 +77,24 @@ function doConsoleCalls(aState)
       level: "dir",
       filename: /test_consoleapi/,
       functionName: "doConsoleCalls",
       timeStamp: /^\d+$/,
       arguments: [
         {
           type: "object",
           actor: /[a-z]/,
-          className: "HTMLDocument",
+          class: "XULDocument",
         },
         {
           type: "object",
           actor: /[a-z]/,
-          className: "Window",
+          class: "Location",
         }
       ],
-      objectProperties: [
-        {
-          name: "ATTRIBUTE_NODE",
-          value: 2,
-        },
-        {
-          name: "CDATA_SECTION_NODE",
-          value: 4,
-        }, // ...
-      ],
     },
     {
       level: "log",
       filename: /test_consoleapi/,
       functionName: "doConsoleCalls",
       timeStamp: /^\d+$/,
       arguments: [
         "foo",
@@ -119,17 +109,17 @@ function doConsoleCalls(aState)
     },
   ];
 }
 
 function startTest()
 {
   removeEventListener("load", startTest);
 
-  attachConsole(["ConsoleAPI"], onAttach);
+  attachConsole(["ConsoleAPI"], onAttach, true);
 }
 
 function onAttach(aState, aResponse)
 {
   onConsoleAPICall = onConsoleAPICall.bind(null, aState);
   aState.dbgClient.addListener("consoleAPICall", onConsoleAPICall);
   doConsoleCalls(aState.actor);
 }
--- a/toolkit/devtools/webconsole/test/test_jsterm.html
+++ b/toolkit/devtools/webconsole/test/test_jsterm.html
@@ -86,41 +86,41 @@ function doSimpleEval()
 function onSimpleEval(aResponse)
 {
   checkObject(aResponse, {
     from: gState.actor,
     input: "2+2",
     result: 4,
   });
 
-  ok(!aResponse.error, "no js error");
+  ok(!aResponse.exception, "no eval exception");
   ok(!aResponse.helperResult, "no helper result");
 
   nextTest();
 }
 
 function doWindowEval()
 {
-  info("test eval 'window'");
-  gState.client.evaluateJS("window", onWindowEval);
+  info("test eval 'document'");
+  gState.client.evaluateJS("document", onWindowEval);
 }
 
 function onWindowEval(aResponse)
 {
   checkObject(aResponse, {
     from: gState.actor,
-    input: "window",
+    input: "document",
     result: {
       type: "object",
-      className: "Window",
+      class: "XULDocument",
       actor: /[a-z]/,
     },
   });
 
-  ok(!aResponse.error, "no js error");
+  ok(!aResponse.exception, "no eval exception");
   ok(!aResponse.helperResult, "no helper result");
 
   nextTest();
 }
 
 function doEvalWithException()
 {
   info("test eval with exception");
@@ -130,20 +130,20 @@ function doEvalWithException()
 function onEvalWithException(aResponse)
 {
   checkObject(aResponse, {
     from: gState.actor,
     input: "window.doTheImpossible()",
     result: {
       type: "undefined",
     },
-    errorMessage: /doTheImpossible/,
+    exceptionMessage: /doTheImpossible/,
   });
 
-  ok(aResponse.error, "js error object");
+  ok(aResponse.exception, "js eval exception");
   ok(!aResponse.helperResult, "no helper result");
 
   nextTest();
 }
 
 function doEvalWithHelper()
 {
   info("test eval with helper");
@@ -156,17 +156,17 @@ function onEvalWithHelper(aResponse)
     from: gState.actor,
     input: "clear()",
     result: {
       type: "undefined",
     },
     helperResult: { type: "clearOutput" },
   });
 
-  ok(!aResponse.error, "no js error");
+  ok(!aResponse.exception, "no eval exception");
 
   nextTest();
 }
 
 function doEvalString()
 {
   gState.client.evaluateJS("window.foobarObject.strfoo", onEvalString);
 }
--- a/toolkit/devtools/webconsole/test/test_object_actor.html
+++ b/toolkit/devtools/webconsole/test/test_object_actor.html
@@ -25,174 +25,148 @@ function startTest()
 
 function onAttach(aState, aResponse)
 {
   onConsoleCall = onConsoleCall.bind(null, aState);
   aState.dbgClient.addListener("consoleAPICall", onConsoleCall);
 
   let longString = (new Array(DebuggerServer.LONG_STRING_LENGTH + 3)).join("\u0629");
 
-  window.foobarObject = Object.create(null);
+  // Here we put the objects in the correct window, to avoid having them all
+  // wrapped by proxies for cross-compartment access.
+
+  let foobarObject = top.Object.create(null);
   foobarObject.tamarbuta = longString;
   foobarObject.foo = 1;
   foobarObject.foobar = "hello";
-  foobarObject.foobaz = document;
   foobarObject.omg = null;
   foobarObject.testfoo = false;
-  foobarObject.notInspectable = {};
-  foobarObject.omgfn = function _omgfn() {
-    return "myResult";
-  };
-  foobarObject.abArray = ["a", "b"];
+  foobarObject.notInspectable = top.Object.create(null);
+  foobarObject.omgfn = new top.Function("return 'myResult'");
+  foobarObject.abArray = new top.Array("a", "b");
+  foobarObject.foobaz = top.document;
 
-  Object.defineProperty(foobarObject, "getterAndSetter", {
+  top.Object.defineProperty(foobarObject, "getterAndSetter", {
     enumerable: true,
-    get: function fooGet() { return "foo"; },
-    set: function fooSet() { 1+2 },
+    get: new top.Function("return 'foo';"),
+    set: new top.Function("1+2"),
   });
 
-  foobarObject.longStringObj = {
-    toSource: function() longString,
-    toString: function() longString,
-    boom: "explode",
-  };
+  foobarObject.longStringObj = top.Object.create(null);
+  foobarObject.longStringObj.toSource = new top.Function("'" + longString + "'");
+  foobarObject.longStringObj.toString = new top.Function("'" + longString + "'");
+  foobarObject.longStringObj.boom = "explode";
 
-  console.log("hello", foobarObject);
+  top.wrappedJSObject.foobarObject = foobarObject;
+  top.console.log("hello", top.wrappedJSObject.foobarObject);
 
-  expectedProps = [
-    {
-      name: "abArray",
+  expectedProps = {
+    "abArray": {
       value: {
         type: "object",
-        className: "Array",
+        class: "Array",
         actor: /[a-z]/,
-        inspectable: true,
       },
     },
-    {
-      name: "foo",
+    "foo": {
       configurable: true,
       enumerable: true,
       writable: true,
       value: 1,
     },
-    {
-      name: "foobar",
+    "foobar": {
       value: "hello",
     },
-    {
-      name: "foobaz",
+    "foobaz": {
       value: {
         type: "object",
-        className: "HTMLDocument",
-        displayString: /\[object HTMLDocument/,
-        inspectable: true,
+        class: "XULDocument",
+        actor: /[a-z]/,
+      },
+    },
+    "getterAndSetter": {
+      get: {
+        type: "object",
+        class: "Function",
+        actor: /[a-z]/,
+      },
+      set: {
+        type: "object",
+        class: "Function",
         actor: /[a-z]/,
       },
     },
-    {
-      name: "getterAndSetter",
-      get: {
-        type: "function",
-        className: "Function",
-        displayString: /function fooGet/,
+    "longStringObj": {
+      value: {
+        type: "object",
+        class: "Object",
         actor: /[a-z]/,
-        inspectable: false,
-      },
-      set: {
-        type: "function",
-        className: "Function",
-        displayString: /function fooSet/,
-        actor: /[a-z]/,
-        inspectable: false,
       },
     },
-    {
-      name: "longStringObj",
+    "notInspectable": {
       value: {
         type: "object",
-        className: "Object",
+        class: "Object",
         actor: /[a-z]/,
-        inspectable: true,
-        displayString: {
-          type: "longString",
-          initial: longString.substring(0,
-            DebuggerServer.LONG_STRING_INITIAL_LENGTH),
-          length: longString.length,
-        },
       },
     },
-    {
-      name: "notInspectable",
+    "omg": {
+      value: { type: "null" },
+    },
+    "omgfn": {
       value: {
         type: "object",
-        className: "Object",
+        class: "Function",
         actor: /[a-z]/,
-        inspectable: false,
       },
     },
-    {
-      name: "omg",
-      value: { type: "null" },
-    },
-    {
-      name: "omgfn",
-      value: {
-        type: "function",
-        className: "Function",
-        displayString: /function _omgfn/,
-        actor: /[a-z]/,
-        inspectable: false,
-      },
-    },
-    {
-      name: "tamarbuta",
+    "tamarbuta": {
       value: {
         type: "longString",
         initial: longString.substring(0,
           DebuggerServer.LONG_STRING_INITIAL_LENGTH),
         length: longString.length,
       },
     },
-    {
-      name: "testfoo",
+    "testfoo": {
       value: false,
     },
-  ];
+  };
 }
 
 function onConsoleCall(aState, aType, aPacket)
 {
   is(aPacket.from, aState.actor, "console API call actor");
 
   info("checking the console API call packet");
 
   checkConsoleAPICall(aPacket.message, {
     level: "log",
     filename: /test_object_actor/,
     functionName: "onAttach",
     arguments: ["hello", {
       type: "object",
       actor: /[a-z]/,
-      inspectable: true,
     }],
   });
 
   aState.dbgClient.removeListener("consoleAPICall", onConsoleCall);
 
   info("inspecting object properties");
   let args = aPacket.message.arguments;
   onProperties = onProperties.bind(null, aState);
-  aState.client.inspectObjectProperties(args[1].actor, onProperties);
+
+  let client = new GripClient(aState.dbgClient, args[1]);
+  client.getPrototypeAndProperties(onProperties);
 }
 
 function onProperties(aState, aResponse)
 {
-  let props = aResponse.properties;
-  is(props.length, expectedProps.length,
+  let props = aResponse.ownProperties;
+  is(Object.keys(props).length, Object.keys(expectedProps).length,
      "number of enumerable properties");
   checkObject(props, expectedProps);
 
   expectedProps = [];
 
   closeDebugger(aState, function() {
     SimpleTest.finish();
   });