Backed out changeset a0a023dac829 (bug 1392760) for debugger failure at object-inspector/types
authorDaniel Varga <dvarga@mozilla.com>
Mon, 06 May 2019 03:14:57 +0300
changeset 531457 57f669c6fab1a6a0ec7446aeee9e7e01c701c0dc
parent 531456 a0a023dac829933a76c7ab83ceb578878a086238
child 531458 ca2f86087d3f9f09c185b2bf209261e892be27d6
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1392760
milestone68.0a1
backs outa0a023dac829933a76c7ab83ceb578878a086238
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
Backed out changeset a0a023dac829 (bug 1392760) for debugger failure at object-inspector/types
devtools/client/debugger/packages/devtools-reps/src/object-inspector/types.js
devtools/client/debugger/packages/devtools-reps/src/object-inspector/utils/client.js
devtools/client/debugger/packages/devtools-reps/src/object-inspector/utils/load-properties.js
devtools/client/debugger/packages/devtools-reps/src/object-inspector/utils/node.js
devtools/client/debugger/packages/devtools-reps/src/reps/stubs/grip.js
devtools/client/shared/components/reps/reps.js
devtools/client/shared/widgets/VariablesViewController.jsm
devtools/client/webconsole/test/mochitest/browser.ini
devtools/client/webconsole/test/mochitest/browser_webconsole_object_inspector_nested_proxy.js
devtools/server/actors/object.js
devtools/server/actors/object/previewers.js
devtools/server/tests/unit/test_objectgrips-17.js
devtools/server/tests/unit/test_objectgrips-nested-proxy.js
devtools/server/tests/unit/xpcshell.ini
devtools/shared/client/debugger-client.js
devtools/shared/client/object-client.js
devtools/shared/specs/object.js
--- a/devtools/client/debugger/packages/devtools-reps/src/object-inspector/types.js
+++ b/devtools/client/debugger/packages/devtools-reps/src/object-inspector/types.js
@@ -69,17 +69,16 @@ export type PropertiesIterator = {
   slice: (start: number, count: number) => Promise<GripProperties>
 };
 
 export type ObjectClient = {
   enumEntries: () => Promise<PropertiesIterator>,
   enumProperties: (options: Object) => Promise<PropertiesIterator>,
   enumSymbols: () => Promise<PropertiesIterator>,
   getPrototype: () => Promise<{ prototype: Object }>
-  getProxySlots: () => Promise<{ proxyTarget: Object, proxyHandler: Object }>
 };
 
 export type LongStringClient = {
   substring: (
     start: number,
     end: number,
     response: {
       substring?: string,
--- a/devtools/client/debugger/packages/devtools-reps/src/object-inspector/utils/client.js
+++ b/devtools/client/debugger/packages/devtools-reps/src/object-inspector/utils/client.js
@@ -112,22 +112,16 @@ async function getFullText(
 
       resolve({
         fullText: initial + response.substring
       });
     });
   });
 }
 
