diff options
Diffstat (limited to 'devtools/server/tests/xpcshell/testactors.js')
-rw-r--r-- | devtools/server/tests/xpcshell/testactors.js | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/devtools/server/tests/xpcshell/testactors.js b/devtools/server/tests/xpcshell/testactors.js new file mode 100644 index 0000000000..af208fe93e --- /dev/null +++ b/devtools/server/tests/xpcshell/testactors.js @@ -0,0 +1,242 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { + LazyPool, + createExtraActors, +} = require("resource://devtools/shared/protocol/lazy-pool.js"); +const { RootActor } = require("resource://devtools/server/actors/root.js"); +const { ThreadActor } = require("resource://devtools/server/actors/thread.js"); +const { + DevToolsServer, +} = require("resource://devtools/server/devtools-server.js"); +const { + ActorRegistry, +} = require("resource://devtools/server/actors/utils/actor-registry.js"); +const { + SourcesManager, +} = require("resource://devtools/server/actors/utils/sources-manager.js"); +const makeDebugger = require("resource://devtools/server/actors/utils/make-debugger.js"); +const protocol = require("resource://devtools/shared/protocol.js"); +const { + windowGlobalTargetSpec, +} = require("resource://devtools/shared/specs/targets/window-global.js"); +const { + tabDescriptorSpec, +} = require("resource://devtools/shared/specs/descriptors/tab.js"); +const Targets = require("resource://devtools/server/actors/targets/index.js"); +const { + createContentProcessSessionContext, +} = require("resource://devtools/server/actors/watcher/session-context.js"); + +var gTestGlobals = new Set(); +DevToolsServer.addTestGlobal = function (global) { + gTestGlobals.add(global); +}; +DevToolsServer.removeTestGlobal = function (global) { + gTestGlobals.delete(global); +}; + +DevToolsServer.getTestGlobal = function (name) { + for (const g of gTestGlobals) { + if (g.__name == name) { + return g; + } + } + + return null; +}; + +var gAllowNewThreadGlobals = false; +DevToolsServer.allowNewThreadGlobals = function () { + gAllowNewThreadGlobals = true; +}; +DevToolsServer.disallowNewThreadGlobals = function () { + gAllowNewThreadGlobals = false; +}; + +// 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. +// +// As implemented now, we consult gTestGlobals when we're constructed, not +// when we're iterated over, so tests have to add their globals before the +// root actor is created. +function TestTabList(connection) { + this.conn = connection; + + // An array of actors for each global added with + // DevToolsServer.addTestGlobal. + this._descriptorActors = []; + + // A pool mapping those actors' names to the actors. + this._descriptorActorPool = new LazyPool(connection); + + for (const global of gTestGlobals) { + const actor = new TestTargetActor(connection, global); + this._descriptorActorPool.manage(actor); + + const descriptorActor = new TestDescriptorActor(connection, actor); + this._descriptorActorPool.manage(descriptorActor); + + this._descriptorActors.push(descriptorActor); + } +} + +TestTabList.prototype = { + constructor: TestTabList, + destroy() {}, + getList() { + return Promise.resolve([...this._descriptorActors]); + }, + // Helper method only available for the xpcshell implementation of tablist. + getTargetActorForTab(title) { + const descriptorActor = this._descriptorActors.find(d => d.title === title); + if (!descriptorActor) { + return null; + } + return descriptorActor._targetActor; + }, +}; + +exports.createRootActor = function createRootActor(connection) { + ActorRegistry.registerModule("devtools/server/actors/webconsole", { + prefix: "console", + constructor: "WebConsoleActor", + type: { target: true }, + }); + const root = new RootActor(connection, { + tabList: new TestTabList(connection), + globalActorFactories: ActorRegistry.globalActorFactories, + }); + + root.applicationType = "xpcshell-tests"; + return root; +}; + +class TestDescriptorActor extends protocol.Actor { + constructor(conn, targetActor) { + super(conn, tabDescriptorSpec); + this._targetActor = targetActor; + } + + // We don't exercise the selected tab in xpcshell tests. + get selected() { + return false; + } + + get title() { + return this._targetActor.title; + } + + form() { + const form = { + actor: this.actorID, + traits: {}, + selected: this.selected, + title: this._targetActor.title, + url: this._targetActor.url, + }; + + return form; + } + + getFavicon() { + return ""; + } + + getTarget() { + return this._targetActor.form(); + } +} + +class TestTargetActor extends protocol.Actor { + constructor(conn, global) { + super(conn, windowGlobalTargetSpec); + + this.sessionContext = createContentProcessSessionContext(); + this._global = global; + this._global.wrappedJSObject = global; + this.threadActor = new ThreadActor(this, this._global); + this.conn.addActor(this.threadActor); + this._extraActors = {}; + // This is a hack in order to enable threadActor to be accessed from getFront + this._extraActors.threadActor = this.threadActor; + this.makeDebugger = makeDebugger.bind(null, { + findDebuggees: () => [this._global], + shouldAddNewGlobalAsDebuggee: g => gAllowNewThreadGlobals, + }); + this.dbg = this.makeDebugger(); + this.notifyResources = this.notifyResources.bind(this); + } + + targetType = Targets.TYPES.FRAME; + + get window() { + return this._global; + } + + // Both title and url point to this._global.__name + get title() { + return this._global.__name; + } + + get url() { + return this._global.__name; + } + + get sourcesManager() { + if (!this._sourcesManager) { + this._sourcesManager = new SourcesManager(this.threadActor); + } + return this._sourcesManager; + } + + form() { + const response = { + actor: this.actorID, + title: this.title, + threadActor: this.threadActor.actorID, + }; + + // Walk over target-scoped actors and add them to a new LazyPool. + const actorPool = new LazyPool(this.conn); + const actors = createExtraActors( + ActorRegistry.targetScopedActorFactories, + actorPool, + this + ); + if (actorPool?._poolMap.size > 0) { + this._descriptorActorPool = actorPool; + this.conn.addActorPool(this._descriptorActorPool); + } + + return { ...response, ...actors }; + } + + detach(request) { + this.threadActor.destroy(); + return { type: "detached" }; + } + + reload(request) { + this.sourcesManager.reset(); + this.threadActor.clearDebuggees(); + this.threadActor.dbg.addDebuggees(); + return {}; + } + + removeActorByName(name) { + const actor = this._extraActors[name]; + if (this._descriptorActorPool) { + this._descriptorActorPool.removeActor(actor); + } + delete this._extraActors[name]; + } + + notifyResources(updateType, resources) { + this.emit(`resource-${updateType}-form`, resources); + } +} |