content-process.js (8077B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 /* 8 * Target actor for all resources in a content process of Firefox (chrome sandboxes, frame 9 * scripts, documents, etc.) 10 * 11 * See devtools/docs/contributor/backend/actor-hierarchy.md for more details about all the targets. 12 */ 13 14 const { ThreadActor } = require("resource://devtools/server/actors/thread.js"); 15 const { 16 WebConsoleActor, 17 } = require("resource://devtools/server/actors/webconsole.js"); 18 const makeDebugger = require("resource://devtools/server/actors/utils/make-debugger.js"); 19 const { Pool } = require("resource://devtools/shared/protocol.js"); 20 const { assert } = require("resource://devtools/shared/DevToolsUtils.js"); 21 const { 22 SourcesManager, 23 } = require("resource://devtools/server/actors/utils/sources-manager.js"); 24 const { 25 contentProcessTargetSpec, 26 } = require("resource://devtools/shared/specs/targets/content-process.js"); 27 const Targets = require("resource://devtools/server/actors/targets/index.js"); 28 const Resources = require("resource://devtools/server/actors/resources/index.js"); 29 const { 30 BaseTargetActor, 31 } = require("resource://devtools/server/actors/targets/base-target-actor.js"); 32 const { TargetActorRegistry } = ChromeUtils.importESModule( 33 "resource://devtools/server/actors/targets/target-actor-registry.sys.mjs", 34 { global: "shared" } 35 ); 36 37 loader.lazyRequireGetter( 38 this, 39 "WorkerDescriptorActorList", 40 "resource://devtools/server/actors/worker/worker-descriptor-actor-list.js", 41 true 42 ); 43 loader.lazyRequireGetter( 44 this, 45 "MemoryActor", 46 "resource://devtools/server/actors/memory.js", 47 true 48 ); 49 loader.lazyRequireGetter( 50 this, 51 "TracerActor", 52 "resource://devtools/server/actors/tracer.js", 53 true 54 ); 55 56 class ContentProcessTargetActor extends BaseTargetActor { 57 constructor(conn, { isXpcShellTarget = false, sessionContext } = {}) { 58 super(conn, Targets.TYPES.PROCESS, contentProcessTargetSpec); 59 60 this.threadActor = null; 61 this.isXpcShellTarget = isXpcShellTarget; 62 this.sessionContext = sessionContext; 63 64 // Use a see-everything debugger 65 this.makeDebugger = makeDebugger.bind(null, { 66 findDebuggees: dbg => 67 dbg.findAllGlobals().map(g => g.unsafeDereference()), 68 shouldAddNewGlobalAsDebuggee: () => true, 69 }); 70 71 const sandboxPrototype = { 72 get tabs() { 73 return Array.from( 74 Services.ww.getWindowEnumerator(), 75 win => win.docShell.messageManager 76 ); 77 }, 78 }; 79 80 // Scope into which the webconsole executes: 81 // A sandbox with chrome privileges with a `tabs` getter. 82 const systemPrincipal = Cc["@mozilla.org/systemprincipal;1"].createInstance( 83 Ci.nsIPrincipal 84 ); 85 const sandbox = Cu.Sandbox(systemPrincipal, { 86 sandboxPrototype, 87 wantGlobalProperties: ["ChromeUtils"], 88 }); 89 this._consoleScope = sandbox; 90 91 this._workerList = null; 92 this._workerDescriptorActorPool = null; 93 this._onWorkerListChanged = this._onWorkerListChanged.bind(this); 94 95 // Try to destroy the Content Process Target when the content process shuts down. 96 // The parent process can't communicate during shutdown as the communication channel 97 // is already down (message manager or JS Window Actor API). 98 // So that we have to observe to some event fired from this process. 99 // While such cleanup doesn't sound ultimately necessary (the process will be completely destroyed) 100 // mochitests are asserting that there is no leaks during process shutdown. 101 // Do not override destroy as Protocol.js may override it when calling destroy, 102 // and we won't be able to call removeObserver correctly. 103 this.destroyObserver = this.destroy.bind(this); 104 Services.obs.addObserver(this.destroyObserver, "xpcom-shutdown"); 105 if (this.isXpcShellTarget) { 106 TargetActorRegistry.registerXpcShellTargetActor(this); 107 } 108 } 109 110 get isRootActor() { 111 return true; 112 } 113 114 get url() { 115 return undefined; 116 } 117 118 get window() { 119 return this._consoleScope; 120 } 121 122 get targetGlobal() { 123 return this._consoleScope; 124 } 125 126 get sourcesManager() { 127 if (!this._sourcesManager) { 128 assert( 129 this.threadActor, 130 "threadActor should exist when creating SourcesManager." 131 ); 132 this._sourcesManager = new SourcesManager(this.threadActor); 133 } 134 return this._sourcesManager; 135 } 136 137 /* 138 * Return a Debugger instance or create one if there is none yet 139 */ 140 get dbg() { 141 if (!this._dbg) { 142 this._dbg = this.makeDebugger(); 143 } 144 return this._dbg; 145 } 146 147 form() { 148 if (!this._consoleActor) { 149 this._consoleActor = new WebConsoleActor(this.conn, this); 150 this.manage(this._consoleActor); 151 } 152 153 if (!this.threadActor) { 154 this.threadActor = new ThreadActor(this); 155 this.manage(this.threadActor); 156 } 157 if (!this.memoryActor) { 158 this.memoryActor = new MemoryActor(this.conn, this); 159 this.manage(this.memoryActor); 160 } 161 if (!this.tracerActor) { 162 this.tracerActor = new TracerActor(this.conn, this); 163 this.manage(this.tracerActor); 164 } 165 166 return { 167 actor: this.actorID, 168 targetType: this.targetType, 169 170 isXpcShellTarget: this.isXpcShellTarget, 171 processID: Services.appinfo.processID, 172 remoteType: Services.appinfo.remoteType, 173 174 consoleActor: this._consoleActor.actorID, 175 memoryActor: this.memoryActor.actorID, 176 threadActor: this.threadActor.actorID, 177 tracerActor: this.tracerActor.actorID, 178 179 traits: { 180 networkMonitor: false, 181 // See trait description in browsing-context.js 182 supportsTopLevelTargetFlag: false, 183 }, 184 }; 185 } 186 187 ensureWorkerList() { 188 if (!this._workerList) { 189 this._workerList = new WorkerDescriptorActorList(this.conn, {}); 190 } 191 return this._workerList; 192 } 193 194 listWorkers() { 195 return this.ensureWorkerList() 196 .getList() 197 .then(actors => { 198 const pool = new Pool(this.conn, "workers"); 199 for (const actor of actors) { 200 pool.manage(actor); 201 } 202 203 // Do not destroy the pool before transfering ownership to the newly created 204 // pool, so that we do not accidentally destroy actors that are still in use. 205 if (this._workerDescriptorActorPool) { 206 this._workerDescriptorActorPool.destroy(); 207 } 208 209 this._workerDescriptorActorPool = pool; 210 this._workerList.onListChanged = this._onWorkerListChanged; 211 212 return { workers: actors }; 213 }); 214 } 215 216 _onWorkerListChanged() { 217 this.conn.send({ from: this.actorID, type: "workerListChanged" }); 218 this._workerList.onListChanged = null; 219 } 220 221 pauseMatchingServiceWorkers(request) { 222 this.ensureWorkerList().workerPauser.setPauseServiceWorkers(request.origin); 223 } 224 225 destroy({ isModeSwitching } = {}) { 226 // Avoid reentrancy. We will destroy the Transport when emitting "destroyed", 227 // which will force destroying all actors. 228 if (this.destroying) { 229 return; 230 } 231 this.destroying = true; 232 233 // Unregistering watchers first is important 234 // otherwise you might have leaks reported when running browser_browser_toolbox_netmonitor.js in debug builds 235 Resources.unwatchAllResources(this); 236 237 this.emit("destroyed", { isModeSwitching }); 238 239 super.destroy(); 240 241 if (this.threadActor) { 242 this.threadActor = null; 243 } 244 245 // Tell the live lists we aren't watching any more. 246 if (this._workerList) { 247 this._workerList.destroy(); 248 this._workerList = null; 249 } 250 251 if (this._sourcesManager) { 252 this._sourcesManager.destroy(); 253 this._sourcesManager = null; 254 } 255 256 if (this._dbg) { 257 this._dbg.disable(); 258 this._dbg = null; 259 } 260 261 Services.obs.removeObserver(this.destroyObserver, "xpcom-shutdown"); 262 263 if (this.isXpcShellTarget) { 264 TargetActorRegistry.unregisterXpcShellTargetActor(this); 265 } 266 } 267 } 268 269 exports.ContentProcessTargetActor = ContentProcessTargetActor;