diff options
Diffstat (limited to 'devtools/server/actors/watcher')
-rw-r--r-- | devtools/server/actors/watcher/ParentProcessWatcherRegistry.sys.mjs (renamed from devtools/server/actors/watcher/WatcherRegistry.sys.mjs) | 138 | ||||
-rw-r--r-- | devtools/server/actors/watcher/SessionDataHelpers.sys.mjs (renamed from devtools/server/actors/watcher/SessionDataHelpers.jsm) | 62 | ||||
-rw-r--r-- | devtools/server/actors/watcher/browsing-context-helpers.sys.mjs | 2 | ||||
-rw-r--r-- | devtools/server/actors/watcher/moz.build | 8 | ||||
-rw-r--r-- | devtools/server/actors/watcher/target-helpers/content-process-jsprocessactor-startup.js | 26 | ||||
-rw-r--r-- | devtools/server/actors/watcher/target-helpers/frame-helper.js | 330 | ||||
-rw-r--r-- | devtools/server/actors/watcher/target-helpers/moz.build | 14 | ||||
-rw-r--r-- | devtools/server/actors/watcher/target-helpers/process-helper.js | 115 | ||||
-rw-r--r-- | devtools/server/actors/watcher/target-helpers/service-worker-helper.js | 220 | ||||
-rw-r--r-- | devtools/server/actors/watcher/target-helpers/service-worker-jsprocessactor-startup.js | 26 | ||||
-rw-r--r-- | devtools/server/actors/watcher/target-helpers/worker-helper.js | 137 |
11 files changed, 78 insertions, 1000 deletions
diff --git a/devtools/server/actors/watcher/WatcherRegistry.sys.mjs b/devtools/server/actors/watcher/ParentProcessWatcherRegistry.sys.mjs index ac8bc7f0c8..e9b3a9d50d 100644 --- a/devtools/server/actors/watcher/WatcherRegistry.sys.mjs +++ b/devtools/server/actors/watcher/ParentProcessWatcherRegistry.sys.mjs @@ -24,10 +24,9 @@ * while from the content process, we will read `sharedData` directly. */ -import { ActorManagerParent } from "resource://gre/modules/ActorManagerParent.sys.mjs"; - -const { SessionDataHelpers } = ChromeUtils.import( - "resource://devtools/server/actors/watcher/SessionDataHelpers.jsm" +const { SessionDataHelpers } = ChromeUtils.importESModule( + "resource://devtools/server/actors/watcher/SessionDataHelpers.sys.mjs", + { global: "contextual" } ); const { SUPPORTED_DATA } = SessionDataHelpers; @@ -68,7 +67,7 @@ function persistMapToSharedData() { Services.ppmm.sharedData.flush(); } -export const WatcherRegistry = { +export const ParentProcessWatcherRegistry = { /** * Tells if a given watcher currently watches for a given target type. * @@ -178,13 +177,16 @@ export const WatcherRegistry = { updateType ); + // Flush sharedData before registering the JS Actors as it is used + // during their instantiation. + persistMapToSharedData(); + // Register the JS Window Actor the first time we start watching for something (e.g. resource, target, …). - registerJSWindowActor(); - if (sessionData?.targets?.includes("process")) { + if (watcher.sessionContext.type == "all") { + registerBrowserToolboxJSProcessActor(); + } else { registerJSProcessActor(); } - - persistMapToSharedData(); }, /** @@ -245,9 +247,9 @@ export const WatcherRegistry = { * if we remove all entries. But we aren't removing all breakpoints. * So here, we force clearing any reference to the watcher actor when it destroys. */ - unregisterWatcher(watcher) { - sessionDataByWatcherActor.delete(watcher.actorID); - watcherActors.delete(watcher.actorID); + unregisterWatcher(watcherActorID) { + sessionDataByWatcherActor.delete(watcherActorID); + watcherActors.delete(watcherActorID); this.maybeUnregisterJSActors(); }, @@ -256,7 +258,7 @@ export const WatcherRegistry = { */ maybeUnregisterJSActors() { if (sessionDataByWatcherActor.size == 0) { - unregisterJSWindowActor(); + unregisterBrowserToolboxJSProcessActor(); unregisterJSProcessActor(); } }, @@ -334,74 +336,9 @@ export const WatcherRegistry = { }, }; -// Boolean flag to know if the DevToolsFrame JS Window Actor is currently registered -let isJSWindowActorRegistered = false; - -/** - * Register the JSWindowActor pair "DevToolsFrame". - * - * We should call this method before we try to use this JS Window Actor from the parent process - * (via `WindowGlobal.getActor("DevToolsFrame")` or `WindowGlobal.getActor("DevToolsWorker")`). - * Also, registering it will automatically force spawing the content process JSWindow Actor - * anytime a new document is opened (via DOMWindowCreated event). - */ - -const JSWindowActorsConfig = { - DevToolsFrame: { - parent: { - esModuleURI: - "resource://devtools/server/connectors/js-window-actor/DevToolsFrameParent.sys.mjs", - }, - child: { - esModuleURI: - "resource://devtools/server/connectors/js-window-actor/DevToolsFrameChild.sys.mjs", - events: { - DOMWindowCreated: {}, - DOMDocElementInserted: {}, - pageshow: {}, - pagehide: {}, - }, - }, - allFrames: true, - }, - DevToolsWorker: { - parent: { - esModuleURI: - "resource://devtools/server/connectors/js-window-actor/DevToolsWorkerParent.sys.mjs", - }, - child: { - esModuleURI: - "resource://devtools/server/connectors/js-window-actor/DevToolsWorkerChild.sys.mjs", - events: { - DOMWindowCreated: {}, - }, - }, - allFrames: true, - }, -}; - -function registerJSWindowActor() { - if (isJSWindowActorRegistered) { - return; - } - isJSWindowActorRegistered = true; - ActorManagerParent.addJSWindowActors(JSWindowActorsConfig); -} - -function unregisterJSWindowActor() { - if (!isJSWindowActorRegistered) { - return; - } - isJSWindowActorRegistered = false; - - for (const JSWindowActorName of Object.keys(JSWindowActorsConfig)) { - // ActorManagerParent doesn't expose a "removeActors" method, but it would be equivalent to that: - ChromeUtils.unregisterWindowActor(JSWindowActorName); - } -} - // Boolean flag to know if the DevToolsProcess JS Process Actor is currently registered let isJSProcessActorRegistered = false; +let isBrowserToolboxJSProcessActorRegistered = false; const JSProcessActorConfig = { parent: { @@ -419,7 +356,11 @@ const JSProcessActorConfig = { // The parent process is handled very differently from content processes // This uses the ParentProcessTarget which inherits from BrowsingContextTarget // and is, for now, manually created by the descriptor as the top level target. - includeParent: false, + includeParent: true, +}; + +const BrowserToolboxJSProcessActorConfig = { + ...JSProcessActorConfig, // This JS Process Actor is used to bootstrap DevTools code debugging the privileged code // in content processes. The privileged code runs in the "shared JSM global" (See mozJSModuleLoader). @@ -432,7 +373,7 @@ const JSProcessActorConfig = { }; const PROCESS_SCRIPT_URL = - "resource://devtools/server/actors/watcher/target-helpers/content-process-jsprocessactor-startup.js"; + "resource://devtools/server/connectors/js-process-actor/content-process-jsprocessactor-startup.js"; function registerJSProcessActor() { if (isJSProcessActorRegistered) { @@ -447,6 +388,22 @@ function registerJSProcessActor() { Services.ppmm.loadProcessScript(PROCESS_SCRIPT_URL, true); } +function registerBrowserToolboxJSProcessActor() { + if (isBrowserToolboxJSProcessActorRegistered) { + return; + } + isBrowserToolboxJSProcessActorRegistered = true; + ChromeUtils.registerProcessActor( + "BrowserToolboxDevToolsProcess", + BrowserToolboxJSProcessActorConfig + ); + + // There is no good observer service notification we can listen to to instantiate the JSProcess Actor + // as soon as the process start. + // So manually spawn our JSProcessActor from a process script emitting a custom observer service notification... + Services.ppmm.loadProcessScript(PROCESS_SCRIPT_URL, true); +} + function unregisterJSProcessActor() { if (!isJSProcessActorRegistered) { return; @@ -457,5 +414,24 @@ function unregisterJSProcessActor() { } catch (e) { // If any pending query was still ongoing, this would throw } + if (isBrowserToolboxJSProcessActorRegistered) { + return; + } + Services.ppmm.removeDelayedProcessScript(PROCESS_SCRIPT_URL); +} + +function unregisterBrowserToolboxJSProcessActor() { + if (!isBrowserToolboxJSProcessActorRegistered) { + return; + } + isBrowserToolboxJSProcessActorRegistered = false; + try { + ChromeUtils.unregisterProcessActor("BrowserToolboxDevToolsProcess"); + } catch (e) { + // If any pending query was still ongoing, this would throw + } + if (isJSProcessActorRegistered) { + return; + } Services.ppmm.removeDelayedProcessScript(PROCESS_SCRIPT_URL); } diff --git a/devtools/server/actors/watcher/SessionDataHelpers.jsm b/devtools/server/actors/watcher/SessionDataHelpers.sys.mjs index c70df1744f..def31b77a8 100644 --- a/devtools/server/actors/watcher/SessionDataHelpers.jsm +++ b/devtools/server/actors/watcher/SessionDataHelpers.sys.mjs @@ -2,49 +2,30 @@ * 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"; - /** - * Helper module alongside WatcherRegistry, which focus on updating the "sessionData" object. + * Helper module alongside ParentProcessWatcherRegistry, which focus on updating the "sessionData" object. * This object is shared across processes and threads and have to be maintained in all these runtimes. */ -var EXPORTED_SYMBOLS = ["SessionDataHelpers"]; - const lazy = {}; +ChromeUtils.defineESModuleGetters( + lazy, + { + validateBreakpointLocation: + "resource://devtools/shared/validate-breakpoint.sys.mjs", + }, + { global: "contextual" } +); -if (typeof module == "object") { - // Allow this JSM to also be loaded as a CommonJS module - // Because this module is used from the worker thread, - // (via target-actor-mixin), and workers can't load JSMs via ChromeUtils.import. - loader.lazyRequireGetter( - lazy, - "validateBreakpointLocation", - "resource://devtools/shared/validate-breakpoint.jsm", - true +ChromeUtils.defineLazyGetter(lazy, "validateEventBreakpoint", () => { + const { loader } = ChromeUtils.importESModule( + "resource://devtools/shared/loader/Loader.sys.mjs", + { global: "contextual" } ); - - loader.lazyRequireGetter( - lazy, - "validateEventBreakpoint", - "resource://devtools/server/actors/utils/event-breakpoints.js", - true - ); -} else { - ChromeUtils.defineLazyGetter(lazy, "validateBreakpointLocation", () => { - return ChromeUtils.import( - "resource://devtools/shared/validate-breakpoint.jsm" - ).validateBreakpointLocation; - }); - ChromeUtils.defineLazyGetter(lazy, "validateEventBreakpoint", () => { - const { loader } = ChromeUtils.importESModule( - "resource://devtools/shared/loader/Loader.sys.mjs" - ); - return loader.require( - "resource://devtools/server/actors/utils/event-breakpoints.js" - ).validateEventBreakpoint; - }); -} + return loader.require( + "resource://devtools/server/actors/utils/event-breakpoints.js" + ).validateEventBreakpoint; +}); // List of all arrays stored in `sessionData`, which are replicated across processes and threads const SUPPORTED_DATA = { @@ -151,7 +132,7 @@ function idFunction(v) { return v; } -const SessionDataHelpers = { +export const SessionDataHelpers = { SUPPORTED_DATA, /** @@ -235,10 +216,3 @@ const SessionDataHelpers = { return true; }, }; - -// Allow this JSM to also be loaded as a CommonJS module -// Because this module is used from the worker thread, -// (via target-actor-mixin), and workers can't load JSMs. -if (typeof module == "object") { - module.exports.SessionDataHelpers = SessionDataHelpers; -} diff --git a/devtools/server/actors/watcher/browsing-context-helpers.sys.mjs b/devtools/server/actors/watcher/browsing-context-helpers.sys.mjs index d52cbc5708..cd34c75760 100644 --- a/devtools/server/actors/watcher/browsing-context-helpers.sys.mjs +++ b/devtools/server/actors/watcher/browsing-context-helpers.sys.mjs @@ -382,7 +382,7 @@ export function getAllBrowsingContextsForContext( sessionContext.browserId ); // topBrowsingContext can be null if getCurrentTopByBrowserId is called for a tab that is unloaded. - if (topBrowsingContext) { + if (topBrowsingContext?.embedderElement) { // Unfortunately, getCurrentTopByBrowserId is subject to race conditions and may refer to a BrowsingContext // that already navigated away. // Query the current "live" BrowsingContext by going through the embedder element (i.e. the <browser>/<iframe> element) diff --git a/devtools/server/actors/watcher/moz.build b/devtools/server/actors/watcher/moz.build index 46a9d89718..47d08e8780 100644 --- a/devtools/server/actors/watcher/moz.build +++ b/devtools/server/actors/watcher/moz.build @@ -4,13 +4,9 @@ # 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/. -DIRS += [ - "target-helpers", -] - DevToolsModules( "browsing-context-helpers.sys.mjs", + "ParentProcessWatcherRegistry.sys.mjs", "session-context.js", - "SessionDataHelpers.jsm", - "WatcherRegistry.sys.mjs", + "SessionDataHelpers.sys.mjs", ) diff --git a/devtools/server/actors/watcher/target-helpers/content-process-jsprocessactor-startup.js b/devtools/server/actors/watcher/target-helpers/content-process-jsprocessactor-startup.js deleted file mode 100644 index 1765bcc66c..0000000000 --- a/devtools/server/actors/watcher/target-helpers/content-process-jsprocessactor-startup.js +++ /dev/null @@ -1,26 +0,0 @@ -/* 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 { setTimeout } = ChromeUtils.importESModule( - "resource://gre/modules/Timer.sys.mjs" -); - -/* - We can't spawn the JSProcessActor right away and have to spin the event loop. - Otherwise it isn't registered yet and isn't listening to observer service. - Could it be the reason why JSProcessActor aren't spawn via process actor option's child.observers notifications ?? -*/ -setTimeout(function () { - /* - This notification is registered in DevToolsServiceWorker JS process actor's options's `observers` attribute - and will force the JS Process actor to be instantiated in all processes. - */ - Services.obs.notifyObservers(null, "init-devtools-content-process-actor"); - /* - Instead of using observer service, we could also manually call some method of the actor: - ChromeUtils.domProcessChild.getActor("DevToolsProcess").observe(null, "foo"); - */ -}, 0); diff --git a/devtools/server/actors/watcher/target-helpers/frame-helper.js b/devtools/server/actors/watcher/target-helpers/frame-helper.js deleted file mode 100644 index 18d4d8f92e..0000000000 --- a/devtools/server/actors/watcher/target-helpers/frame-helper.js +++ /dev/null @@ -1,330 +0,0 @@ -/* 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 { WatcherRegistry } = ChromeUtils.importESModule( - "resource://devtools/server/actors/watcher/WatcherRegistry.sys.mjs", - // WatcherRegistry needs to be a true singleton and loads ActorManagerParent - // which also has to be a true singleton. - { global: "shared" } -); -const { WindowGlobalLogger } = ChromeUtils.importESModule( - "resource://devtools/server/connectors/js-window-actor/WindowGlobalLogger.sys.mjs", - { global: "contextual" } -); -const Targets = require("resource://devtools/server/actors/targets/index.js"); - -const browsingContextAttachedObserverByWatcher = new Map(); - -/** - * Force creating targets for all existing BrowsingContext, that, for a given Watcher Actor. - * - * @param WatcherActor watcher - * The Watcher Actor requesting to watch for new targets. - */ -async function createTargets(watcher) { - // Go over all existing BrowsingContext in order to: - // - Force the instantiation of a DevToolsFrameChild - // - Have the DevToolsFrameChild to spawn the WindowGlobalTargetActor - - // If we have a browserElement, set the watchedByDevTools flag on its related browsing context - // TODO: We should also set the flag for the "parent process" browsing context when we're - // in the browser toolbox. This is blocked by Bug 1675763, and should be handled as part - // of Bug 1709529. - if (watcher.sessionContext.type == "browser-element") { - // The `watchedByDevTools` enables gecko behavior tied to this flag, such as: - // - reporting the contents of HTML loaded in the docshells - // - capturing stacks for the network monitor. - watcher.browserElement.browsingContext.watchedByDevTools = true; - } - - if (!browsingContextAttachedObserverByWatcher.has(watcher)) { - // We store the browserId here as watcher.browserElement.browserId can momentary be - // set to 0 when there's a navigation to a new browsing context. - const browserId = watcher.sessionContext.browserId; - const onBrowsingContextAttached = browsingContext => { - // We want to set watchedByDevTools on new top-level browsing contexts: - // - in the case of the BrowserToolbox/BrowserConsole, that would be the browsing - // contexts of all the tabs we want to handle. - // - for the regular toolbox, browsing context that are being created when navigating - // to a page that forces a new browsing context. - // Then BrowsingContext will propagate to all the tree of children BrowsingContext's. - if ( - !browsingContext.parent && - (watcher.sessionContext.type != "browser-element" || - browserId === browsingContext.browserId) - ) { - browsingContext.watchedByDevTools = true; - } - }; - Services.obs.addObserver( - onBrowsingContextAttached, - "browsing-context-attached" - ); - // We store the observer so we can retrieve it elsewhere (e.g. for removal in destroyTargets). - browsingContextAttachedObserverByWatcher.set( - watcher, - onBrowsingContextAttached - ); - } - - if ( - watcher.sessionContext.isServerTargetSwitchingEnabled && - watcher.sessionContext.type == "browser-element" - ) { - // If server side target switching is enabled, process the top level browsing context first, - // so that we guarantee it is notified to the client first. - // If it is disabled, the top level target will be created from the client instead. - await createTargetForBrowsingContext({ - watcher, - browsingContext: watcher.browserElement.browsingContext, - retryOnAbortError: true, - }); - } - - const browsingContexts = watcher.getAllBrowsingContexts().filter( - // Filter out the top browsing context we just processed. - browsingContext => - browsingContext != watcher.browserElement?.browsingContext - ); - // Await for the all the queries in order to resolve only *after* we received all - // already available targets. - // i.e. each call to `createTargetForBrowsingContext` should end up emitting - // a target-available-form event via the WatcherActor. - await Promise.allSettled( - browsingContexts.map(browsingContext => - createTargetForBrowsingContext({ watcher, browsingContext }) - ) - ); -} - -/** - * (internal helper method) Force creating the target actor for a given BrowsingContext. - * - * @param WatcherActor watcher - * The Watcher Actor requesting to watch for new targets. - * @param BrowsingContext browsingContext - * The context for which a target should be created. - * @param Boolean retryOnAbortError - * Set to true to retry creating existing targets when receiving an AbortError. - * An AbortError is sent when the JSWindowActor pair was destroyed before the query - * was complete, which can happen if the document navigates while the query is pending. - */ -async function createTargetForBrowsingContext({ - watcher, - browsingContext, - retryOnAbortError = false, -}) { - logWindowGlobal(browsingContext.currentWindowGlobal, "Existing WindowGlobal"); - - // We need to set the watchedByDevTools flag on all top-level browsing context. In the - // case of a content toolbox, this is done in the tab descriptor, but when we're in the - // browser toolbox, such descriptor is not created. - // Then BrowsingContext will propagate to all the tree of children BbrowsingContext's. - if (!browsingContext.parent) { - browsingContext.watchedByDevTools = true; - } - - try { - await browsingContext.currentWindowGlobal - .getActor("DevToolsFrame") - .instantiateTarget({ - watcherActorID: watcher.actorID, - connectionPrefix: watcher.conn.prefix, - sessionContext: watcher.sessionContext, - sessionData: watcher.sessionData, - }); - } catch (e) { - console.warn( - "Failed to create DevTools Frame target for browsingContext", - browsingContext.id, - ": ", - e, - retryOnAbortError ? "retrying" : "" - ); - if (retryOnAbortError && e.name === "AbortError") { - await createTargetForBrowsingContext({ - watcher, - browsingContext, - retryOnAbortError, - }); - } else { - throw e; - } - } -} - -/** - * Force destroying all BrowsingContext targets which were related to a given watcher. - * - * @param WatcherActor watcher - * The Watcher Actor requesting to stop watching for new targets. - * @param {object} options - * @param {boolean} options.isModeSwitching - * true when this is called as the result of a change to the devtools.browsertoolbox.scope pref - */ -function destroyTargets(watcher, options) { - // Go over all existing BrowsingContext in order to destroy all targets - const browsingContexts = watcher.getAllBrowsingContexts(); - - for (const browsingContext of browsingContexts) { - logWindowGlobal( - browsingContext.currentWindowGlobal, - "Existing WindowGlobal" - ); - - if (!browsingContext.parent) { - browsingContext.watchedByDevTools = false; - } - - browsingContext.currentWindowGlobal - .getActor("DevToolsFrame") - .destroyTarget({ - watcherActorID: watcher.actorID, - sessionContext: watcher.sessionContext, - options, - }); - } - - if (watcher.sessionContext.type == "browser-element") { - watcher.browserElement.browsingContext.watchedByDevTools = false; - } - - if (browsingContextAttachedObserverByWatcher.has(watcher)) { - Services.obs.removeObserver( - browsingContextAttachedObserverByWatcher.get(watcher), - "browsing-context-attached" - ); - browsingContextAttachedObserverByWatcher.delete(watcher); - } -} - -/** - * Go over all existing BrowsingContext in order to communicate about new data entries - * - * @param WatcherActor watcher - * The Watcher Actor requesting to stop watching for new targets. - * @param string type - * The type of data to be added - * @param Array<Object> entries - * The values to be added to this type of data - * @param String updateType - * "add" will only add the new entries in the existing data set. - * "set" will update the data set with the new entries. - */ -async function addOrSetSessionDataEntry({ - watcher, - type, - entries, - updateType, -}) { - const browsingContexts = getWatchingBrowsingContexts(watcher); - const promises = []; - for (const browsingContext of browsingContexts) { - logWindowGlobal( - browsingContext.currentWindowGlobal, - "Existing WindowGlobal" - ); - - const promise = browsingContext.currentWindowGlobal - .getActor("DevToolsFrame") - .addOrSetSessionDataEntry({ - watcherActorID: watcher.actorID, - sessionContext: watcher.sessionContext, - type, - entries, - updateType, - }); - promises.push(promise); - } - // Await for the queries in order to try to resolve only *after* the remote code processed the new data - return Promise.all(promises); -} - -/** - * Notify all existing frame targets that some data entries have been removed - * - * See addOrSetSessionDataEntry for argument documentation. - */ -function removeSessionDataEntry({ watcher, type, entries }) { - const browsingContexts = getWatchingBrowsingContexts(watcher); - for (const browsingContext of browsingContexts) { - logWindowGlobal( - browsingContext.currentWindowGlobal, - "Existing WindowGlobal" - ); - - browsingContext.currentWindowGlobal - .getActor("DevToolsFrame") - .removeSessionDataEntry({ - watcherActorID: watcher.actorID, - sessionContext: watcher.sessionContext, - type, - entries, - }); - } -} - -module.exports = { - createTargets, - destroyTargets, - addOrSetSessionDataEntry, - removeSessionDataEntry, -}; - -/** - * Return the list of BrowsingContexts which should be targeted in order to communicate - * updated session data. - * - * @param WatcherActor watcher - * The watcher actor will be used to know which target we debug - * and what BrowsingContext should be considered. - */ -function getWatchingBrowsingContexts(watcher) { - // If we are watching for additional frame targets, it means that the multiprocess or fission mode is enabled, - // either for a content toolbox or a BrowserToolbox via scope set to everything. - const watchingAdditionalTargets = WatcherRegistry.isWatchingTargets( - watcher, - Targets.TYPES.FRAME - ); - if (watchingAdditionalTargets) { - return watcher.getAllBrowsingContexts(); - } - // By default, when we are no longer watching for frame targets, we should no longer try to - // communicate with any browsing-context. But. - // - // For "browser-element" debugging, all targets are provided by watching by watching for frame targets. - // So, when we are no longer watching for frame, we don't expect to have any frame target to talk to. - // => we should no longer reach any browsing context. - // - // For "all" (=browser toolbox), there is only the special ParentProcessTargetActor we might want to return here. - // But this is actually handled by the WatcherActor which uses `WatcherActor.getTargetActorInParentProcess` to convey session data. - // => we should no longer reach any browsing context. - // - // For "webextension" debugging, there is the special WebExtensionTargetActor, which doesn't run in the parent process, - // so that we can't rely on the same code as the browser toolbox. - // => we should always reach out this particular browsing context. - if (watcher.sessionContext.type == "webextension") { - const browsingContext = BrowsingContext.get( - watcher.sessionContext.addonBrowsingContextID - ); - // The add-on browsing context may be destroying, in which case we shouldn't try to communicate with it - if (browsingContext.currentWindowGlobal) { - return [browsingContext]; - } - } - return []; -} - -// Set to true to log info about about WindowGlobal's being watched. -const DEBUG = false; - -function logWindowGlobal(windowGlobal, message) { - if (!DEBUG) { - return; - } - - WindowGlobalLogger.logWindowGlobal(windowGlobal, message); -} diff --git a/devtools/server/actors/watcher/target-helpers/moz.build b/devtools/server/actors/watcher/target-helpers/moz.build deleted file mode 100644 index 3b00f0ef47..0000000000 --- a/devtools/server/actors/watcher/target-helpers/moz.build +++ /dev/null @@ -1,14 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -DevToolsModules( - "content-process-jsprocessactor-startup.js", - "frame-helper.js", - "process-helper.js", - "service-worker-helper.js", - "service-worker-jsprocessactor-startup.js", - "worker-helper.js", -) diff --git a/devtools/server/actors/watcher/target-helpers/process-helper.js b/devtools/server/actors/watcher/target-helpers/process-helper.js deleted file mode 100644 index e36f0a204c..0000000000 --- a/devtools/server/actors/watcher/target-helpers/process-helper.js +++ /dev/null @@ -1,115 +0,0 @@ -/* 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"; - -/** - * Return the list of all DOM Processes except the one for the parent process - * - * @return Array<nsIDOMProcessParent> - */ -function getAllContentProcesses() { - return ChromeUtils.getAllDOMProcesses().filter( - process => process.childID !== 0 - ); -} - -/** - * Instantiate all Content Process targets in all the DOM Processes. - * - * @param {WatcherActor} watcher - */ -async function createTargets(watcher) { - const promises = []; - for (const domProcess of getAllContentProcesses()) { - const processActor = domProcess.getActor("DevToolsProcess"); - promises.push( - processActor.instantiateTarget({ - watcherActorID: watcher.actorID, - connectionPrefix: watcher.conn.prefix, - sessionContext: watcher.sessionContext, - sessionData: watcher.sessionData, - }) - ); - } - await Promise.all(promises); -} - -/** - * @param {WatcherActor} watcher - * @param {object} options - * @param {boolean} options.isModeSwitching - * true when this is called as the result of a change to the devtools.browsertoolbox.scope pref - */ -function destroyTargets(watcher, options) { - for (const domProcess of getAllContentProcesses()) { - const processActor = domProcess.getActor("DevToolsProcess"); - processActor.destroyTarget({ - watcherActorID: watcher.actorID, - isModeSwitching: options.isModeSwitching, - }); - } -} - -/** - * Go over all existing content processes in order to communicate about new data entries - * - * @param {Object} options - * @param {WatcherActor} options.watcher - * The Watcher Actor providing new data entries - * @param {string} options.type - * The type of data to be added - * @param {Array<Object>} options.entries - * The values to be added to this type of data - * @param String updateType - * "add" will only add the new entries in the existing data set. - * "set" will update the data set with the new entries. - */ -async function addOrSetSessionDataEntry({ - watcher, - type, - entries, - updateType, -}) { - const promises = []; - for (const domProcess of getAllContentProcesses()) { - const processActor = domProcess.getActor("DevToolsProcess"); - promises.push( - processActor.addOrSetSessionDataEntry({ - watcherActorID: watcher.actorID, - type, - entries, - updateType, - }) - ); - } - await Promise.all(promises); -} - -/** - * Notify all existing content processes that some data entries have been removed - * - * See addOrSetSessionDataEntry for argument documentation. - */ -async function removeSessionDataEntry({ watcher, type, entries }) { - const promises = []; - for (const domProcess of getAllContentProcesses()) { - const processActor = domProcess.getActor("DevToolsProcess"); - promises.push( - processActor.removeSessionDataEntry({ - watcherActorID: watcher.actorID, - type, - entries, - }) - ); - } - await Promise.all(promises); -} - -module.exports = { - createTargets, - destroyTargets, - addOrSetSessionDataEntry, - removeSessionDataEntry, -}; diff --git a/devtools/server/actors/watcher/target-helpers/service-worker-helper.js b/devtools/server/actors/watcher/target-helpers/service-worker-helper.js deleted file mode 100644 index 53fceead17..0000000000 --- a/devtools/server/actors/watcher/target-helpers/service-worker-helper.js +++ /dev/null @@ -1,220 +0,0 @@ -/* 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 { waitForTick } = require("resource://devtools/shared/DevToolsUtils.js"); - -const PROCESS_SCRIPT_URL = - "resource://devtools/server/actors/watcher/target-helpers/service-worker-jsprocessactor-startup.js"; - -const PROCESS_ACTOR_NAME = "DevToolsServiceWorker"; -const PROCESS_ACTOR_OPTIONS = { - // Ignore the parent process. - includeParent: false, - - parent: { - esModuleURI: - "resource://devtools/server/connectors/process-actor/DevToolsServiceWorkerParent.sys.mjs", - }, - - child: { - esModuleURI: - "resource://devtools/server/connectors/process-actor/DevToolsServiceWorkerChild.sys.mjs", - - observers: [ - // Tried various notification to ensure starting the actor - // from webServiceWorker processes... but none of them worked. - /* - "chrome-event-target-created", - "webnavigation-create", - "chrome-webnavigation-create", - "webnavigation-destroy", - "chrome-webnavigation-destroy", - "browsing-context-did-set-embedder", - "browsing-context-discarded", - "ipc:content-initializing", - "ipc:content-created", - */ - - // Fallback on firing a very custom notification from a "process script" (loadProcessScript) - "init-devtools-service-worker-actor", - ], - }, -}; - -// List of all active watchers -const gWatchers = new Set(); - -/** - * Register the DevToolsServiceWorker JS Process Actor, - * if we are registering the first watcher actor. - * - * @param {Watcher Actor} watcher - */ -function maybeRegisterProcessActor(watcher) { - const sizeBefore = gWatchers.size; - gWatchers.add(watcher); - - if (sizeBefore == 0 && gWatchers.size == 1) { - ChromeUtils.registerProcessActor(PROCESS_ACTOR_NAME, PROCESS_ACTOR_OPTIONS); - - // For some reason JSProcessActor doesn't work out of the box for `webServiceWorker` content processes. - // So manually spawn our JSProcessActor from a process script emitting an observer service notification... - // The Process script are correctly executed on all process types during their early startup. - Services.ppmm.loadProcessScript(PROCESS_SCRIPT_URL, true); - } -} - -/** - * Unregister the DevToolsServiceWorker JS Process Actor, - * if we are unregistering the last watcher actor. - * - * @param {Watcher Actor} watcher - */ -function maybeUnregisterProcessActor(watcher) { - const sizeBefore = gWatchers.size; - gWatchers.delete(watcher); - - if (sizeBefore == 1 && gWatchers.size == 0) { - ChromeUtils.unregisterProcessActor( - PROCESS_ACTOR_NAME, - PROCESS_ACTOR_OPTIONS - ); - - Services.ppmm.removeDelayedProcessScript(PROCESS_SCRIPT_URL); - } -} - -/** - * Return the list of all DOM Processes except the one for the parent process - * - * @return Array<nsIDOMProcessParent> - */ -function getAllContentProcesses() { - return ChromeUtils.getAllDOMProcesses().filter( - process => process.childID !== 0 - ); -} - -/** - * Force creating targets for all existing service workers for a given Watcher Actor. - * - * @param WatcherActor watcher - * The Watcher Actor requesting to watch for new targets. - */ -async function createTargets(watcher) { - maybeRegisterProcessActor(watcher); - // Go over all existing content process in order to: - // - Force the instantiation of a DevToolsServiceWorkerChild - // - Have the DevToolsServiceWorkerChild to spawn the WorkerTargetActors - - const promises = []; - for (const process of getAllContentProcesses()) { - const promise = process - .getActor(PROCESS_ACTOR_NAME) - .instantiateServiceWorkerTargets({ - watcherActorID: watcher.actorID, - connectionPrefix: watcher.conn.prefix, - sessionContext: watcher.sessionContext, - sessionData: watcher.sessionData, - }); - promises.push(promise); - } - - // Await for the different queries in order to try to resolve only *after* we received - // the already available worker targets. - return Promise.all(promises); -} - -/** - * Force destroying all worker targets which were related to a given watcher. - * - * @param WatcherActor watcher - * The Watcher Actor requesting to stop watching for new targets. - */ -async function destroyTargets(watcher) { - // Go over all existing content processes in order to destroy all targets - for (const process of getAllContentProcesses()) { - let processActor; - try { - processActor = process.getActor(PROCESS_ACTOR_NAME); - } catch (e) { - // Ignore any exception during destroy as we may be closing firefox/devtools/tab - // and that can easily lead to many exceptions. - continue; - } - - processActor.destroyServiceWorkerTargets({ - watcherActorID: watcher.actorID, - sessionContext: watcher.sessionContext, - }); - } - - // browser_dbg-breakpoints-columns.js crashes if we unregister the Process Actor - // in the same event loop as we call destroyServiceWorkerTargets. - await waitForTick(); - - maybeUnregisterProcessActor(watcher); -} - -/** - * Go over all existing JSProcessActor in order to communicate about new data entries - * - * @param WatcherActor watcher - * The Watcher Actor requesting to update data entries. - * @param string type - * The type of data to be added - * @param Array<Object> entries - * The values to be added to this type of data - * @param String updateType - * "add" will only add the new entries in the existing data set. - * "set" will update the data set with the new entries. - */ -async function addOrSetSessionDataEntry({ - watcher, - type, - entries, - updateType, -}) { - maybeRegisterProcessActor(watcher); - const promises = []; - for (const process of getAllContentProcesses()) { - const promise = process - .getActor(PROCESS_ACTOR_NAME) - .addOrSetSessionDataEntry({ - watcherActorID: watcher.actorID, - sessionContext: watcher.sessionContext, - type, - entries, - updateType, - }); - promises.push(promise); - } - // Await for the queries in order to try to resolve only *after* the remote code processed the new data - return Promise.all(promises); -} - -/** - * Notify all existing frame targets that some data entries have been removed - * - * See addOrSetSessionDataEntry for argument documentation. - */ -function removeSessionDataEntry({ watcher, type, entries }) { - for (const process of getAllContentProcesses()) { - process.getActor(PROCESS_ACTOR_NAME).removeSessionDataEntry({ - watcherActorID: watcher.actorID, - sessionContext: watcher.sessionContext, - type, - entries, - }); - } -} - -module.exports = { - createTargets, - destroyTargets, - addOrSetSessionDataEntry, - removeSessionDataEntry, -}; diff --git a/devtools/server/actors/watcher/target-helpers/service-worker-jsprocessactor-startup.js b/devtools/server/actors/watcher/target-helpers/service-worker-jsprocessactor-startup.js deleted file mode 100644 index 03f042ad68..0000000000 --- a/devtools/server/actors/watcher/target-helpers/service-worker-jsprocessactor-startup.js +++ /dev/null @@ -1,26 +0,0 @@ -/* 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 { setTimeout } = ChromeUtils.importESModule( - "resource://gre/modules/Timer.sys.mjs" -); - -/* - We can't spawn the JSProcessActor right away and have to spin the event loop. - Otherwise it isn't registered yet and isn't listening to observer service. - Could it be the reason why JSProcessActor aren't spawn via process actor option's child.observers notifications ?? -*/ -setTimeout(function () { - /* - This notification is registered in DevToolsServiceWorker JS process actor's options's `observers` attribute - and will force the JS Process actor to be instantiated in all processes. - */ - Services.obs.notifyObservers(null, "init-devtools-service-worker-actor"); - /* - Instead of using observer service, we could also manually call some method of the actor: - ChromeUtils.domProcessChild.getActor("DevToolsServiceWorker").observe(null, "foo"); - */ -}, 0); diff --git a/devtools/server/actors/watcher/target-helpers/worker-helper.js b/devtools/server/actors/watcher/target-helpers/worker-helper.js deleted file mode 100644 index 671d1dc706..0000000000 --- a/devtools/server/actors/watcher/target-helpers/worker-helper.js +++ /dev/null @@ -1,137 +0,0 @@ -/* 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 DEVTOOLS_WORKER_JS_WINDOW_ACTOR_NAME = "DevToolsWorker"; - -/** - * Force creating targets for all existing workers for a given Watcher Actor. - * - * @param WatcherActor watcher - * The Watcher Actor requesting to watch for new targets. - */ -async function createTargets(watcher) { - // Go over all existing BrowsingContext in order to: - // - Force the instantiation of a DevToolsWorkerChild - // - Have the DevToolsWorkerChild to spawn the WorkerTargetActors - const browsingContexts = watcher.getAllBrowsingContexts({ - acceptSameProcessIframes: true, - forceAcceptTopLevelTarget: true, - }); - const promises = []; - for (const browsingContext of browsingContexts) { - const promise = browsingContext.currentWindowGlobal - .getActor(DEVTOOLS_WORKER_JS_WINDOW_ACTOR_NAME) - .instantiateWorkerTargets({ - watcherActorID: watcher.actorID, - connectionPrefix: watcher.conn.prefix, - sessionContext: watcher.sessionContext, - sessionData: watcher.sessionData, - }); - promises.push(promise); - } - - // Await for the different queries in order to try to resolve only *after* we received - // the already available worker targets. - return Promise.all(promises); -} - -/** - * Force destroying all worker targets which were related to a given watcher. - * - * @param WatcherActor watcher - * The Watcher Actor requesting to stop watching for new targets. - */ -async function destroyTargets(watcher) { - // Go over all existing BrowsingContext in order to destroy all targets - const browsingContexts = watcher.getAllBrowsingContexts({ - acceptSameProcessIframes: true, - forceAcceptTopLevelTarget: true, - }); - for (const browsingContext of browsingContexts) { - let windowActor; - try { - windowActor = browsingContext.currentWindowGlobal.getActor( - DEVTOOLS_WORKER_JS_WINDOW_ACTOR_NAME - ); - } catch (e) { - continue; - } - - windowActor.destroyWorkerTargets({ - watcherActorID: watcher.actorID, - sessionContext: watcher.sessionContext, - }); - } -} - -/** - * Go over all existing BrowsingContext in order to communicate about new data entries - * - * @param WatcherActor watcher - * The Watcher Actor requesting to stop watching for new targets. - * @param string type - * The type of data to be added - * @param Array<Object> entries - * The values to be added to this type of data - * @param String updateType - * "add" will only add the new entries in the existing data set. - * "set" will update the data set with the new entries. - */ -async function addOrSetSessionDataEntry({ - watcher, - type, - entries, - updateType, -}) { - const browsingContexts = watcher.getAllBrowsingContexts({ - acceptSameProcessIframes: true, - forceAcceptTopLevelTarget: true, - }); - const promises = []; - for (const browsingContext of browsingContexts) { - const promise = browsingContext.currentWindowGlobal - .getActor(DEVTOOLS_WORKER_JS_WINDOW_ACTOR_NAME) - .addOrSetSessionDataEntry({ - watcherActorID: watcher.actorID, - sessionContext: watcher.sessionContext, - type, - entries, - updateType, - }); - promises.push(promise); - } - // Await for the queries in order to try to resolve only *after* the remote code processed the new data - return Promise.all(promises); -} - -/** - * Notify all existing frame targets that some data entries have been removed - * - * See addOrSetSessionDataEntry for argument documentation. - */ -function removeSessionDataEntry({ watcher, type, entries }) { - const browsingContexts = watcher.getAllBrowsingContexts({ - acceptSameProcessIframes: true, - forceAcceptTopLevelTarget: true, - }); - for (const browsingContext of browsingContexts) { - browsingContext.currentWindowGlobal - .getActor(DEVTOOLS_WORKER_JS_WINDOW_ACTOR_NAME) - .removeSessionDataEntry({ - watcherActorID: watcher.actorID, - sessionContext: watcher.sessionContext, - type, - entries, - }); - } -} - -module.exports = { - createTargets, - destroyTargets, - addOrSetSessionDataEntry, - removeSessionDataEntry, -}; |