parent-process.js (5954B)
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 the entire parent process. 9 * 10 * This actor extends WindowGlobalTargetActor. 11 * 12 * See devtools/docs/contributor/backend/actor-hierarchy.md for more details about all the targets. 13 */ 14 15 const { 16 DevToolsServer, 17 } = require("resource://devtools/server/devtools-server.js"); 18 const { 19 getChildDocShells, 20 WindowGlobalTargetActor, 21 } = require("resource://devtools/server/actors/targets/window-global.js"); 22 const makeDebugger = require("resource://devtools/server/actors/utils/make-debugger.js"); 23 24 const { 25 parentProcessTargetSpec, 26 } = require("resource://devtools/shared/specs/targets/parent-process.js"); 27 28 class ParentProcessTargetActor extends WindowGlobalTargetActor { 29 /** 30 * Creates a target actor for debugging all the chrome content in the parent process. 31 * Most of the implementation is inherited from WindowGlobalTargetActor. 32 * ParentProcessTargetActor is a child of RootActor, it can be instantiated via 33 * RootActor.getProcess request. ParentProcessTargetActor exposes all target-scoped actors 34 * via its form() request, like WindowGlobalTargetActor. 35 * 36 * @param {DevToolsServerConnection} conn 37 * The connection to the client. 38 * @param {boolean} options.isTopLevelTarget 39 * flag to indicate if this is the top 40 * level target of the DevTools session 41 * @param {object} options.sessionContext 42 * The Session Context to help know what is debugged. 43 * See devtools/server/actors/watcher/session-context.js 44 */ 45 constructor(conn, { isTopLevelTarget, sessionContext }) { 46 super(conn, { 47 isTopLevelTarget, 48 sessionContext, 49 customSpec: parentProcessTargetSpec, 50 }); 51 52 // This creates a Debugger instance for chrome debugging all globals. 53 this.makeDebugger = makeDebugger.bind(null, { 54 findDebuggees: dbg => 55 dbg.findAllGlobals().map(g => g.unsafeDereference()), 56 shouldAddNewGlobalAsDebuggee: () => true, 57 }); 58 59 // Ensure catching the creation of any new content docshell 60 this.watchNewDocShells = true; 61 62 this.isRootActor = true; 63 64 // Listen for any new/destroyed chrome docshell 65 Services.obs.addObserver(this, "chrome-webnavigation-create"); 66 Services.obs.addObserver(this, "chrome-webnavigation-destroy"); 67 68 this.setDocShell(this._getInitialDocShell()); 69 } 70 71 // Overload setDocShell in order to observe all the docshells. 72 // WindowGlobalTargetActor only observes the top level one. 73 setDocShell(initialDocShell) { 74 super.setDocShell(initialDocShell); 75 76 // Iterate over all top-level windows. 77 for (const { docShell } of Services.ww.getWindowEnumerator()) { 78 if (docShell == this.docShell) { 79 continue; 80 } 81 this._progressListener.watch(docShell); 82 } 83 } 84 85 _getInitialDocShell() { 86 // Defines the default docshell selected for the target actor 87 let window = Services.wm.getMostRecentWindow( 88 DevToolsServer.chromeWindowType 89 ); 90 91 // Default to any available top level window if there is no expected window 92 // eg when running ./mach run --chrome chrome://browser/content/aboutTabCrashed.xhtml --jsdebugger 93 if (!window) { 94 // If DevTools is started early enough, this window will be the 95 // early navigator:blank window created in BrowserGlue.sys.mjs 96 window = Services.wm.getMostRecentWindow(null); 97 } 98 99 // On Fenix, we may not have any document to inspect when there is no tab 100 // opened, so return a fake document, just to ensure the ParentProcessWindowGlobalTarget 101 // actor has a functional document to operate with. 102 if (!window) { 103 const browser = Services.appShell.createWindowlessBrowser(false); 104 105 // Keep a strong reference to the document to keep it alive 106 this.headlessBrowser = browser; 107 108 // Create a document in order to avoid being on the initial about:blank document 109 // and have the document be ignored because its `isInitialDocument` attribute being true 110 const systemPrincipal = 111 Services.scriptSecurityManager.getSystemPrincipal(); 112 browser.docShell.createAboutBlankDocumentViewer( 113 systemPrincipal, 114 systemPrincipal 115 ); 116 117 window = browser.docShell.domWindow; 118 119 // Set some content to be shown in the inspector 120 window.document.body.textContent = 121 "Fake DevTools document, as there is no tab opened yet"; 122 } 123 124 return window.docShell; 125 } 126 127 /** 128 * Getter for the list of all docshells in this targetActor 129 * 130 * @return {Array} 131 */ 132 get docShells() { 133 // Iterate over all top-level windows and all their docshells. 134 let docShells = []; 135 for (const { docShell } of Services.ww.getWindowEnumerator()) { 136 docShells = docShells.concat(getChildDocShells(docShell)); 137 } 138 139 return docShells; 140 } 141 142 observe(subject, topic, data) { 143 super.observe(subject, topic, data); 144 if (this.isDestroyed()) { 145 return; 146 } 147 148 subject.QueryInterface(Ci.nsIDocShell); 149 150 if (topic == "chrome-webnavigation-create") { 151 this._onDocShellCreated(subject); 152 } else if (topic == "chrome-webnavigation-destroy") { 153 this._onDocShellDestroy(subject); 154 } 155 } 156 157 _detach() { 158 if (this.isDestroyed()) { 159 return false; 160 } 161 162 Services.obs.removeObserver(this, "chrome-webnavigation-create"); 163 Services.obs.removeObserver(this, "chrome-webnavigation-destroy"); 164 165 // Iterate over all top-level windows. 166 for (const { docShell } of Services.ww.getWindowEnumerator()) { 167 if (docShell == this.docShell) { 168 continue; 169 } 170 this._progressListener.unwatch(docShell); 171 } 172 173 this.headlessBrowser = null; 174 175 return super._detach(); 176 } 177 } 178 179 exports.ParentProcessTargetActor = ParentProcessTargetActor;