-async function getProxySlots(
-  objectClient: ObjectClient
-): Promise<{ proxyTarget?: Object, proxyHandler?: Object }> {
-  return objectClient.getProxySlots();
-}
-
 function iteratorSlice(
   iterator: PropertiesIterator,
   start: ?number,
   end: ?number
 ): Promise<GripProperties> {
   start = start || 0;
   const count = end ? end - start + 1 : iterator.count;
 
@@ -138,11 +132,10 @@ function iteratorSlice(
 }
 
 module.exports = {
   enumEntries,
   enumIndexedProperties,
   enumNonIndexedProperties,
   enumSymbols,
   getPrototype,
-  getFullText,
-  getProxySlots
+  getFullText
 };
--- a/devtools/client/debugger/packages/devtools-reps/src/object-inspector/utils/load-properties.js
+++ b/devtools/client/debugger/packages/devtools-reps/src/object-inspector/utils/load-properties.js
@@ -3,18 +3,17 @@
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 const {
   enumEntries,
   enumIndexedProperties,
   enumNonIndexedProperties,
   getPrototype,
   enumSymbols,
-  getFullText,
-  getProxySlots
+  getFullText
 } = require("./client");
 
 const {
   getClosestGripNode,
   getClosestNonBucketNode,
   getValue,
   nodeHasAccessors,
   nodeHasAllEntriesInPreview,
@@ -73,20 +72,16 @@ function loadItemProperties(
   if (shouldLoadItemSymbols(item, loadedProperties)) {
     promises.push(enumSymbols(getObjectClient(), start, end));
   }
 
   if (shouldLoadItemFullText(item, loadedProperties)) {
     promises.push(getFullText(createLongStringClient(value), item));
   }
 
-  if (shouldLoadItemProxySlots(item, loadedProperties)) {
-    promises.push(getProxySlots(getObjectClient()));
-  }
-
   return Promise.all(promises).then(mergeResponses);
 }
 
 function mergeResponses(responses: Array<Object>): Object {
   const data = {};
 
   for (const response of responses) {
     if (response.hasOwnProperty("ownProperties")) {
@@ -99,21 +94,16 @@ function mergeResponses(responses: Array
 
     if (response.prototype) {
       data.prototype = response.prototype;
     }
 
     if (response.fullText) {
       data.fullText = response.fullText;
     }
-
-    if (response.proxyTarget && response.proxyHandler) {
-      data.proxyTarget = response.proxyTarget;
-      data.proxyHandler = response.proxyHandler;
-    }
   }
 
   return data;
 }
 
 function shouldLoadItemIndexedProperties(
   item: Node,
   loadedProperties: LoadedProperties = new Map()
@@ -209,26 +199,18 @@ function shouldLoadItemSymbols(
 
 function shouldLoadItemFullText(
   item: Node,
   loadedProperties: LoadedProperties = new Map()
 ) {
   return !loadedProperties.has(item.path) && nodeIsLongString(item);
 }
 
-function shouldLoadItemProxySlots(
-  item: Node,
-  loadedProperties: LoadedProperties = new Map()
-): boolean {
-  return !loadedProperties.has(item.path) && nodeIsProxy(item);
-}
-
 module.exports = {
   loadItemProperties,
   mergeResponses,
   shouldLoadItemEntries,
   shouldLoadItemIndexedProperties,
   shouldLoadItemNonIndexedProperties,
   shouldLoadItemPrototype,
   shouldLoadItemSymbols,
-  shouldLoadItemFullText,
-  shouldLoadItemProxySlots
+  shouldLoadItemFullText
 };
--- a/devtools/client/debugger/packages/devtools-reps/src/object-inspector/utils/node.js
+++ b/devtools/client/debugger/packages/devtools-reps/src/object-inspector/utils/node.js
@@ -341,21 +341,18 @@ function makeNodesForPromiseProperties(i
         type: NODE_TYPES.PROMISE_VALUE
       })
     );
   }
 
   return properties;
 }
 
-function makeNodesForProxyProperties(
-  loadedProps: GripProperties,
-  item: Node
-): Array<Node> {
-  const { proxyHandler, proxyTarget } = loadedProps;
+function makeNodesForProxyProperties(item: Node): Array<Node> {
+  const { proxyHandler, proxyTarget } = getValue(item);
 
   return [
     createNode({
       parent: item,
       name: "<target>",
       contents: { value: proxyTarget },
       type: NODE_TYPES.PROXY_TARGET
     }),
@@ -790,18 +787,18 @@ function getChildren(options: {
   if (nodeHasChildren(item)) {
     return addToCache(item.contents);
   }
 
   if (nodeIsMapEntry(item)) {
     return addToCache(makeNodesForMapEntry(item));
   }
 
-  if (nodeIsProxy(item) && hasLoadedProps) {
-    return addToCache(makeNodesForProxyProperties(loadedProps, item));
+  if (nodeIsProxy(item)) {
+    return addToCache(makeNodesForProxyProperties(item));
   }
 
   if (nodeIsLongString(item) && hasLoadedProps) {
     // Set longString object's fullText to fetched one.
     return addToCache(setNodeFullText(loadedProps, item));
   }
 
   if (nodeNeedsNumericalBuckets(item) && hasLoadedProps) {
--- a/devtools/client/debugger/packages/devtools-reps/src/reps/stubs/grip.js
+++ b/devtools/client/debugger/packages/devtools-reps/src/reps/stubs/grip.js
@@ -298,16 +298,32 @@ stubs.set("testStringObject", {
     safeGetterValues: {},
     wrappedValue: "foo"
   }
 });
 stubs.set("testProxy", {
   type: "object",
   actor: "server1.conn1.child1/obj47",
   class: "Proxy",
+  proxyTarget: {
+    type: "object",
+    actor: "server1.conn1.child1/obj48",
+    class: "Object",
+    ownPropertyLength: 1
+  },
+  proxyHandler: {
+    type: "object",
+    actor: "server1.conn1.child1/obj49",
+    class: "Array",
+    ownPropertyLength: 4,
+    preview: {
+      kind: "ArrayLike",
+      length: 3
+    }
+  },
   preview: {
     kind: "Object",
     ownProperties: {
       "<target>": {
         value: {
           type: "object",
           actor: "server1.conn1.child1/obj48",
           class: "Object",
--- a/devtools/client/shared/components/reps/reps.js
+++ b/devtools/client/shared/components/reps/reps.js
@@ -1707,18 +1707,18 @@ function makeNodesForPromiseProperties(i
       contents: { value: value },
       type: NODE_TYPES.PROMISE_VALUE
     }));
   }
 
   return properties;
 }
 
-function makeNodesForProxyProperties(loadedProps, item) {
-  const { proxyHandler, proxyTarget } = loadedProps;
+function makeNodesForProxyProperties(item) {
+  const { proxyHandler, proxyTarget } = getValue(item);
 
   return [createNode({
     parent: item,
     name: "<target>",
     contents: { value: proxyTarget },
     type: NODE_TYPES.PROXY_TARGET
   }), createNode({
     parent: item,
@@ -2101,18 +2101,18 @@ function getChildren(options) {
   if (nodeHasChildren(item)) {
     return addToCache(item.contents);
   }
 
   if (nodeIsMapEntry(item)) {
     return addToCache(makeNodesForMapEntry(item));
   }
 
-  if (nodeIsProxy(item) && hasLoadedProps) {
-    return addToCache(makeNodesForProxyProperties(loadedProps, item));
+  if (nodeIsProxy(item)) {
+    return addToCache(makeNodesForProxyProperties(item));
   }
 
   if (nodeIsLongString(item) && hasLoadedProps) {
     // Set longString object's fullText to fetched one.
     return addToCache(setNodeFullText(loadedProps, item));
   }
 
   if (nodeNeedsNumericalBuckets(item) && hasLoadedProps) {
@@ -3476,18 +3476,17 @@ module.exports = {
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 const {
   enumEntries,
   enumIndexedProperties,
   enumNonIndexedProperties,
   getPrototype,
   enumSymbols,
-  getFullText,
-  getProxySlots
+  getFullText
 } = __webpack_require__(197);
 
 const {
   getClosestGripNode,
   getClosestNonBucketNode,
   getValue,
   nodeHasAccessors,
   nodeHasAllEntriesInPreview,
@@ -3531,20 +3530,16 @@ function loadItemProperties(item, create
   if (shouldLoadItemSymbols(item, loadedProperties)) {
     promises.push(enumSymbols(getObjectClient(), start, end));
   }
 
   if (shouldLoadItemFullText(item, loadedProperties)) {
     promises.push(getFullText(createLongStringClient(value), item));
   }
 
-  if (shouldLoadItemProxySlots(item, loadedProperties)) {
-    promises.push(getProxySlots(getObjectClient()));
-  }
-
   return Promise.all(promises).then(mergeResponses);
 }
 
 function mergeResponses(responses) {
   const data = {};
 
   for (const response of responses) {
     if (response.hasOwnProperty("ownProperties")) {
@@ -3557,21 +3552,16 @@ function mergeResponses(responses) {
 
     if (response.prototype) {
       data.prototype = response.prototype;
     }
 
     if (response.fullText) {
       data.fullText = response.fullText;
     }
-
-    if (response.proxyTarget && response.proxyHandler) {
-      data.proxyTarget = response.proxyTarget;
-      data.proxyHandler = response.proxyHandler;
-    }
   }
 
   return data;
 }
 
 function shouldLoadItemIndexedProperties(item, loadedProperties = new Map()) {
   const gripItem = getClosestGripNode(item);
   const value = getValue(gripItem);
@@ -3608,30 +3598,25 @@ function shouldLoadItemSymbols(item, loa
 
   return value && !loadedProperties.has(item.path) && !nodeIsBucket(item) && !nodeIsMapEntry(item) && !nodeIsEntries(item) && !nodeIsDefaultProperties(item) && !nodeHasAccessors(item) && !nodeIsPrimitive(item) && !nodeIsLongString(item) && !nodeIsProxy(item);
 }
 
 function shouldLoadItemFullText(item, loadedProperties = new Map()) {
   return !loadedProperties.has(item.path) && nodeIsLongString(item);
 }
 
-function shouldLoadItemProxySlots(item, loadedProperties = new Map()) {
-  return !loadedProperties.has(item.path) && nodeIsProxy(item);
-}
-
 module.exports = {
   loadItemProperties,
   mergeResponses,
   shouldLoadItemEntries,
   shouldLoadItemIndexedProperties,
   shouldLoadItemNonIndexedProperties,
   shouldLoadItemPrototype,
   shouldLoadItemSymbols,
-  shouldLoadItemFullText,
-  shouldLoadItemProxySlots
+  shouldLoadItemFullText
 };
 
 /***/ }),
 
 /***/ 197:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
@@ -3716,38 +3701,33 @@ async function getFullText(longStringCli
 
       resolve({
         fullText: initial + response.substring
       });
     });
   });
 }
 
-async function getProxySlots(objectClient) {
-  return objectClient.getProxySlots();
-}
-
 function iteratorSlice(iterator, start, end) {
   start = start || 0;
   const count = end ? end - start + 1 : iterator.count;
 
   if (count === 0) {
     return Promise.resolve({});
   }
   return iterator.slice(start, count);
 }
 
 module.exports = {
   enumEntries,
   enumIndexedProperties,
   enumNonIndexedProperties,
   enumSymbols,
   getPrototype,
-  getFullText,
-  getProxySlots
+  getFullText
 };
 
 /***/ }),
 
 /***/ 2:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
--- a/devtools/client/shared/widgets/VariablesViewController.jsm
+++ b/devtools/client/shared/widgets/VariablesViewController.jsm
@@ -313,28 +313,26 @@ VariablesViewController.prototype = {
    *
    * @param Scope aTarget
    *        The Scope where the properties will be placed into.
    * @param object aGrip
    *        The grip to use to populate the target.
    */
   _populateFromObject: function(aTarget, aGrip) {
     if (aGrip.class === "Proxy") {
-      // Refuse to play the proxy's stupid game and just expose the target and handler.
+      this.addExpander(
+        aTarget.addItem("<target>", { value: aGrip.proxyTarget }, { internalItem: true }),
+        aGrip.proxyTarget);
+      this.addExpander(
+        aTarget.addItem("<handler>", { value: aGrip.proxyHandler }, { internalItem: true }),
+        aGrip.proxyHandler);
+
+      // Refuse to play the proxy's stupid game and return immediately
       const deferred = defer();
-      const objectClient = this._getObjectClient(aGrip);
-      objectClient.getProxySlots(aResponse => {
-        const target = aTarget.addItem("<target>", { value: aResponse.proxyTarget },
-          { internalItem: true });
-        this.addExpander(target, aResponse.proxyTarget);
-        const handler = aTarget.addItem("<handler>", { value: aResponse.proxyHandler },
-          { internalItem: true });
-        this.addExpander(handler, aResponse.proxyHandler);
-        deferred.resolve();
-      });
+      deferred.resolve();
       return deferred.promise;
     }
 
     if (aGrip.class === "Promise" && aGrip.promiseState) {
       const { state, value, reason } = aGrip.promiseState;
       aTarget.addItem("<state>", { value: state }, { internalItem: true });
       if (state === "fulfilled") {
         this.addExpander(
--- a/devtools/client/webconsole/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/test/mochitest/browser.ini
@@ -349,17 +349,16 @@ skip-if = true  # Bug 1438979
 [browser_webconsole_object_inspector.js]
 [browser_webconsole_object_inspector__proto__.js]
 [browser_webconsole_object_inspector_entries.js]
 [browser_webconsole_object_inspector_getters.js]
 [browser_webconsole_object_inspector_getters_prototype.js]
 [browser_webconsole_object_inspector_getters_shadowed.js]
 [browser_webconsole_object_inspector_key_sorting.js]
 [browser_webconsole_object_inspector_local_session_storage.js]
-[browser_webconsole_object_inspector_nested_proxy.js]
 [browser_webconsole_object_inspector_selected_text.js]
 [browser_webconsole_object_inspector_scroll.js]
 [browser_webconsole_object_inspector_while_debugging_and_inspecting.js]
 [browser_webconsole_observer_notifications.js]
 [browser_webconsole_optimized_out_vars.js]
 [browser_webconsole_output_copy.js]
 tags = clipboard
 [browser_webconsole_output_copy_newlines.js]
deleted file mode 100644
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_object_inspector_nested_proxy.js
+++ /dev/null
@@ -1,50 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-// Check evaluating and expanding getters in the console.
-const TEST_URI = "data:text/html;charset=utf8,"
- + "<h1>Object Inspector on deeply nested proxies</h1>";
-
-add_task(async function() {
-  const hud = await openNewTabAndConsole(TEST_URI);
-
-  await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
-    let proxy = new Proxy({}, {});
-    for (let i = 0; i < 1e5; ++i) {
-      proxy = new Proxy(proxy, proxy);
-    }
-    content.wrappedJSObject.console.log("oi-test", proxy);
-  });
-
-  const node = await waitFor(() => findMessage(hud, "oi-test"));
-  const oi = node.querySelector(".tree");
-  const [proxyNode] = getObjectInspectorNodes(oi);
-
-  expandObjectInspectorNode(proxyNode);
-  await waitFor(() => getObjectInspectorNodes(oi).length > 1);
-  checkChildren(proxyNode, [`<target>`, `<handler>`]);
-
-  const targetNode = findObjectInspectorNode(oi, "<target>");
-  expandObjectInspectorNode(targetNode);
-  await waitFor(() => getObjectInspectorChildrenNodes(targetNode).length > 0);
-  checkChildren(targetNode, [`<target>`, `<handler>`]);
-
-  const handlerNode = findObjectInspectorNode(oi, "<handler>");
-  expandObjectInspectorNode(handlerNode);
-  await waitFor(() => getObjectInspectorChildrenNodes(handlerNode).length > 0);
-  checkChildren(handlerNode, [`<target>`, `<handler>`]);
-});
-
-function checkChildren(node, expectedChildren) {
-  const children = getObjectInspectorChildrenNodes(node);
-  is(children.length, expectedChildren.length,
-    "There is the expected number of children");
-  children.forEach((child, index) => {
-    ok(child.textContent.includes(expectedChildren[index]),
-      `Expected "${expectedChildren[index]}" child`);
-  });
-}
--- a/devtools/server/actors/object.js
+++ b/devtools/server/actors/object.js
@@ -850,33 +850,16 @@ const proto = {
       source,
       line: stack.line,
       column: stack.column,
       functionDisplayName: stack.functionDisplayName,
     };
   },
 
   /**
-   * Handle a protocol request to get the target and handler internal slots of a proxy.
-   */
-  proxySlots: function() {
-    // There could be transparent security wrappers, unwrap to check if it's a proxy.
-    // However, retrieve proxyTarget and proxyHandler from `this.obj` to avoid exposing
-    // the unwrapped target and handler.
-    if (!DevToolsUtils.unwrap(this.obj).isProxy) {
-      return this.throwError("objectNotProxy",
-        "'proxySlots' request is only valid for grips with a 'Proxy' class.");
-    }
-    return {
-      proxyTarget: this.hooks.createValueGrip(this.obj.proxyTarget),
-      proxyHandler: this.hooks.createValueGrip(this.obj.proxyHandler),
-    };
-  },
-
-  /**
    * Release the actor, when it isn't needed anymore.
    * Protocol.js uses this release method to call the destroy method.
    */
   release: function() {},
 };
 
 exports.ObjectActor = protocol.ActorClassWithSpec(objectSpec, proto);
 exports.ObjectActorProto = proto;
--- a/devtools/server/actors/object/previewers.js
+++ b/devtools/server/actors/object/previewers.js
@@ -283,37 +283,37 @@ const previewers = {
         break;
       }
     }
 
     return true;
   }],
 
   Proxy: [function({obj, hooks}, grip, rawObj) {
-    // Only preview top-level proxies, avoiding recursion. Otherwise, since both the
-    // target and handler can also be proxies, we could get an exponential behavior.
-    if (hooks.getGripDepth() > 1) {
-      return true;
-    }
-
     // The `isProxy` getter of the debuggee object only detects proxies without
     // security wrappers. If false, the target and handler are not available.
     const hasTargetAndHandler = obj.isProxy;
+    if (hasTargetAndHandler) {
+      grip.proxyTarget = hooks.createValueGrip(obj.proxyTarget);
+      grip.proxyHandler = hooks.createValueGrip(obj.proxyHandler);
+    }
 
     grip.preview = {
       kind: "Object",
       ownProperties: Object.create(null),
       ownPropertiesLength: 2 * hasTargetAndHandler,
     };
 
+    if (hooks.getGripDepth() > 1) {
+      return true;
+    }
+
     if (hasTargetAndHandler) {
-      Object.assign(grip.preview.ownProperties, {
-        "<target>": {value: hooks.createValueGrip(obj.proxyTarget)},
-        "<handler>": {value: hooks.createValueGrip(obj.proxyHandler)},
-      });
+      grip.preview.ownProperties["<target>"] = {value: grip.proxyTarget};
+      grip.preview.ownProperties["<handler>"] = {value: grip.proxyHandler};
     }
 
     return true;
   }],
 };
 
 /**
  * Generic previewer for classes wrapping primitives, like String,
--- a/devtools/server/tests/unit/test_objectgrips-17.js
+++ b/devtools/server/tests/unit/test_objectgrips-17.js
@@ -52,22 +52,18 @@ function test({ threadClient, debuggee }
   return new Promise(function(resolve) {
     threadClient.addOneTimeListener("paused", async function(event, packet) {
       // Get the grips.
       const [proxyGrip, inheritsProxyGrip, inheritsProxy2Grip] = packet.frame.arguments;
 
       // Check the grip of the proxy object.
       check_proxy_grip(debuggee, testOptions, proxyGrip);
 
-      // Check the target and handler slots of the proxy object.
+      // Check the prototype and properties of the proxy object.
       const proxyClient = threadClient.pauseGrip(proxyGrip);
-      const proxySlots = await proxyClient.getProxySlots();
-      check_proxy_slots(debuggee, testOptions, proxyGrip, proxySlots);
-
-      // Check the prototype and properties of the proxy object.
       const proxyResponse = await proxyClient.getPrototypeAndProperties();
       check_properties(testOptions, proxyResponse.ownProperties, true, false);
       check_prototype(debuggee, testOptions, proxyResponse.prototype, true, false);
 
       // Check the prototype and properties of the object which inherits from the proxy.
       const inheritsProxyClient = threadClient.pauseGrip(inheritsProxyGrip);
       const inheritsProxyResponse = await inheritsProxyClient.getPrototypeAndProperties();
       check_properties(testOptions, inheritsProxyResponse.ownProperties, false, false);
@@ -116,58 +112,47 @@ function test({ threadClient, debuggee }
 
 function check_proxy_grip(debuggee, testOptions, grip) {
   const { global, isOpaque, subsumes, globalIsInvisible } = testOptions;
   const {preview} = grip;
 
   if (global === debuggee) {
     // The proxy has no security wrappers.
     strictEqual(grip.class, "Proxy", "The grip has a Proxy class.");
+    ok(grip.proxyTarget, "There is a [[ProxyTarget]] grip.");
+    ok(grip.proxyHandler, "There is a [[ProxyHandler]] grip.");
     strictEqual(preview.ownPropertiesLength, 2, "The preview has 2 properties.");
-    const props = preview.ownProperties;
-    ok(props["<target>"].value, "<target> contains the [[ProxyTarget]].");
-    ok(props["<handler>"].value, "<handler> contains the [[ProxyHandler]].");
+    const target = preview.ownProperties["<target>"].value;
+    strictEqual(target, grip.proxyTarget, "<target> contains the [[ProxyTarget]].");
+    const handler = preview.ownProperties["<handler>"].value;
+    strictEqual(handler, grip.proxyHandler, "<handler> contains the [[ProxyHandler]].");
   } else if (isOpaque) {
     // The proxy has opaque security wrappers.
     strictEqual(grip.class, "Opaque", "The grip has an Opaque class.");
     strictEqual(grip.ownPropertyLength, 0, "The grip has no properties.");
   } else if (!subsumes) {
     // The proxy belongs to compartment not subsumed by the debuggee.
-    strictEqual(grip.class, "Restricted", "The grip has a Restricted class.");
+    strictEqual(grip.class, "Restricted", "The grip has an Restricted class.");
     ok(!("ownPropertyLength" in grip), "The grip doesn't know the number of properties.");
   } else if (globalIsInvisible) {
     // The proxy belongs to an invisible-to-debugger compartment.
     strictEqual(grip.class, "InvisibleToDebugger: Object",
                 "The grip has an InvisibleToDebugger class.");
     ok(!("ownPropertyLength" in grip), "The grip doesn't know the number of properties.");
   } else {
     // The proxy has non-opaque security wrappers.
     strictEqual(grip.class, "Proxy", "The grip has a Proxy class.");
+    ok(!("proxyTarget" in grip), "There is no [[ProxyTarget]] grip.");
+    ok(!("proxyHandler" in grip), "There is no [[ProxyHandler]] grip.");
     strictEqual(preview.ownPropertiesLength, 0, "The preview has no properties.");
     ok(!("<target>" in preview), "The preview has no <target> property.");
     ok(!("<handler>" in preview), "The preview has no <handler> property.");
   }
 }
 
-function check_proxy_slots(debuggee, testOptions, grip, proxySlots) {
-  const { global } = testOptions;
-
-  if (grip.class !== "Proxy") {
-    strictEqual(proxySlots, undefined, "Slots can only be retrived for Proxy grips.");
-  } else if (global === debuggee) {
-    const { proxyTarget, proxyHandler } = proxySlots;
-    strictEqual(proxyTarget.type, "object", "There is a [[ProxyTarget]] grip.");
-    strictEqual(proxyHandler.type, "object", "There is a [[ProxyHandler]] grip.");
-  } else {
-    const { proxyTarget, proxyHandler } = proxySlots;
-    strictEqual(proxyTarget.type, "undefined", "There is no [[ProxyTarget]] grip.");
-    strictEqual(proxyHandler.type, "undefined", "There is no [[ProxyHandler]] grip.");
-  }
-}
-
 function check_properties(testOptions, props, isProxy, createdInDebuggee) {
   const { subsumes, globalIsInvisible } = testOptions;
   const ownPropertiesLength = Reflect.ownKeys(props).length;
 
   if (createdInDebuggee || !isProxy && subsumes && !globalIsInvisible) {
     // The debuggee can access the properties.
     strictEqual(ownPropertiesLength, 1, "1 own property was retrieved.");
     strictEqual(props.x.value, 1, "The property has the right value.");
deleted file mode 100644
--- a/devtools/server/tests/unit/test_objectgrips-nested-proxy.js
+++ /dev/null
@@ -1,36 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-/* eslint-disable no-shadow, max-nested-callbacks */
-
-"use strict";
-
-Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-registerCleanupFunction(() => {
-  Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-});
-
-add_task(threadClientTest(async ({ threadClient, debuggee, client }) => {
-  await new Promise(function(resolve) {
-    threadClient.addOneTimeListener("paused", async function(event, packet) {
-      const [grip] = packet.frame.arguments;
-      const objClient = threadClient.pauseGrip(grip);
-      const {proxyTarget, proxyHandler} = await objClient.getProxySlots();
-
-      strictEqual(grip.class, "Proxy", "Its a proxy grip.");
-      strictEqual(proxyTarget.class, "Proxy", "The target is also a proxy.");
-      strictEqual(proxyHandler.class, "Proxy", "The handler is also a proxy.");
-
-      await threadClient.resume();
-      resolve();
-    });
-    debuggee.eval(function stopMe(arg) {
-      debugger;
-    }.toString());
-    debuggee.eval(`
-      var proxy = new Proxy({}, {});
-      for (let i = 0; i < 1e5; ++i)
-        proxy = new Proxy(proxy, proxy);
-      stopMe(proxy);
-    `);
-  });
-}));
--- a/devtools/server/tests/unit/xpcshell.ini
+++ b/devtools/server/tests/unit/xpcshell.ini
@@ -165,17 +165,16 @@ skip-if = true # breakpoint sliding is n
 [test_objectgrips-22.js]
 [test_objectgrips-array-like-object.js]
 [test_objectgrips-property-value-01.js]
 [test_objectgrips-property-value-02.js]
 [test_objectgrips-property-value-03.js]
 [test_objectgrips-fn-apply-01.js]
 [test_objectgrips-fn-apply-02.js]
 [test_objectgrips-fn-apply-03.js]
-[test_objectgrips-nested-proxy.js]
 [test_promise_state-01.js]
 [test_promise_state-02.js]
 [test_promise_state-03.js]
 [test_interrupt.js]
 [test_stepping-01.js]
 [test_stepping-02.js]
 [test_stepping-03.js]
 [test_stepping-04.js]
--- a/devtools/shared/client/debugger-client.js
+++ b/devtools/shared/client/debugger-client.js
@@ -354,21 +354,20 @@ DebuggerClient.prototype = {
     request.format = "json";
     request.stack = getStack();
 
     // Implement a Promise like API on the returned object
     // that resolves/rejects on request response
     const deferred = promise.defer();
     function listenerJson(resp) {
       removeRequestListeners();
-      resp = safeOnResponse(resp);
       if (resp.error) {
-        deferred.reject(resp);
+        deferred.reject(safeOnResponse(resp));
       } else {
-        deferred.resolve(resp);
+        deferred.resolve(safeOnResponse(resp));
       }
     }
     function listenerBulk(resp) {
       removeRequestListeners();
       deferred.resolve(safeOnResponse(resp));
     }
 
     const removeRequestListeners = () => {
--- a/devtools/shared/client/object-client.js
+++ b/devtools/shared/client/object-client.js
@@ -298,34 +298,11 @@ ObjectClient.prototype = {
     before: function(packet) {
       if (this._grip.class !== "Promise") {
         throw new Error("getPromiseRejectionStack is only valid for " +
           "promise grips.");
       }
       return packet;
     },
   }),
-
-  /**
-   * Request the target and handler internal slots of a proxy.
-   */
-  getProxySlots: DebuggerClient.requester({
-    type: "proxySlots",
-  }, {
-    before: function(packet) {
-      if (this._grip.class !== "Proxy") {
-        throw new Error("getProxySlots is only valid for proxy grips.");
-      }
-      return packet;
-    },
-    after: function(response) {
-      // Before Firefox 68 (bug 1392760), the proxySlots request didn't exist.
-      // The proxy target and handler were directly included in the grip.
-      if (response.error === "unrecognizedPacketType") {
-        const {proxyTarget, proxyHandler} = this._grip;
-        return {proxyTarget, proxyHandler};
-      }
-      return response;
-    },
-  }),
 };
 
 module.exports = ObjectClient;
--- a/devtools/shared/specs/object.js
+++ b/devtools/shared/specs/object.js
@@ -97,21 +97,16 @@ types.addDictType("object.dependentPromi
 
 types.addDictType("object.originalSourceLocation", {
   source: "source",
   line: "number",
   column: "number",
   functionDisplayName: "string",
 });
 
-types.addDictType("object.proxySlots", {
-  proxyTarget: "object.descriptor",
-  proxyHandler: "object.descriptor",
-});
-
 const objectSpec = generateActorSpec({
   typeName: "obj",
 
   methods: {
     allocationStack: {
       request: {},
       response: {
         allocationStack: RetVal("array:object.originalSourceLocation"),
@@ -198,20 +193,16 @@ const objectSpec = generateActorSpec({
       response: RetVal("object.apply"),
     },
     rejectionStack: {
       request: {},
       response: {
         rejectionStack: RetVal("array:object.originalSourceLocation"),
       },
     },
-    proxySlots: {
-      request: {},
-      response: RetVal("object.proxySlots"),
-    },
     release: { release: true },
     scope: {
       request: {},
       response: RetVal("object.scope"),
     },
     // Needed for the PauseScopedObjectActor which extends the ObjectActor.
     threadGrip: {
       request: {},