Bug 1403106 - Enhance ObjectClient mock so ObjectInspector works in lauchpad; r=Honza. draft
authorNicolas Chevobbe <nchevobbe@mozilla.com>
Tue, 26 Sep 2017 13:59:57 +0200
changeset 670500 baebce7b44b6e2c912a1618f254ae4dac3a5c853
parent 670405 0851b95d0696ecb6a19afef0ca78c683b3a497fb
child 670501 3f9088572f7870d0d1573a204a0d587889797437
push id81633
push userbmo:nchevobbe@mozilla.com
push dateTue, 26 Sep 2017 12:31:54 +0000
reviewersHonza
bugs1403106
milestone58.0a1
Bug 1403106 - Enhance ObjectClient mock so ObjectInspector works in lauchpad; r=Honza. Adding proper mocks for ObjectClient so we can fetch prototype, properties, entries and symbols in the launchpad." MozReview-Commit-ID: 5vQokhUGbm7
devtools/client/webconsole/new-console-output/test/fixtures/ObjectClient.js
--- a/devtools/client/webconsole/new-console-output/test/fixtures/ObjectClient.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/ObjectClient.js
@@ -1,21 +1,481 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+// Objects and functions were cherry-picked from devtools/shared/client/main.js, in
+// order to make the console launchpad Worker.
+
 "use strict";
 
