summaryrefslogtreecommitdiffstats
path: root/devtools/server/actors/promises.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/server/actors/promises.js')
-rw-r--r--devtools/server/actors/promises.js200
1 files changed, 200 insertions, 0 deletions
diff --git a/devtools/server/actors/promises.js b/devtools/server/actors/promises.js
new file mode 100644
index 000000000..a9a56219d
--- /dev/null
+++ b/devtools/server/actors/promises.js
@@ -0,0 +1,200 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const protocol = require("devtools/shared/protocol");
+const { promisesSpec } = require("devtools/shared/specs/promises");
+const { expectState, ActorPool } = require("devtools/server/actors/common");
+const { ObjectActor, createValueGrip } = require("devtools/server/actors/object");
+const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+loader.lazyRequireGetter(this, "events", "sdk/event/core");
+
+/**
+ * The Promises Actor provides support for getting the list of live promises and
+ * observing changes to their settlement state.
+ */
+var PromisesActor = protocol.ActorClassWithSpec(promisesSpec, {
+ /**
+ * @param conn DebuggerServerConnection.
+ * @param parent TabActor|RootActor
+ */
+ 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._newPromises = null;
+ this._promisesSettled = null;
+
+ this.objectGrip = this.objectGrip.bind(this);
+ this._makePromiseEventHandler = this._makePromiseEventHandler.bind(this);
+ this._onWindowReady = this._onWindowReady.bind(this);
+ },
+
+ destroy: function () {
+ protocol.Actor.prototype.destroy.call(this, this.conn);
+
+ if (this.state === "attached") {
+ this.detach();
+ }
+ },
+
+ get dbg() {
+ if (!this._dbg) {
+ this._dbg = this.parent.makeDebugger();
+ }
+ return this._dbg;
+ },
+
+ /**
+ * Attach to the PromisesActor.
+ */
+ attach: expectState("detached", function () {
+ this.dbg.addDebuggees();
+
+ this._navigationLifetimePool = this._createActorPool();
+ this.conn.addActorPool(this._navigationLifetimePool);
+
+ this._newPromises = [];
+ this._promisesSettled = [];
+
+ this.dbg.findScripts().forEach(s => {
+ this.parent.sources.createSourceActors(s.source);
+ });
+
+ this.dbg.onNewScript = s => {
+ this.parent.sources.createSourceActors(s.source);
+ };
+
+ events.on(this.parent, "window-ready", this._onWindowReady);
+
+ this.state = "attached";
+ }, "attaching to the PromisesActor"),
+
+ /**
+ * Detach from the PromisesActor upon Debugger closing.
+ */
+ detach: expectState("attached", function () {
+ this.dbg.removeAllDebuggees();
+ this.dbg.enabled = false;
+ this._dbg = null;
+ this._newPromises = null;
+ this._promisesSettled = null;
+
+ if (this._navigationLifetimePool) {
+ this.conn.removeActorPool(this._navigationLifetimePool);
+ this._navigationLifetimePool = null;
+ }
+
+ events.off(this.parent, "window-ready", this._onWindowReady);
+
+ this.state = "detached";
+ }),
+
+ _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) {
+ if (this._navigationLifetimePool.objectActors.has(promise)) {
+ return this._navigationLifetimePool.objectActors.get(promise);
+ }
+
+ let actor = new ObjectActor(promise, {
+ getGripDepth: () => this._gripDepth,
+ incrementGripDepth: () => this._gripDepth++,
+ decrementGripDepth: () => this._gripDepth--,
+ createValueGrip: v =>
+ createValueGrip(v, this._navigationLifetimePool, this.objectGrip),
+ sources: () => this.parent.sources,
+ createEnvironmentActor: () => DevToolsUtils.reportException(
+ "PromisesActor", Error("createEnvironmentActor not yet implemented")),
+ getGlobalDebugObject: () => DevToolsUtils.reportException(
+ "PromisesActor", Error("getGlobalDebugObject not yet implemented")),
+ });
+
+ this._navigationLifetimePool.addActor(actor);
+ this._navigationLifetimePool.objectActors.set(promise, actor);
+
+ return actor;
+ },
+
+ /**
+ * 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) {
+ return this._createObjectActorForPromise(value).grip();
+ },
+
+ /**
+ * Get a list of ObjectActors for all live Promise Objects.
+ */
+ listPromises: function () {
+ let promises = this.dbg.findObjects({ class: "Promise" });
+
+ this.dbg.onNewPromise = this._makePromiseEventHandler(this._newPromises,
+ "new-promises");
+ this.dbg.onPromiseSettled = this._makePromiseEventHandler(
+ this._promisesSettled, "promises-settled");
+
+ return promises.map(p => this._createObjectActorForPromise(p));
+ },
+
+ /**
+ * Creates an event handler for onNewPromise that will add the new
+ * Promise ObjectActor to the array and schedule it to be emitted as a
+ * batch for the provided event.
+ *
+ * @param array array
+ * The list of Promise ObjectActors to emit
+ * @param string eventName
+ * The event name
+ */
+ _makePromiseEventHandler: function (array, eventName) {
+ return promise => {
+ let actor = this._createObjectActorForPromise(promise);
+ let needsScheduling = array.length == 0;
+
+ array.push(actor);
+
+ if (needsScheduling) {
+ DevToolsUtils.executeSoon(() => {
+ events.emit(this, eventName, array.splice(0, array.length));
+ });
+ }
+ };
+ },
+
+ _onWindowReady: expectState("attached", function ({ isTopLevel }) {
+ if (!isTopLevel) {
+ return;
+ }
+
+ this._navigationLifetimePool.cleanup();
+ this.dbg.removeAllDebuggees();
+ this.dbg.addDebuggees();
+ })
+});
+
+exports.PromisesActor = PromisesActor;