Bug 1084525 - Part 9: Implement getDependentPromises method in ObjectClient r=fitzgen
authorGabriel Luong <gabriel.luong@gmail.com>
Wed, 10 Jun 2015 00:18:48 -0700
changeset 248117 57ec499fafb57645f5aa26ab8a9e2247a258cfb2
parent 248116 ae4f4b3fd22f0ff16501329eb980afce496aafa0
child 248118 a01b1fbc61e9a91cb1c2ce17147a88ce15d10a9c
push id60888
push userkwierso@gmail.com
push dateThu, 11 Jun 2015 01:38:38 +0000
treeherdermozilla-inbound@39e638ed06bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfitzgen
bugs1084525
milestone41.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 1084525 - Part 9: Implement getDependentPromises method in ObjectClient r=fitzgen
toolkit/devtools/client/dbg-client.jsm
toolkit/devtools/server/actors/object.js
toolkit/devtools/server/tests/unit/test_promises_client_getdependentpromises.js
toolkit/devtools/server/tests/unit/xpcshell.ini
--- a/toolkit/devtools/client/dbg-client.jsm
+++ b/toolkit/devtools/client/dbg-client.jsm
@@ -2411,16 +2411,31 @@ ObjectClient.prototype = {
   }, {
     before: function (aPacket) {
       if (this._grip.class !== "Function") {
         throw new Error("scope is only valid for function grips.");
       }
       return aPacket;
     },
     telemetry: "SCOPE"
+  }),
+
+  /**
+   * Request the promises directly depending on the current promise.
+   */
+  getDependentPromises: DebuggerClient.requester({
+    type: "dependentPromises"
+  }, {
+    before: function(aPacket) {
+      if (this._grip.class !== "Promise") {
+        throw new Error("getDependentPromises is only valid for promise " +
+          "grips.");
+      }
+      return aPacket;
+    }
   })
 };
 
 /**
  * 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
--- a/toolkit/devtools/server/actors/object.js
+++ b/toolkit/devtools/server/actors/object.js
@@ -513,31 +513,54 @@ ObjectActor.prototype = {
     let envActor = this.hooks.createEnvironmentActor(this.obj.environment,
                                                      this.registeredPool);
     if (!envActor) {
       return { error: "notDebuggee",
                message: "cannot access the environment of this function." };
     }
 
     return { from: this.actorID, scope: envActor.form() };
+  },
+
+  /**
+   * Handle a protocol request to get the list of dependent promises of a
+   * promise.
+   *
+   * @return object
+   *         Returns an object containing an array of object grips of the
+   *         dependent promises
+   */
+  onDependentPromises: function() {
+    if (this.obj.class != "Promise") {
+      return { error: "objectNotPromise",
+               message: "'dependentPromises' request is only valid for " +
+                        "object grips with a 'Promise' class." };
+    }
+
+    let rawPromise = this.obj.unsafeDereference();
+    let promises = PromiseDebugging.getDependentPromises(rawPromise).map(p =>
+      this.hooks.createValueGrip(this.obj.makeDebuggeeValue(p)));
+
+    return { promises };
   }
 };
 
 ObjectActor.prototype.requestTypes = {
   "definitionSite": ObjectActor.prototype.onDefinitionSite,
   "parameterNames": ObjectActor.prototype.onParameterNames,
   "prototypeAndProperties": ObjectActor.prototype.onPrototypeAndProperties,
   "enumProperties": ObjectActor.prototype.onEnumProperties,
   "prototype": ObjectActor.prototype.onPrototype,
   "property": ObjectActor.prototype.onProperty,
   "displayString": ObjectActor.prototype.onDisplayString,
   "ownPropertyNames": ObjectActor.prototype.onOwnPropertyNames,
   "decompile": ObjectActor.prototype.onDecompile,
   "release": ObjectActor.prototype.onRelease,
   "scope": ObjectActor.prototype.onScope,
+  "dependentPromises": ObjectActor.prototype.onDependentPromises
 };
 
 /**
  * Creates an actor to iterate over an object's property names and values.
  *
  * @param objectActor ObjectActor
  *        The object actor.
  * @param options Object
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/server/tests/unit/test_promises_client_getdependentpromises.js
@@ -0,0 +1,110 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Test that we can get the list of dependent promises from the ObjectClient.
+ */
+
+"use strict";
+
+const { PromisesFront } = devtools.require("devtools/server/actors/promises");
+
+let events = devtools.require("sdk/event/core");
+
+add_task(function*() {
+  let client = yield startTestDebuggerServer("test-promises-dependentpromises");
+  let chromeActors = yield getChromeActors(client);
+
+  ok(Promise.toString().contains("native code"), "Expect native DOM Promise.");
+
+  yield testGetDependentPromises(client, chromeActors, () => {
+    let p = new Promise(() => {});
+    p.name = "p";
+    let q = p.then();
+    q.name = "q";
+    let r = p.then(null, () => {});
+    r.name = "r";
+
+    return p;
+  });
+
+  let response = yield listTabs(client);
+  let targetTab = findTab(response.tabs, "test-promises-dependentpromises");
+  ok(targetTab, "Found our target tab.");
+
+  yield testGetDependentPromises(client, targetTab, () => {
+    const debuggee =
+      DebuggerServer.getTestGlobal("test-promises-dependentpromises");
+
+    let p = new debuggee.Promise(() => {});
+    p.name = "p";
+    let q = p.then();
+    q.name = "q";
+    let r = p.then(null, () => {});
+    r.name = "r";
+
+    return p;
+  });
+
+  yield close(client);
+});
+
+function* testGetDependentPromises(client, form, makePromises) {
+  let front = PromisesFront(client, form);
+
+  yield front.attach();
+  yield front.listPromises();
+
+  // Get the grip for promise p
+  let onNewPromise = new Promise(resolve => {
+    events.on(front, "new-promises", promises => {
+      for (let p of promises) {
+        if (p.preview.ownProperties.name &&
+            p.preview.ownProperties.name.value === "p") {
+          resolve(p);
+        }
+      }
+    });
+  });
+
+  let promise = makePromises();
+
+  let grip = yield onNewPromise;
+  ok(grip, "Found our promise p.");
+
+  let objectClient = new ObjectClient(client, grip);
+  ok(objectClient, "Got Object Client.");
+
+  // Get the dependent promises for promise p and assert that the list of
+  // dependent promises is correct
+  yield new Promise(resolve => {
+    objectClient.getDependentPromises(response => {
+      let dependentNames = response.promises.map(p =>
+        p.preview.ownProperties.name.value);
+      let expectedDependentNames = ["q", "r"];
+
+      equal(dependentNames.length, expectedDependentNames.length,
+        "Got expected number of dependent promises.");
+
+      for (let i = 0; i < dependentNames.length; i++) {
+        equal(dependentNames[i], expectedDependentNames[i],
+          "Got expected dependent name.");
+      }
+
+      for (let p of response.promises) {
+        equal(p.type, "object", "Expect type to be Object.");
+        equal(p.class, "Promise", "Expect class to be Promise.");
+        equal(typeof p.promiseState.creationTimestamp, "number",
+          "Expect creation timestamp to be a number.");
+        ok(!p.promiseState.timeToSettle,
+          "Expect time to settle to be undefined.");
+      }
+
+      resolve();
+    });
+  });
+
+  yield front.detach();
+  // Appease eslint
+  void promise;
+}
--- a/toolkit/devtools/server/tests/unit/xpcshell.ini
+++ b/toolkit/devtools/server/tests/unit/xpcshell.ini
@@ -80,16 +80,17 @@ support-files =
 [test_eval-03.js]
 [test_eval-04.js]
 [test_eval-05.js]
 [test_promises_actor_attach.js]
 [test_promises_actor_exist.js]
 [test_promises_actor_list_promises.js]
 [test_promises_actor_onnewpromise.js]
 [test_promises_actor_onpromisesettled.js]
+[test_promises_client_getdependentpromises.js]
 [test_promises_object_creationtimestamp.js]
 [test_promises_object_timetosettle-01.js]
 [test_promises_object_timetosettle-02.js]
 [test_protocol_abort.js]
 [test_protocol_async.js]
 [test_protocol_children.js]
 [test_protocol_formtype.js]
 [test_protocol_longstring.js]