watcher.js (7123B)
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 "use strict"; 5 6 const { watcherSpec } = require("resource://devtools/shared/specs/watcher.js"); 7 const { 8 FrontClassWithSpec, 9 registerFront, 10 } = require("resource://devtools/shared/protocol.js"); 11 12 loader.lazyRequireGetter( 13 this, 14 "WindowGlobalTargetFront", 15 "resource://devtools/client/fronts/targets/window-global.js", 16 true 17 ); 18 loader.lazyRequireGetter( 19 this, 20 "ContentProcessTargetFront", 21 "resource://devtools/client/fronts/targets/content-process.js", 22 true 23 ); 24 loader.lazyRequireGetter( 25 this, 26 "WorkerTargetFront", 27 "resource://devtools/client/fronts/targets/worker.js", 28 true 29 ); 30 loader.lazyRequireGetter( 31 this, 32 "ContentScriptTargetFront", 33 "resource://devtools/client/fronts/targets/content-script.js", 34 true 35 ); 36 37 class WatcherFront extends FrontClassWithSpec(watcherSpec) { 38 constructor(client, targetFront, parentFront) { 39 super(client, targetFront, parentFront); 40 41 this._onTargetAvailable = this._onTargetAvailable.bind(this); 42 this._onTargetDestroyed = this._onTargetDestroyed.bind(this); 43 44 // Convert form, which is just JSON object to Fronts for these two events 45 this.on("target-available-form", this._onTargetAvailable); 46 this.on("target-destroyed-form", this._onTargetDestroyed); 47 } 48 49 form(json) { 50 this.actorID = json.actor; 51 this.traits = json.traits; 52 } 53 54 _onTargetAvailable(form) { 55 let front; 56 if (form.actor.includes("/contentProcessTarget")) { 57 front = new ContentProcessTargetFront(this.conn, null, this); 58 } else if (form.actor.includes("/workerTarget")) { 59 front = new WorkerTargetFront(this.conn, null, this); 60 } else if (form.actor.includes("/contentScriptTarget")) { 61 front = new ContentScriptTargetFront(this.conn, null, this); 62 } else { 63 front = new WindowGlobalTargetFront(this.conn, null, this); 64 } 65 front.actorID = form.actor; 66 front.form(form); 67 this.manage(front); 68 this.emit("target-available", front); 69 } 70 71 _onTargetDestroyed(form, options = {}) { 72 const front = this._getTargetFront(form); 73 74 // When server side target switching is off, 75 // the watcher may notify us about the top level target destruction a bit late. 76 // The descriptor (`this.parentFront`) already switched to the new target. 77 // Missing `target-destroyed` isn't critical when target switching is off 78 // as `TargetCommand.switchToTarget` will end calling `TargetCommand.onTargetDestroyed` for all 79 // existing targets. 80 // https://searchfox.org/mozilla-central/rev/af8e5d37fd56be90ccddae2203e7b875d3f3ae87/devtools/shared/commands/target/target-command.js#166-173 81 if (front) { 82 this.emit("target-destroyed", front, options); 83 } 84 } 85 86 _getTargetFront(form) { 87 let front = this.getActorByID(form.actor); 88 // For top level target, the target will be a child of the descriptor front, 89 // which happens to be the parent front of the watcher. 90 if (!front) { 91 front = this.parentFront.getActorByID(form.actor); 92 } 93 return front; 94 } 95 96 /** 97 * Retrieve the already existing WindowGlobalTargetFront for the parent 98 * BrowsingContext of the given BrowsingContext ID. 99 */ 100 async getParentWindowGlobalTarget(browsingContextID) { 101 const id = await this.getParentBrowsingContextID(browsingContextID); 102 if (!id) { 103 return null; 104 } 105 return this.getWindowGlobalTarget(id); 106 } 107 108 /** 109 * Memoized getter for the "blackboxing" actor 110 */ 111 async getBlackboxingActor() { 112 if (!this._blackboxingActor) { 113 this._blackboxingActor = await super.getBlackboxingActor(); 114 } 115 return this._blackboxingActor; 116 } 117 /** 118 * Memoized getter for the "breakpoint-list" actor 119 */ 120 async getBreakpointListActor() { 121 if (!this._breakpointListActor) { 122 this._breakpointListActor = await super.getBreakpointListActor(); 123 } 124 return this._breakpointListActor; 125 } 126 127 /** 128 * Memoized getter for the "target-configuration" actor 129 */ 130 async getTargetConfigurationActor() { 131 if (!this._targetConfigurationActor) { 132 this._targetConfigurationActor = 133 await super.getTargetConfigurationActor(); 134 } 135 return this._targetConfigurationActor; 136 } 137 138 /** 139 * Memoized getter for the "thread-configuration" actor 140 */ 141 async getThreadConfigurationActor() { 142 if (!this._threadConfigurationActor) { 143 this._threadConfigurationActor = 144 await super.getThreadConfigurationActor(); 145 } 146 return this._threadConfigurationActor; 147 } 148 149 /** 150 * For a given BrowsingContext ID, return the already existing WindowGlobalTargetFront 151 */ 152 async getWindowGlobalTarget(id) { 153 // First scan the watcher children as the watcher manages all the targets 154 for (const front of this.poolChildren()) { 155 if (front.browsingContextID == id) { 156 return front; 157 } 158 } 159 // But the top level target will be created by the Descriptor.getTarget() method 160 // and so be hosted in the Descriptor's pool. 161 // The parent front of the WatcherActor happens to be the Descriptor Actor. 162 // This code could go away or be simplified if the Descriptor starts fetch all 163 // the targets, including the top level one via the Watcher. i.e. drop Descriptor.getTarget(). 164 const topLevelTarget = await this.parentFront.getTarget(); 165 if (topLevelTarget?.browsingContextID == id) { 166 return topLevelTarget; 167 } 168 169 // If we could not find a window global target for the provided id, the 170 // window global might not be the topmost one of a given process (isProcessRoot == true). 171 // For now we only create targets for the top window global of each process, 172 // so we recursively check the parent browsing context ids 173 // until we find a valid target. 174 const parentBrowsingContextID = await this.getParentBrowsingContextID(id); 175 if (parentBrowsingContextID && parentBrowsingContextID !== id) { 176 return this.getWindowGlobalTarget(parentBrowsingContextID); 177 } 178 179 return null; 180 } 181 182 getWindowGlobalTargetByInnerWindowId(innerWindowId) { 183 for (const front of this.poolChildren()) { 184 if (front.innerWindowId == innerWindowId) { 185 return front; 186 } 187 } 188 // Use getCachedTarget in order to have a fully synchronous method 189 // as the callsite in ResourceCommand benefit from being synchronous. 190 // Here we care only about already existing resource and do not need to 191 // wait for the next target to come. 192 const topLevelTarget = this.parentFront.getCachedTarget(); 193 if (topLevelTarget?.innerWindowId == innerWindowId) { 194 return topLevelTarget; 195 } 196 console.error("Unable to find target with innerWindowId:" + innerWindowId); 197 return null; 198 } 199 200 /** 201 * Memoized getter for the "networkParent" actor 202 */ 203 async getNetworkParentActor() { 204 if (!this._networkParentActor) { 205 this._networkParentActor = await super.getNetworkParentActor(); 206 } 207 return this._networkParentActor; 208 } 209 } 210 registerFront(WatcherFront);