-class ObjectClient {
-  constructor(client, grip) {
-    this._grip = grip;
-    this._client = client;
-    this.request = this._client.request;
-  }
+// mock, we only ned DebuggerClient.requester
+const DebuggerClient = function (transport) {};
+
+/**
+ * A declarative helper for defining methods that send requests to the server.
+ *
+ * @param packetSkeleton
+ *        The form of the packet to send. Can specify fields to be filled from
+ *        the parameters by using the |arg| function.
+ * @param before
+ *        The function to call before sending the packet. Is passed the packet,
+ *        and the return value is used as the new packet. The |this| context is
+ *        the instance of the client object we are defining a method for.
+ * @param after
+ *        The function to call after the response is received. It is passed the
+ *        response, and the return value is considered the new response that
+ *        will be passed to the callback. The |this| context is the instance of
+ *        the client object we are defining a method for.
+ * @return Request
+ *         The `Request` object that is a Promise object and resolves once
+ *         we receive the response. (See request method for more details)
+ */
+DebuggerClient.requester = function (packetSkeleton, config = {}) {
+  let { before, after } = config;
+  return function (...args) {
+    let outgoingPacket = {
+      to: packetSkeleton.to || this.actor
+    };
+
+    let maxPosition = -1;
+    for (let k of Object.keys(packetSkeleton)) {
+      if (packetSkeleton[k] instanceof DebuggerClient.Argument) {
+        let { position } = packetSkeleton[k];
+        outgoingPacket[k] = packetSkeleton[k].getArgument(args);
+        maxPosition = Math.max(position, maxPosition);
+      } else {
+        outgoingPacket[k] = packetSkeleton[k];
+      }
+    }
+
+    if (before) {
+      outgoingPacket = before.call(this, outgoingPacket);
+    }
 
-  getPrototypeAndProperties() {
-    return this._client.request({
-      to: this._grip.actor,
-      type: "prototypeAndProperties"
-    });
+    return this.request(outgoingPacket, (response) => {
+      if (after) {
+        let { from } = response;
+        response = after.call(this, response);
+        if (!response.from) {
+          response.from = from;
+        }
+      }
+
+      // The callback is always the last parameter.
+      let thisCallback = args[maxPosition + 1];
+      if (thisCallback) {
+        thisCallback(response);
+      }
+      return response;
+    }, "DebuggerClient.requester request callback");
+  };
+};
+
+function arg(pos) {
+  return new DebuggerClient.Argument(pos);
+}
+
+DebuggerClient.Argument = function (position) {
+  this.position = position;
+};
+
+DebuggerClient.Argument.prototype.getArgument = function (params) {
+  if (!(this.position in params)) {
+    throw new Error("Bad index into params: " + this.position);
   }
+  return params[this.position];
+};
+
+/**
+ * Grip clients are used to retrieve information about the relevant object.
+ *
+ * @param client DebuggerClient
+ *        The debugger client parent.
+ * @param grip object
+ *        A pause-lifetime object grip returned by the protocol.
+ */
+function ObjectClient(client, grip) {
+  this._grip = grip;
+  this._client = client;
+  this.request = this._client.request;
 }
 
-module.exports = { ObjectClient };
+ObjectClient.prototype = {
+  get actor() {
+    return this._grip.actor;
+  },
+  get _transport() {
+    return this._client._transport;
+  },
+
+  valid: true,
+
+  get isFrozen() {
+    return this._grip.frozen;
+  },
+  get isSealed() {
+    return this._grip.sealed;
+  },
+  get isExtensible() {
+    return this._grip.extensible;
+  },
+
+  getDefinitionSite: DebuggerClient.requester({
+    type: "definitionSite"
+  }, {
+    before: function (packet) {
+      if (this._grip.class != "Function") {
+        throw new Error("getDefinitionSite is only valid for function grips.");
+      }
+      return packet;
+    }
+  }),
+
+  /**
+   * Request the names of a function's formal parameters.
+   *
+   * @param onResponse function
+   *        Called with an object of the form:
+   *        { parameterNames:[<parameterName>, ...] }
+   *        where each <parameterName> is the name of a parameter.
+   */
+  getParameterNames: DebuggerClient.requester({
+    type: "parameterNames"
+  }, {
+    before: function (packet) {
+      if (this._grip.class !== "Function") {
+        throw new Error("getParameterNames is only valid for function grips.");
+      }
+      return packet;
+    },
+  }),
+
+  /**
+   * Request the names of the properties defined on the object and not its
+   * prototype.
+   *
+   * @param onResponse function Called with the request's response.
+   */
+  getOwnPropertyNames: DebuggerClient.requester({
+    type: "ownPropertyNames"
+  }),
+
+  /**
+   * Request the prototype and own properties of the object.
+   *
+   * @param onResponse function Called with the request's response.
+   */
+  getPrototypeAndProperties: DebuggerClient.requester({
+    type: "prototypeAndProperties"
+  }),
+
+  /**
+   * Request a PropertyIteratorClient instance to ease listing
+   * properties for this object.
+   *
+   * @param options Object
+   *        A dictionary object with various boolean attributes:
+   *        - ignoreIndexedProperties Boolean
+   *          If true, filters out Array items.
+   *          e.g. properties names between `0` and `object.length`.
+   *        - ignoreNonIndexedProperties Boolean
+   *          If true, filters out items that aren't array items
+   *          e.g. properties names that are not a number between `0`
+   *          and `object.length`.
+   *        - sort Boolean
+   *          If true, the iterator will sort the properties by name
+   *          before dispatching them.
+   * @param onResponse function Called with the client instance.
+   */
+  enumProperties: DebuggerClient.requester({
+    type: "enumProperties",
+    options: arg(0)
+  }, {
+    after: function (response) {
+      if (response.iterator) {
+        return { iterator: new PropertyIteratorClient(this._client, response.iterator) };
+      }
+      return response;
+    },
+  }),
+
+  /**
+   * Request a PropertyIteratorClient instance to enumerate entries in a
+   * Map/Set-like object.
+   *
+   * @param onResponse function Called with the request's response.
+   */
+  enumEntries: DebuggerClient.requester({
+    type: "enumEntries"
+  }, {
+    before: function (packet) {
+      if (!["Map", "WeakMap", "Set", "WeakSet"].includes(this._grip.class)) {
+        throw new Error("enumEntries is only valid for Map/Set-like grips.");
+      }
+      return packet;
+    },
+    after: function (response) {
+      if (response.iterator) {
+        return {
+          iterator: new PropertyIteratorClient(this._client, response.iterator)
+        };
+      }
+      return response;
+    }
+  }),
+
+  /**
+   * Request a SymbolIteratorClient instance to enumerate symbols in an object.
+   *
+   * @param onResponse function Called with the request's response.
+   */
+  enumSymbols: DebuggerClient.requester({
+    type: "enumSymbols"
+  }, {
+    before: function (packet) {
+      if (this._grip.type !== "object") {
+        throw new Error("enumSymbols is only valid for objects grips.");
+      }
+      return packet;
+    },
+    after: function (response) {
+      if (response.iterator) {
+        return {
+          iterator: new SymbolIteratorClient(this._client, response.iterator)
+        };
+      }
+      return response;
+    }
+  }),
+
+  /**
+   * Request the property descriptor of the object's specified property.
+   *
+   * @param name string The name of the requested property.
+   * @param onResponse function Called with the request's response.
+   */
+  getProperty: DebuggerClient.requester({
+    type: "property",
+    name: arg(0)
+  }),
+
+  /**
+   * Request the prototype of the object.
+   *
+   * @param onResponse function Called with the request's response.
+   */
+  getPrototype: DebuggerClient.requester({
+    type: "prototype"
+  }),
+
+  /**
+   * Request the display string of the object.
+   *
+   * @param onResponse function Called with the request's response.
+   */
+  getDisplayString: DebuggerClient.requester({
+    type: "displayString"
+  }),
+
+  /**
+   * Request the scope of the object.
+   *
+   * @param onResponse function Called with the request's response.
+   */
+  getScope: DebuggerClient.requester({
+    type: "scope"
+  }, {
+    before: function (packet) {
+      if (this._grip.class !== "Function") {
+        throw new Error("scope is only valid for function grips.");
+      }
+      return packet;
+    },
+  }),
+
+  /**
+   * Request the promises directly depending on the current promise.
+   */
+  getDependentPromises: DebuggerClient.requester({
+    type: "dependentPromises"
+  }, {
+    before: function (packet) {
+      if (this._grip.class !== "Promise") {
+        throw new Error("getDependentPromises is only valid for promise " +
+          "grips.");
+      }
+      return packet;
+    }
+  }),
+
+  /**
+   * Request the stack to the promise's allocation point.
+   */
+  getPromiseAllocationStack: DebuggerClient.requester({
+    type: "allocationStack"
+  }, {
+    before: function (packet) {
+      if (this._grip.class !== "Promise") {
+        throw new Error("getAllocationStack is only valid for promise grips.");
+      }
+      return packet;
+    }
+  }),
+
+  /**
+   * Request the stack to the promise's fulfillment point.
+   */
+  getPromiseFulfillmentStack: DebuggerClient.requester({
+    type: "fulfillmentStack"
+  }, {
+    before: function (packet) {
+      if (this._grip.class !== "Promise") {
+        throw new Error("getPromiseFulfillmentStack is only valid for " +
+          "promise grips.");
+      }
+      return packet;
+    }
+  }),
+
+  /**
+   * Request the stack to the promise's rejection point.
+   */
+  getPromiseRejectionStack: DebuggerClient.requester({
+    type: "rejectionStack"
+  }, {
+    before: function (packet) {
+      if (this._grip.class !== "Promise") {
+        throw new Error("getPromiseRejectionStack is only valid for " +
+          "promise grips.");
+      }
+      return packet;
+    }
+  })
+};
+
+/**
+ * A PropertyIteratorClient provides a way to access to property names and
+ * values of an object efficiently, slice by slice.
+ * Note that the properties can be sorted in the backend,
+ * this is controled while creating the PropertyIteratorClient
+ * from ObjectClient.enumProperties.
+ *
+ * @param client DebuggerClient
+ *        The debugger client parent.
+ * @param grip Object
+ *        A PropertyIteratorActor grip returned by the protocol via
+ *        TabActor.enumProperties request.
+ */
+function PropertyIteratorClient(client, grip) {
+  this._grip = grip;
+  this._client = client;
+  this.request = this._client.request;
+}
+
+PropertyIteratorClient.prototype = {
+  get actor() {
+    return this._grip.actor;
+  },
+
+  /**
+   * Get the total number of properties available in the iterator.
+   */
+  get count() {
+    return this._grip.count;
+  },
+
+  /**
+   * Get one or more property names that correspond to the positions in the
+   * indexes parameter.
+   *
+   * @param indexes Array
+   *        An array of property indexes.
+   * @param callback Function
+   *        The function called when we receive the property names.
+   */
+  names: DebuggerClient.requester({
+    type: "names",
+    indexes: arg(0)
+  }, {}),
+
+  /**
+   * Get a set of following property value(s).
+   *
+   * @param start Number
+   *        The index of the first property to fetch.
+   * @param count Number
+   *        The number of properties to fetch.
+   * @param callback Function
+   *        The function called when we receive the property values.
+   */
+  slice: DebuggerClient.requester({
+    type: "slice",
+    start: arg(0),
+    count: arg(1)
+  }, {}),
+
+  /**
+   * Get all the property values.
+   *
+   * @param callback Function
+   *        The function called when we receive the property values.
+   */
+  all: DebuggerClient.requester({
+    type: "all"
+  }, {}),
+};
+
+/**
+ * A SymbolIteratorClient provides a way to access to symbols
+ * of an object efficiently, slice by slice.
+ *
+ * @param client DebuggerClient
+ *        The debugger client parent.
+ * @param grip Object
+ *        A SymbolIteratorActor grip returned by the protocol via
+ *        TabActor.enumSymbols request.
+ */
+function SymbolIteratorClient(client, grip) {
+  this._grip = grip;
+  this._client = client;
+  this.request = this._client.request;
+}
+
+SymbolIteratorClient.prototype = {
+  get actor() {
+    return this._grip.actor;
+  },
+
+  /**
+   * Get the total number of symbols available in the iterator.
+   */
+  get count() {
+    return this._grip.count;
+  },
+
+  /**
+   * Get a set of following symbols.
+   *
+   * @param start Number
+   *        The index of the first symbol to fetch.
+   * @param count Number
+   *        The number of symbols to fetch.
+   * @param callback Function
+   *        The function called when we receive the symbols.
+   */
+  slice: DebuggerClient.requester({
+    type: "slice",
+    start: arg(0),
+    count: arg(1)
+  }, {}),
+
+  /**
+   * Get all the symbols.
+   *
+   * @param callback Function
+   *        The function called when we receive the symbols.
+   */
+  all: DebuggerClient.requester({
+    type: "all"
+  }, {}),
+};
+
+module.exports = { DebuggerClient, ObjectClient };