Bug 1084525 - Part 4: Add listPromises method to PromisesActor r=fitzgen

This commit is contained in:
Gabriel Luong 2015-06-02 14:52:56 -07:00
Родитель 78acd7471f
Коммит d721f8d0e5
5 изменённых файлов: 174 добавлений и 3 удалений

Просмотреть файл

@ -6,7 +6,16 @@
const protocol = require("devtools/server/protocol");
const { method, RetVal, Arg, types } = protocol;
const { expectState } = require("devtools/server/actors/common");
const { expectState, ActorPool } = require("devtools/server/actors/common");
const { ObjectActor, createValueGrip } = require("devtools/server/actors/object");
const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
loader.lazyRequireGetter(this, "events", "sdk/event/core");
// Teach protocol.js how to deal with legacy actor types
types.addType("ObjectActor", {
write: actor => actor.grip(),
read: grip => grip
});
/**
* The Promises Actor provides support for getting the list of live promises and
@ -22,13 +31,19 @@ let PromisesActor = protocol.ActorClass({
initialize: function(conn, parent) {
protocol.Actor.prototype.initialize.call(this, conn);
this.conn = conn;
this.parent = parent;
this.state = "detached";
this._dbg = null;
this._gripDepth = 0;
this._navigationLifetimePool = null;
this.objectGrip = this.objectGrip.bind(this);
this._onWindowReady = this._onWindowReady.bind(this);
},
destroy: function() {
protocol.Actor.prototype.destroy.call(this, conn);
protocol.Actor.prototype.destroy.call(this, this.conn);
if (this.state === "attached") {
this.detach();
@ -47,6 +62,12 @@ let PromisesActor = protocol.ActorClass({
*/
attach: method(expectState("detached", function() {
this.dbg.addDebuggees();
this._navigationLifetimePool = this._createActorPool();
this.conn.addActorPool(this._navigationLifetimePool);
events.on(this.parent, "window-ready", this._onWindowReady);
this.state = "attached";
}, `attaching to the PromisesActor`), {
request: {},
@ -60,11 +81,93 @@ let PromisesActor = protocol.ActorClass({
this.dbg.removeAllDebuggees();
this.dbg.enabled = false;
this._dbg = null;
if (this._navigationLifetimePool) {
this.conn.removeActorPool(this._navigationLifetimePool);
this._navigationLifetimePool = null;
}
events.off(this.parent, "window-ready", this._onWindowReady);
this.state = "detached";
}, `detaching from the PromisesActor`), {
request: {},
response: {}
}),
_createActorPool: function() {
let pool = new ActorPool(this.conn);
pool.objectActors = new WeakMap();
return pool;
},
/**
* Create an ObjectActor for the given Promise object.
*
* @param object promise
* The promise object
* @return object
* An ObjectActor object that wraps the given Promise object
*/
_createObjectActorForPromise: function(promise) {
let object = new ObjectActor(promise, {
getGripDepth: () => this._gripDepth,
incrementGripDepth: () => this._gripDepth++,
decrementGripDepth: () => this._gripDepth--,
createValueGrip: v =>
createValueGrip(v, this._navigationLifetimePool, this.objectGrip),
sources: () => DevToolsUtils.reportException("PromisesActor",
Error("sources not yet implemented")),
createEnvironmentActor: () => DevToolsUtils.reportException(
"PromisesActor", Error("createEnvironmentActor not yet implemented"))
});
this._navigationLifetimePool.addActor(object);
return object;
},
/**
* Get a grip for the given Promise object.
*
* @param object value
* The Promise object
* @return object
* The grip for the given Promise object
*/
objectGrip: function(value) {
let pool = this._navigationLifetimePool;
if (pool.objectActors.has(value)) {
return pool.objectActors.get(value).grip();
}
let actor = this._createObjectActorForPromise(value);
pool.objectActors.set(value, actor);
return actor.grip();
},
/**
* Get a list of ObjectActors for all live Promise Objects.
*/
listPromises: method(function() {
let promises = this.dbg.findObjects({ class: "Promise" });
return promises.map(p => this._createObjectActorForPromise(p));
}, {
request: {
},
response: {
promises: RetVal("array:ObjectActor")
}
}),
_onWindowReady: expectState("attached", function({ isTopLevel }) {
if (!isTopLevel) {
return;
}
this._navigationLifetimePool.cleanup();
this.dbg.removeAllDebuggees();
this.dbg.addDebuggees();
})
});
exports.PromisesActor = PromisesActor;

Просмотреть файл

Просмотреть файл

@ -0,0 +1,57 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that we can get the list of all live Promise objects from the
* PromisesActor.
*/
"use strict";
const { PromisesFront } = devtools.require("devtools/server/actors/promises");
const SECRET = "MyLittleSecret";
add_task(function*() {
let client = yield startTestDebuggerServer("promises-actor-test");
let chromeActors = yield getChromeActors(client);
yield testListPromises(client, chromeActors, v =>
new Promise(resolve => resolve(v)));
let response = yield listTabs(client);
let targetTab = findTab(response.tabs, "promises-actor-test");
ok(targetTab, "Found our target tab.");
yield testListPromises(client, targetTab, v => {
const debuggee = DebuggerServer.getTestGlobal("promises-actor-test");
return debuggee.Promise.resolve(v);
});
yield close(client);
});
function* testListPromises(client, form, makePromise) {
let resolution = SECRET + Math.random();
let promise = makePromise(resolution);
let front = PromisesFront(client, form);
yield front.attach();
let promises = yield front.listPromises();
let found = false;
for (let p of promises) {
equal(p.type, "object", "Expect type to be Object");
equal(p.class, "Promise", "Expect class to be Promise");
if (p.promiseState.state === "fulfilled" &&
p.promiseState.value === resolution) {
found = true;
}
}
ok(found, "Found our promise");
yield front.detach();
// Appease eslint
void promise;
}

Просмотреть файл

@ -9,11 +9,21 @@ const { TabSources } = require("devtools/server/actors/utils/TabSources");
const promise = require("promise");
const makeDebugger = require("devtools/server/actors/utils/make-debugger");
var gTestGlobals = [];
let gTestGlobals = [];
DebuggerServer.addTestGlobal = function(aGlobal) {
gTestGlobals.push(aGlobal);
};
DebuggerServer.getTestGlobal = function(name) {
for (let g of gTestGlobals) {
if (g.__name == name) {
return g;
}
}
return null;
}
// A mock tab list, for use by tests. This simply presents each global in
// gTestGlobals as a tab, and the list is fixed: it never calls its
// onListChanged handler.

Просмотреть файл

@ -82,6 +82,7 @@ support-files =
[test_eval-05.js]
[test_promises_actor_attach.js]
[test_promises_actor_exist.js]
[test_promises_actor_list_promises.js]
[test_protocol_abort.js]
[test_protocol_async.js]
[test_protocol_children.js]