diff options
Diffstat (limited to 'devtools/server/actors/targets/content-process.js')
-rw-r--r-- | devtools/server/actors/targets/content-process.js | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/devtools/server/actors/targets/content-process.js b/devtools/server/actors/targets/content-process.js new file mode 100644 index 0000000000..0da23e766e --- /dev/null +++ b/devtools/server/actors/targets/content-process.js @@ -0,0 +1,242 @@ +/* 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"; + +/* + * Target actor for all resources in a content process of Firefox (chrome sandboxes, frame + * scripts, documents, etc.) + * + * See devtools/docs/backend/actor-hierarchy.md for more details. + */ + +const { Cc, Ci, Cu } = require("chrome"); +const Services = require("Services"); + +const { ThreadActor } = require("devtools/server/actors/thread"); +const { WebConsoleActor } = require("devtools/server/actors/webconsole"); +const makeDebugger = require("devtools/server/actors/utils/make-debugger"); +const { Pool } = require("devtools/shared/protocol"); +const { assert } = require("devtools/shared/DevToolsUtils"); +const { + SourcesManager, +} = require("devtools/server/actors/utils/sources-manager"); +const { Actor } = require("devtools/shared/protocol"); +const { + contentProcessTargetSpec, +} = require("devtools/shared/specs/targets/content-process"); +const Targets = require("devtools/server/actors/targets/index"); +const Resources = require("devtools/server/actors/resources/index"); +const TargetActorMixin = require("devtools/server/actors/targets/target-actor-mixin"); + +loader.lazyRequireGetter( + this, + "WorkerDescriptorActorList", + "devtools/server/actors/worker/worker-descriptor-actor-list", + true +); +loader.lazyRequireGetter( + this, + "MemoryActor", + "devtools/server/actors/memory", + true +); + +const ContentProcessTargetActor = TargetActorMixin( + Targets.TYPES.PROCESS, + contentProcessTargetSpec, + { + initialize: function(connection) { + Actor.prototype.initialize.call(this, connection); + this.conn = connection; + this.threadActor = null; + + // Use a see-everything debugger + this.makeDebugger = makeDebugger.bind(null, { + findDebuggees: dbg => dbg.findAllGlobals(), + shouldAddNewGlobalAsDebuggee: global => true, + }); + + const sandboxPrototype = { + get tabs() { + return Array.from( + Services.ww.getWindowEnumerator(), + win => win.docShell.messageManager + ); + }, + }; + + // Scope into which the webconsole executes: + // A sandbox with chrome privileges with a `tabs` getter. + const systemPrincipal = Cc[ + "@mozilla.org/systemprincipal;1" + ].createInstance(Ci.nsIPrincipal); + const sandbox = Cu.Sandbox(systemPrincipal, { + sandboxPrototype, + wantGlobalProperties: ["ChromeUtils"], + }); + this._consoleScope = sandbox; + + this._workerList = null; + this._workerDescriptorActorPool = null; + this._onWorkerListChanged = this._onWorkerListChanged.bind(this); + + // Try to destroy the Content Process Target when the content process shuts down. + // The parent process can't communicate during shutdown as the communication channel + // is already down (message manager or JS Window Actor API). + // So that we have to observe to some event fired from this process. + // While such cleanup doesn't sound ultimately necessary (the process will be completely destroyed) + // mochitests are asserting that there is no leaks during process shutdown. + // Do not override destroy as Protocol.js may override it when calling destroy, + // and we won't be able to call removeObserver correctly. + this.destroyObserver = this.destroy.bind(this); + Services.obs.addObserver(this.destroyObserver, "xpcom-shutdown"); + }, + + get isRootActor() { + return true; + }, + + get exited() { + return !this._contextPool; + }, + + get url() { + return undefined; + }, + + get window() { + return this._consoleScope; + }, + + get sourcesManager() { + if (!this._sourcesManager) { + assert( + this.threadActor, + "threadActor should exist when creating SourcesManager." + ); + this._sourcesManager = new SourcesManager(this.threadActor); + } + return this._sourcesManager; + }, + + /* + * Return a Debugger instance or create one if there is none yet + */ + get dbg() { + if (!this._dbg) { + this._dbg = this.makeDebugger(); + } + return this._dbg; + }, + + form: function() { + if (!this._consoleActor) { + this._consoleActor = new WebConsoleActor(this.conn, this); + this.manage(this._consoleActor); + } + + if (!this.threadActor) { + this.threadActor = new ThreadActor(this, null); + this.manage(this.threadActor); + } + if (!this.memoryActor) { + this.memoryActor = new MemoryActor(this.conn, this); + this.manage(this.memoryActor); + } + + return { + actor: this.actorID, + consoleActor: this._consoleActor.actorID, + threadActor: this.threadActor.actorID, + memoryActor: this.memoryActor.actorID, + processID: Services.appinfo.processID, + + traits: { + networkMonitor: false, + }, + }; + }, + + ensureWorkerList() { + if (!this._workerList) { + this._workerList = new WorkerDescriptorActorList(this.conn, {}); + } + return this._workerList; + }, + + listWorkers: function() { + return this.ensureWorkerList() + .getList() + .then(actors => { + const pool = new Pool(this.conn, "workers"); + for (const actor of actors) { + pool.manage(actor); + } + + // Do not destroy the pool before transfering ownership to the newly created + // pool, so that we do not accidentally destroy actors that are still in use. + if (this._workerDescriptorActorPool) { + this._workerDescriptorActorPool.destroy(); + } + + this._workerDescriptorActorPool = pool; + this._workerList.onListChanged = this._onWorkerListChanged; + + return { + from: this.actorID, + workers: actors, + }; + }); + }, + + _onWorkerListChanged: function() { + this.conn.send({ from: this.actorID, type: "workerListChanged" }); + this._workerList.onListChanged = null; + }, + + pauseMatchingServiceWorkers(request) { + this.ensureWorkerList().workerPauser.setPauseServiceWorkers( + request.origin + ); + }, + + destroy: function() { + if (this.isDestroyed()) { + return; + } + Resources.unwatchAllTargetResources(this); + + // Notify the client that this target is being destroyed. + // So that we can destroy the target front and all its children. + this.emit("tabDetached"); + + Actor.prototype.destroy.call(this); + + if (this.threadActor) { + this.threadActor = null; + } + + // Tell the live lists we aren't watching any more. + if (this._workerList) { + this._workerList.destroy(); + this._workerList = null; + } + + if (this._sourcesManager) { + this._sourcesManager.destroy(); + this._sourcesManager = null; + } + + if (this._dbg) { + this._dbg.disable(); + this._dbg = null; + } + + Services.obs.removeObserver(this.destroyObserver, "xpcom-shutdown"); + }, + } +); + +exports.ContentProcessTargetActor = ContentProcessTargetActor; |