diff options
Diffstat (limited to 'devtools/client/fronts/watcher.js')
-rw-r--r-- | devtools/client/fronts/watcher.js | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/devtools/client/fronts/watcher.js b/devtools/client/fronts/watcher.js new file mode 100644 index 0000000000..1a7499561a --- /dev/null +++ b/devtools/client/fronts/watcher.js @@ -0,0 +1,202 @@ +/* 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"; + +const { watcherSpec } = require("resource://devtools/shared/specs/watcher.js"); +const { + FrontClassWithSpec, + registerFront, +} = require("resource://devtools/shared/protocol.js"); + +loader.lazyRequireGetter( + this, + "WindowGlobalTargetFront", + "resource://devtools/client/fronts/targets/window-global.js", + true +); +loader.lazyRequireGetter( + this, + "ContentProcessTargetFront", + "resource://devtools/client/fronts/targets/content-process.js", + true +); +loader.lazyRequireGetter( + this, + "WorkerTargetFront", + "resource://devtools/client/fronts/targets/worker.js", + true +); + +class WatcherFront extends FrontClassWithSpec(watcherSpec) { + constructor(client, targetFront, parentFront) { + super(client, targetFront, parentFront); + + this._onTargetAvailable = this._onTargetAvailable.bind(this); + this._onTargetDestroyed = this._onTargetDestroyed.bind(this); + + // Convert form, which is just JSON object to Fronts for these two events + this.on("target-available-form", this._onTargetAvailable); + this.on("target-destroyed-form", this._onTargetDestroyed); + } + + form(json) { + this.actorID = json.actor; + this.traits = json.traits; + } + + _onTargetAvailable(form) { + let front; + if (form.actor.includes("/contentProcessTarget")) { + front = new ContentProcessTargetFront(this.conn, null, this); + } else if (form.actor.includes("/workerTarget")) { + front = new WorkerTargetFront(this.conn, null, this); + } else { + front = new WindowGlobalTargetFront(this.conn, null, this); + } + front.actorID = form.actor; + front.form(form); + this.manage(front); + this.emit("target-available", front); + } + + _onTargetDestroyed(form, options = {}) { + const front = this._getTargetFront(form); + + // When server side target switching is off, + // the watcher may notify us about the top level target destruction a bit late. + // The descriptor (`this.parentFront`) already switched to the new target. + // Missing `target-destroyed` isn't critical when target switching is off + // as `TargetCommand.switchToTarget` will end calling `TargetCommandonTargetDestroyed` for all + // existing targets. + // https://searchfox.org/mozilla-central/rev/af8e5d37fd56be90ccddae2203e7b875d3f3ae87/devtools/shared/commands/target/target-command.js#166-173 + if (front) { + this.emit("target-destroyed", front, options); + } + } + + _getTargetFront(form) { + let front = this.getActorByID(form.actor); + // For top level target, the target will be a child of the descriptor front, + // which happens to be the parent front of the watcher. + if (!front) { + front = this.parentFront.getActorByID(form.actor); + } + return front; + } + + /** + * Retrieve the already existing WindowGlobalTargetFront for the parent + * BrowsingContext of the given BrowsingContext ID. + */ + async getParentWindowGlobalTarget(browsingContextID) { + const id = await this.getParentBrowsingContextID(browsingContextID); + if (!id) { + return null; + } + return this.getWindowGlobalTarget(id); + } + + /** + * Memoized getter for the "blackboxing" actor + */ + async getBlackboxingActor() { + if (!this._blackboxingActor) { + this._blackboxingActor = await super.getBlackboxingActor(); + } + return this._blackboxingActor; + } + /** + * Memoized getter for the "breakpoint-list" actor + */ + async getBreakpointListActor() { + if (!this._breakpointListActor) { + this._breakpointListActor = await super.getBreakpointListActor(); + } + return this._breakpointListActor; + } + + /** + * Memoized getter for the "target-configuration" actor + */ + async getTargetConfigurationActor() { + if (!this._targetConfigurationActor) { + this._targetConfigurationActor = + await super.getTargetConfigurationActor(); + } + return this._targetConfigurationActor; + } + + /** + * Memoized getter for the "thread-configuration" actor + */ + async getThreadConfigurationActor() { + if (!this._threadConfigurationActor) { + this._threadConfigurationActor = + await super.getThreadConfigurationActor(); + } + return this._threadConfigurationActor; + } + + /** + * For a given BrowsingContext ID, return the already existing WindowGlobalTargetFront + */ + async getWindowGlobalTarget(id) { + // First scan the watcher children as the watcher manages all the targets + for (const front of this.poolChildren()) { + if (front.browsingContextID == id) { + return front; + } + } + // But the top level target will be created by the Descriptor.getTarget() method + // and so be hosted in the Descriptor's pool. + // The parent front of the WatcherActor happens to be the Descriptor Actor. + // This code could go away or be simplified if the Descriptor starts fetch all + // the targets, including the top level one via the Watcher. i.e. drop Descriptor.getTarget(). + const topLevelTarget = await this.parentFront.getTarget(); + if (topLevelTarget?.browsingContextID == id) { + return topLevelTarget; + } + + // If we could not find a window global target for the provided id, the + // window global might not be the topmost one of a given process (isProcessRoot == true). + // For now we only create targets for the top window global of each process, + // so we recursively check the parent browsing context ids + // until we find a valid target. + const parentBrowsingContextID = await this.getParentBrowsingContextID(id); + if (parentBrowsingContextID && parentBrowsingContextID !== id) { + return this.getWindowGlobalTarget(parentBrowsingContextID); + } + + return null; + } + + getWindowGlobalTargetByInnerWindowId(innerWindowId) { + for (const front of this.poolChildren()) { + if (front.innerWindowId == innerWindowId) { + return front; + } + } + // Use getCachedTarget in order to have a fully synchronous method + // as the callsite in ResourceCommand benefit from being synchronous. + // Here we care only about already existing resource and do not need to + // wait for the next target to come. + const topLevelTarget = this.parentFront.getCachedTarget(); + if (topLevelTarget?.innerWindowId == innerWindowId) { + return topLevelTarget; + } + console.error("Unable to find target with innerWindowId:" + innerWindowId); + return null; + } + + /** + * Memoized getter for the "networkParent" actor + */ + async getNetworkParentActor() { + if (!this._networkParentActor) { + this._networkParentActor = await super.getNetworkParentActor(); + } + return this._networkParentActor; + } +} +registerFront(WatcherFront); |