summaryrefslogtreecommitdiffstats
path: root/devtools/server/actors
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:35:49 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:35:49 +0000
commitd8bbc7858622b6d9c278469aab701ca0b609cddf (patch)
treeeff41dc61d9f714852212739e6b3738b82a2af87 /devtools/server/actors
parentReleasing progress-linux version 125.0.3-1~progress7.99u1. (diff)
downloadfirefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.tar.xz
firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.zip
Merging upstream version 126.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'devtools/server/actors')
-rw-r--r--devtools/server/actors/accessibility/audit/contrast.js16
-rw-r--r--devtools/server/actors/blackboxing.js7
-rw-r--r--devtools/server/actors/breakpoint-list.js7
-rw-r--r--devtools/server/actors/inspector/walker.js4
-rw-r--r--devtools/server/actors/page-style.js1
-rw-r--r--devtools/server/actors/resources/index.js16
-rw-r--r--devtools/server/actors/resources/jstracer-state.js15
-rw-r--r--devtools/server/actors/resources/network-events.js8
-rw-r--r--devtools/server/actors/resources/sources.js2
-rw-r--r--devtools/server/actors/resources/utils/parent-process-storage.js11
-rw-r--r--devtools/server/actors/style-rule.js37
-rw-r--r--devtools/server/actors/target-configuration.js12
-rw-r--r--devtools/server/actors/targets/base-target-actor.js12
-rw-r--r--devtools/server/actors/targets/session-data-processors/breakpoints.js4
-rw-r--r--devtools/server/actors/targets/session-data-processors/event-breakpoints.js7
-rw-r--r--devtools/server/actors/targets/session-data-processors/index.js7
-rw-r--r--devtools/server/actors/targets/session-data-processors/thread-configuration.js7
-rw-r--r--devtools/server/actors/targets/session-data-processors/xhr-breakpoints.js5
-rw-r--r--devtools/server/actors/targets/target-actor-registry.sys.mjs13
-rw-r--r--devtools/server/actors/targets/webextension.js6
-rw-r--r--devtools/server/actors/targets/window-global.js59
-rw-r--r--devtools/server/actors/targets/worker.js6
-rw-r--r--devtools/server/actors/thread-configuration.js7
-rw-r--r--devtools/server/actors/thread.js17
-rw-r--r--devtools/server/actors/tracer.js30
-rw-r--r--devtools/server/actors/utils/event-breakpoints.js15
-rw-r--r--devtools/server/actors/utils/style-utils.js66
-rw-r--r--devtools/server/actors/utils/stylesheets-manager.js31
-rw-r--r--devtools/server/actors/watcher.js353
-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.mjs2
-rw-r--r--devtools/server/actors/watcher/moz.build8
-rw-r--r--devtools/server/actors/watcher/target-helpers/content-process-jsprocessactor-startup.js26
-rw-r--r--devtools/server/actors/watcher/target-helpers/frame-helper.js330
-rw-r--r--devtools/server/actors/watcher/target-helpers/moz.build14
-rw-r--r--devtools/server/actors/watcher/target-helpers/process-helper.js115
-rw-r--r--devtools/server/actors/watcher/target-helpers/service-worker-helper.js220
-rw-r--r--devtools/server/actors/watcher/target-helpers/service-worker-jsprocessactor-startup.js26
-rw-r--r--devtools/server/actors/watcher/target-helpers/worker-helper.js137
-rw-r--r--devtools/server/actors/webconsole/commands/manager.js19
41 files changed, 491 insertions, 1387 deletions
diff --git a/devtools/server/actors/accessibility/audit/contrast.js b/devtools/server/actors/accessibility/audit/contrast.js
index 68e7b497f8..95510be4fc 100644
--- a/devtools/server/actors/accessibility/audit/contrast.js
+++ b/devtools/server/actors/accessibility/audit/contrast.js
@@ -42,15 +42,17 @@ loader.lazyRequireGetter(
);
loader.lazyRequireGetter(
this,
- "DevToolsWorker",
- "resource://devtools/shared/worker/worker.js",
- true
-);
-loader.lazyRequireGetter(
- this,
"InspectorActorUtils",
"resource://devtools/server/actors/inspector/utils.js"
);
+const lazy = {};
+ChromeUtils.defineESModuleGetters(
+ lazy,
+ {
+ DevToolsWorker: "resource://devtools/shared/worker/worker.sys.mjs",
+ },
+ { global: "contextual" }
+);
const WORKER_URL = "resource://devtools/server/actors/accessibility/worker.js";
const HIGHLIGHTED_PSEUDO_CLASS = ":-moz-devtools-highlighted";
@@ -58,7 +60,7 @@ const {
LARGE_TEXT: { BOLD_LARGE_TEXT_MIN_PIXELS, LARGE_TEXT_MIN_PIXELS },
} = require("resource://devtools/shared/accessibility.js");
-loader.lazyGetter(this, "worker", () => new DevToolsWorker(WORKER_URL));
+loader.lazyGetter(this, "worker", () => new lazy.DevToolsWorker(WORKER_URL));
/**
* Get canvas rendering context for the current target window bound by the bounds of the
diff --git a/devtools/server/actors/blackboxing.js b/devtools/server/actors/blackboxing.js
index 49dfc8180d..8163327b46 100644
--- a/devtools/server/actors/blackboxing.js
+++ b/devtools/server/actors/blackboxing.js
@@ -9,9 +9,10 @@ const {
blackboxingSpec,
} = require("resource://devtools/shared/specs/blackboxing.js");
-const {
- SessionDataHelpers,
-} = require("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;
const { BLACKBOXING } = SUPPORTED_DATA;
diff --git a/devtools/server/actors/breakpoint-list.js b/devtools/server/actors/breakpoint-list.js
index 1f9d6c0bf9..a28ffc3f7a 100644
--- a/devtools/server/actors/breakpoint-list.js
+++ b/devtools/server/actors/breakpoint-list.js
@@ -9,9 +9,10 @@ const {
breakpointListSpec,
} = require("resource://devtools/shared/specs/breakpoint-list.js");
-const {
- SessionDataHelpers,
-} = require("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;
const { BREAKPOINTS, XHR_BREAKPOINTS, EVENT_BREAKPOINTS } = SUPPORTED_DATA;
diff --git a/devtools/server/actors/inspector/walker.js b/devtools/server/actors/inspector/walker.js
index 50df1720b7..fbf417565c 100644
--- a/devtools/server/actors/inspector/walker.js
+++ b/devtools/server/actors/inspector/walker.js
@@ -384,7 +384,6 @@ class WalkerActor extends Actor {
this.layoutHelpers = null;
this._orphaned = null;
this._retainedOrphans = null;
- this._nodeActorsMap = null;
this.targetActor.off("will-navigate", this.onFrameUnload);
this.targetActor.off("window-ready", this.onFrameLoad);
@@ -433,6 +432,9 @@ class WalkerActor extends Actor {
this._onEventListenerChange
);
+ // Only nullify some key attributes after having removed all the listeners
+ // as they may still be used in the related listeners.
+ this._nodeActorsMap = null;
this.onMutations = null;
this.layoutActor = null;
diff --git a/devtools/server/actors/page-style.js b/devtools/server/actors/page-style.js
index 1783b58a8f..b37816c85f 100644
--- a/devtools/server/actors/page-style.js
+++ b/devtools/server/actors/page-style.js
@@ -704,6 +704,7 @@ class PageStyleActor extends Actor {
case "::first-line":
case "::selection":
case "::highlight":
+ case "::target-text":
return true;
case "::marker":
return this._nodeIsListItem(node);
diff --git a/devtools/server/actors/resources/index.js b/devtools/server/actors/resources/index.js
index e2857502ad..cfc941a161 100644
--- a/devtools/server/actors/resources/index.js
+++ b/devtools/server/actors/resources/index.js
@@ -390,6 +390,14 @@ exports.hasResourceTypesForTargets = hasResourceTypesForTargets;
* List of all type of resource to stop listening to.
*/
function unwatchResources(rootOrWatcherOrTargetActor, resourceTypes) {
+ // If we are given a target actor, filter out the resource types supported by the target.
+ // When using sharedData to pass types between processes, we are passing them for all target types.
+ const { targetType } = rootOrWatcherOrTargetActor;
+ // Only target actors usecase will have a target type.
+ // For Root and Watcher we process the `resourceTypes` list unfiltered.
+ if (targetType) {
+ resourceTypes = getResourceTypesForTargetType(resourceTypes, targetType);
+ }
for (const resourceType of resourceTypes) {
// Pull all info about this resource type from `Resources` global object
const { watchers } = getResourceTypeEntry(
@@ -415,6 +423,14 @@ exports.unwatchResources = unwatchResources;
* List of all type of resource to clear.
*/
function clearResources(rootOrWatcherOrTargetActor, resourceTypes) {
+ // If we are given a target actor, filter out the resource types supported by the target.
+ // When using sharedData to pass types between processes, we are passing them for all target types.
+ const { targetType } = rootOrWatcherOrTargetActor;
+ // Only target actors usecase will have a target type.
+ // For Root and Watcher we process the `resourceTypes` list unfiltered.
+ if (targetType) {
+ resourceTypes = getResourceTypesForTargetType(resourceTypes, targetType);
+ }
for (const resourceType of resourceTypes) {
const { watchers } = getResourceTypeEntry(
rootOrWatcherOrTargetActor,
diff --git a/devtools/server/actors/resources/jstracer-state.js b/devtools/server/actors/resources/jstracer-state.js
index 74491a6ced..1bb4723b55 100644
--- a/devtools/server/actors/resources/jstracer-state.js
+++ b/devtools/server/actors/resources/jstracer-state.js
@@ -8,13 +8,10 @@ const {
TYPES: { JSTRACER_STATE },
} = require("resource://devtools/server/actors/resources/index.js");
-// Bug 1827382, as this module can be used from the worker thread,
-// the following JSM may be loaded by the worker loader until
-// we have proper support for ESM from workers.
-const {
- addTracingListener,
- removeTracingListener,
-} = require("resource://devtools/server/tracer/tracer.jsm");
+const { JSTracer } = ChromeUtils.importESModule(
+ "resource://devtools/server/tracer/tracer.sys.mjs",
+ { global: "contextual" }
+);
const { LOG_METHODS } = require("resource://devtools/server/actors/tracer.js");
const Targets = require("resource://devtools/server/actors/targets/index.js");
@@ -42,7 +39,7 @@ class TracingStateWatcher {
this.tracingListener = {
onTracingToggled: this.onTracingToggled.bind(this),
};
- addTracingListener(this.tracingListener);
+ JSTracer.addTracingListener(this.tracingListener);
}
/**
@@ -52,7 +49,7 @@ class TracingStateWatcher {
if (!this.tracingListener) {
return;
}
- removeTracingListener(this.tracingListener);
+ JSTracer.removeTracingListener(this.tracingListener);
}
/**
diff --git a/devtools/server/actors/resources/network-events.js b/devtools/server/actors/resources/network-events.js
index 909c16e052..9401d835ff 100644
--- a/devtools/server/actors/resources/network-events.js
+++ b/devtools/server/actors/resources/network-events.js
@@ -9,9 +9,9 @@ const { isWindowGlobalPartOfContext } = ChromeUtils.importESModule(
"resource://devtools/server/actors/watcher/browsing-context-helpers.sys.mjs",
{ global: "contextual" }
);
-const { WatcherRegistry } = ChromeUtils.importESModule(
- "resource://devtools/server/actors/watcher/WatcherRegistry.sys.mjs",
- // WatcherRegistry needs to be a true singleton and loads ActorManagerParent
+const { ParentProcessWatcherRegistry } = ChromeUtils.importESModule(
+ "resource://devtools/server/actors/watcher/ParentProcessWatcherRegistry.sys.mjs",
+ // ParentProcessWatcherRegistry needs to be a true singleton and loads ActorManagerParent
// which also has to be a true singleton.
{ global: "shared" }
);
@@ -253,7 +253,7 @@ class NetworkEventWatcher {
// (i.e. the process where this Watcher runs)
const isParentProcessOnlyBrowserToolbox =
this.watcherActor.sessionContext.type == "all" &&
- !WatcherRegistry.isWatchingTargets(
+ !ParentProcessWatcherRegistry.isWatchingTargets(
this.watcherActor,
Targets.TYPES.FRAME
);
diff --git a/devtools/server/actors/resources/sources.js b/devtools/server/actors/resources/sources.js
index c4f0601106..63a5987e0e 100644
--- a/devtools/server/actors/resources/sources.js
+++ b/devtools/server/actors/resources/sources.js
@@ -44,6 +44,8 @@ class SourceWatcher {
this.sourcesManager = targetActor.sourcesManager;
this.onAvailable = onAvailable;
+ threadActor.attach({});
+
// Disable `ThreadActor.newSource` RDP event in order to avoid unnecessary traffic
threadActor.disableNewSourceEvents();
diff --git a/devtools/server/actors/resources/utils/parent-process-storage.js b/devtools/server/actors/resources/utils/parent-process-storage.js
index 760e6e4d38..1d3a3dd341 100644
--- a/devtools/server/actors/resources/utils/parent-process-storage.js
+++ b/devtools/server/actors/resources/utils/parent-process-storage.js
@@ -79,11 +79,12 @@ class ParentProcessStorage {
watcherActor.sessionContext;
await this._spawnActor(addonBrowsingContextID, addonInnerWindowId);
} else if (watcherActor.sessionContext.type == "all") {
- const parentProcessTargetActor =
- this.watcherActor.getTargetActorInParentProcess();
- const { browsingContextID, innerWindowId } =
- parentProcessTargetActor.form();
- await this._spawnActor(browsingContextID, innerWindowId);
+ // Note that there should be only one such target in the browser toolbox.
+ // The Parent Process Target Actor.
+ for (const targetActor of this.watcherActor.getTargetActorsInParentProcess()) {
+ const { browsingContextID, innerWindowId } = targetActor.form();
+ await this._spawnActor(browsingContextID, innerWindowId);
+ }
} else {
throw new Error(
"Unsupported session context type=" + watcherActor.sessionContext.type
diff --git a/devtools/server/actors/style-rule.js b/devtools/server/actors/style-rule.js
index e9f39fa3d0..9ddd6a380c 100644
--- a/devtools/server/actors/style-rule.js
+++ b/devtools/server/actors/style-rule.js
@@ -724,7 +724,7 @@ class StyleRuleActor extends Actor {
const cssText = await this.pageStyle.styleSheetsManager.getText(
resourceId
);
- const { text } = getRuleText(cssText, this.line, this.column);
+ const text = getRuleText(cssText, this.line, this.column);
// Cache the result on the rule actor to avoid parsing again next time
this._failedToGetRuleText = false;
this.authoredText = text;
@@ -823,19 +823,32 @@ class StyleRuleActor extends Actor {
this.pageStyle.styleSheetsManager.getStyleSheetResourceId(
this._parentSheet
);
- let cssText = await this.pageStyle.styleSheetsManager.getText(resourceId);
- const { offset, text } = getRuleText(cssText, this.line, this.column);
- cssText =
- cssText.substring(0, offset) +
- newText +
- cssText.substring(offset + text.length);
-
- await this.pageStyle.styleSheetsManager.setStyleSheetText(
- resourceId,
- cssText,
- { kind: UPDATE_PRESERVING_RULES }
+ const sheetText = await this.pageStyle.styleSheetsManager.getText(
+ resourceId
+ );
+ const cssText = InspectorUtils.replaceBlockRuleBodyTextInStylesheet(
+ sheetText,
+ this.line,
+ this.column,
+ newText
);
+
+ if (typeof cssText !== "string") {
+ throw new Error(
+ "Error in InspectorUtils.replaceBlockRuleBodyTextInStylesheet"
+ );
+ }
+
+ // setStyleSheetText will parse the stylesheet which can be costly, so only do it
+ // if the text has actually changed.
+ if (sheetText !== newText) {
+ await this.pageStyle.styleSheetsManager.setStyleSheetText(
+ resourceId,
+ cssText,
+ { kind: UPDATE_PRESERVING_RULES }
+ );
+ }
}
this.authoredText = newText;
diff --git a/devtools/server/actors/target-configuration.js b/devtools/server/actors/target-configuration.js
index b6db235143..e739c1cc3d 100644
--- a/devtools/server/actors/target-configuration.js
+++ b/devtools/server/actors/target-configuration.js
@@ -9,9 +9,10 @@ const {
targetConfigurationSpec,
} = require("resource://devtools/shared/specs/target-configuration.js");
-const {
- SessionDataHelpers,
-} = require("resource://devtools/server/actors/watcher/SessionDataHelpers.jsm");
+const { SessionDataHelpers } = ChromeUtils.importESModule(
+ "resource://devtools/server/actors/watcher/SessionDataHelpers.sys.mjs",
+ { global: "contextual" }
+);
const { isBrowsingContextPartOfContext } = ChromeUtils.importESModule(
"resource://devtools/server/actors/watcher/browsing-context-helpers.sys.mjs",
{ global: "contextual" }
@@ -486,7 +487,10 @@ class TargetConfigurationActor extends Actor {
"bf-cache-navigation-pageshow",
this._onBfCacheNavigation
);
- this._restoreParentProcessConfiguration();
+ // Avoid trying to restore if the related context is already being destroyed
+ if (this._browsingContext && !this._browsingContext.isDiscarded) {
+ this._restoreParentProcessConfiguration();
+ }
super.destroy();
}
}
diff --git a/devtools/server/actors/targets/base-target-actor.js b/devtools/server/actors/targets/base-target-actor.js
index f3fc2a89e7..646874c4f1 100644
--- a/devtools/server/actors/targets/base-target-actor.js
+++ b/devtools/server/actors/targets/base-target-actor.js
@@ -203,6 +203,18 @@ class BaseTargetActor extends Actor {
) {
return;
}
+ // In the browser toolbox, when debugging the parent process, we should only toggle the tracer in the Parent Process Target Actor.
+ // We have to ignore any frame target which may run in the parent process.
+ // For example DevTools documents or a tab running in the parent process.
+ // (PROCESS_TYPE_DEFAULT refers to the parent process)
+ if (
+ this.sessionContext.type == "all" &&
+ this.targetType === Targets.TYPES.FRAME &&
+ this.typeName != "parentProcessTarget" &&
+ Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_DEFAULT
+ ) {
+ return;
+ }
const tracerActor = this.getTargetScopedActor("tracer");
tracerActor.startTracing(options.tracerOptions);
} else if (this.hasTargetScopedActor("tracer")) {
diff --git a/devtools/server/actors/targets/session-data-processors/breakpoints.js b/devtools/server/actors/targets/session-data-processors/breakpoints.js
index 67c270654d..8ecd80ad64 100644
--- a/devtools/server/actors/targets/session-data-processors/breakpoints.js
+++ b/devtools/server/actors/targets/session-data-processors/breakpoints.js
@@ -32,11 +32,9 @@ module.exports = {
threadActor.removeAllBreakpoints();
}
const isTargetCreation = threadActor.state == THREAD_STATES.DETACHED;
- if (isTargetCreation && !targetActor.targetType.endsWith("worker")) {
+ if (isTargetCreation) {
// If addOrSetSessionDataEntry is called during target creation, attach the
// thread actor automatically and pass the initial breakpoints.
- // However, do not attach the thread actor for Workers. They use a codepath
- // which releases the worker on `attach`. For them, the client will call `attach`. (bug 1691986)
await threadActor.attach({ breakpoints: entries });
} else {
// If addOrSetSessionDataEntry is called for an existing target, set the new
diff --git a/devtools/server/actors/targets/session-data-processors/event-breakpoints.js b/devtools/server/actors/targets/session-data-processors/event-breakpoints.js
index 4eb9e4f3a8..1b2dbd847e 100644
--- a/devtools/server/actors/targets/session-data-processors/event-breakpoints.js
+++ b/devtools/server/actors/targets/session-data-processors/event-breakpoints.js
@@ -16,11 +16,8 @@ module.exports = {
updateType
) {
const { threadActor } = targetActor;
- // Same as comments for XHR breakpoints. See lines 117-118
- if (
- threadActor.state == THREAD_STATES.DETACHED &&
- !targetActor.targetType.endsWith("worker")
- ) {
+ // The thread actor has to be initialized in order to have functional breakpoints
+ if (threadActor.state == THREAD_STATES.DETACHED) {
threadActor.attach();
}
if (updateType == "set") {
diff --git a/devtools/server/actors/targets/session-data-processors/index.js b/devtools/server/actors/targets/session-data-processors/index.js
index 19b7d69302..72bc769dd1 100644
--- a/devtools/server/actors/targets/session-data-processors/index.js
+++ b/devtools/server/actors/targets/session-data-processors/index.js
@@ -4,9 +4,10 @@
"use strict";
-const {
- SessionDataHelpers,
-} = require("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;
const SessionDataProcessors = {};
diff --git a/devtools/server/actors/targets/session-data-processors/thread-configuration.js b/devtools/server/actors/targets/session-data-processors/thread-configuration.js
index ad5c0fe024..381a62f640 100644
--- a/devtools/server/actors/targets/session-data-processors/thread-configuration.js
+++ b/devtools/server/actors/targets/session-data-processors/thread-configuration.js
@@ -28,12 +28,9 @@ module.exports = {
threadOptions[key] = value;
}
- if (
- !targetActor.targetType.endsWith("worker") &&
- targetActor.threadActor.state == THREAD_STATES.DETACHED
- ) {
+ if (targetActor.threadActor.state == THREAD_STATES.DETACHED) {
await targetActor.threadActor.attach(threadOptions);
- } else {
+ } else if (!targetActor.threadActor.isDestroyed()) {
// Regarding `updateType`, `entries` is always a partial set of configurations.
// We will acknowledge the passed attribute, but if we had set some other attributes
// before this call, they will stay as-is.
diff --git a/devtools/server/actors/targets/session-data-processors/xhr-breakpoints.js b/devtools/server/actors/targets/session-data-processors/xhr-breakpoints.js
index 3bbcf54aaf..81ecb72fb2 100644
--- a/devtools/server/actors/targets/session-data-processors/xhr-breakpoints.js
+++ b/devtools/server/actors/targets/session-data-processors/xhr-breakpoints.js
@@ -22,10 +22,7 @@ module.exports = {
// The thread actor has to be initialized in order to correctly
// retrieve the stack trace when hitting an XHR
- if (
- threadActor.state == THREAD_STATES.DETACHED &&
- !targetActor.targetType.endsWith("worker")
- ) {
+ if (threadActor.state == THREAD_STATES.DETACHED) {
await threadActor.attach();
}
diff --git a/devtools/server/actors/targets/target-actor-registry.sys.mjs b/devtools/server/actors/targets/target-actor-registry.sys.mjs
index 25c1ac1234..bc7adffec2 100644
--- a/devtools/server/actors/targets/target-actor-registry.sys.mjs
+++ b/devtools/server/actors/targets/target-actor-registry.sys.mjs
@@ -9,7 +9,8 @@
// are still using message manager in order to avoid being destroyed on navigation.
// And because of this, these actors aren't using JS Window Actor.
const windowGlobalTargetActors = new Set();
-let xpcShellTargetActor = null;
+
+const xpcShellTargetActors = new Set();
export var TargetActorRegistry = {
registerTargetActor(targetActor) {
@@ -21,15 +22,15 @@ export var TargetActorRegistry = {
},
registerXpcShellTargetActor(targetActor) {
- xpcShellTargetActor = targetActor;
+ xpcShellTargetActors.add(targetActor);
},
- unregisterXpcShellTargetActor() {
- xpcShellTargetActor = null;
+ unregisterXpcShellTargetActor(targetActor) {
+ xpcShellTargetActors.delete(targetActor);
},
- get xpcShellTargetActor() {
- return xpcShellTargetActor;
+ get xpcShellTargetActors() {
+ return xpcShellTargetActors;
},
/**
diff --git a/devtools/server/actors/targets/webextension.js b/devtools/server/actors/targets/webextension.js
index c717b53011..47127dc65c 100644
--- a/devtools/server/actors/targets/webextension.js
+++ b/devtools/server/actors/targets/webextension.js
@@ -162,6 +162,12 @@ class WebExtensionTargetActor extends ParentProcessTargetActor {
// URL shown in the window tittle when the addon debugger is opened).
const extensionWindow = this._searchForExtensionWindow();
this.setDocShell(extensionWindow.docShell);
+
+ // `setDocShell` will force the instantiation of the thread actor.
+ // We now have to initialize it in order to listen for new global
+ // which allows to properly detect addon reload via _shouldAddNewGlobalAsDebuggee
+ // which may call _onNewExtensionWindow.
+ this.threadActor.attach({});
}
// Override the ParentProcessTargetActor's override in order to only iterate
diff --git a/devtools/server/actors/targets/window-global.js b/devtools/server/actors/targets/window-global.js
index 6719f0518d..f8f5e5f3c6 100644
--- a/devtools/server/actors/targets/window-global.js
+++ b/devtools/server/actors/targets/window-global.js
@@ -381,6 +381,15 @@ class WindowGlobalTargetActor extends BaseTargetActor {
// (This is also probably meant to disappear once EFT is the only supported codepath)
this._docShellsObserved = false;
DevToolsUtils.executeSoon(() => this._watchDocshells());
+
+ // The `watchedByDevTools` enables gecko behavior tied to this flag, such as:
+ // - reporting the contents of HTML loaded in the docshells,
+ // - or capturing stacks for the network monitor.
+ //
+ // This flag can only be set on top level BrowsingContexts.
+ if (!this.browsingContext.parent) {
+ this.browsingContext.watchedByDevTools = true;
+ }
}
get docShell() {
@@ -480,6 +489,10 @@ class WindowGlobalTargetActor extends BaseTargetActor {
return this.browsingContext?.id;
}
+ get innerWindowId() {
+ return this.window?.windowGlobalChild.innerWindowId;
+ }
+
get browserId() {
return this.browsingContext?.browserId;
}
@@ -687,6 +700,11 @@ class WindowGlobalTargetActor extends BaseTargetActor {
response.outerWindowID = this.outerWindowID;
}
+ // If the actor is already being destroyed, avoid re-registering the target scoped actors
+ if (this.destroying) {
+ return response;
+ }
+
const actors = this._createExtraActors();
Object.assign(response, actors);
@@ -731,6 +749,17 @@ class WindowGlobalTargetActor extends BaseTargetActor {
this._touchSimulator = null;
}
+ // The watchedByDevTools flag is only set on top level BrowsingContext
+ // (as it then cascades to all its children),
+ // and when destroying the target, we should tell the platform we no longer
+ // observe this BrowsingContext and set this attribute to false.
+ if (
+ this.browsingContext?.watchedByDevTools &&
+ !this.browsingContext.parent
+ ) {
+ this.browsingContext.watchedByDevTools = false;
+ }
+
// Check for `docShell` availability, as it can be already gone during
// Firefox shutdown.
if (this.docShell) {
@@ -1314,10 +1343,6 @@ class WindowGlobalTargetActor extends BaseTargetActor {
if (typeof options.touchEventsOverride !== "undefined") {
const enableTouchSimulator = options.touchEventsOverride === "enabled";
- this.docShell.metaViewportOverride = enableTouchSimulator
- ? Ci.nsIDocShell.META_VIEWPORT_OVERRIDE_ENABLED
- : Ci.nsIDocShell.META_VIEWPORT_OVERRIDE_NONE;
-
// We want to reload the document if it's an "existing" top level target on which
// the touch simulator will be toggled and the user has turned the
// "reload on touch simulation" setting on.
@@ -1384,7 +1409,14 @@ class WindowGlobalTargetActor extends BaseTargetActor {
*/
_restoreTargetConfiguration() {
if (this._restoreFocus && this.browsingContext?.isActive) {
- this.window.focus();
+ try {
+ this.window.focus();
+ } catch (e) {
+ // When closing devtools while navigating, focus() may throw NS_ERROR_XPC_SECURITY_MANAGER_VETO
+ if (e.result != Cr.NS_ERROR_XPC_SECURITY_MANAGER_VETO) {
+ throw e;
+ }
+ }
}
}
@@ -1688,17 +1720,6 @@ class DebuggerProgressListener {
this._knownWindowIDs.set(getWindowID(win), win);
}
- // The `watchedByDevTools` enables gecko behavior tied to this flag, such as:
- // - reporting the contents of HTML loaded in the docshells,
- // - or capturing stacks for the network monitor.
- //
- // This flag is also set in frame-helper but in the case of the browser toolbox, we
- // don't have the watcher enabled by default yet, and as a result we need to set it
- // here for the parent process window global.
- // This should be removed as part of Bug 1709529.
- if (this._targetActor.typeName === "parentProcessTarget") {
- docShell.browsingContext.watchedByDevTools = true;
- }
// Immediately enable CSS error reports on new top level docshells, if this was already enabled.
// This is specific to MBT and WebExtension targets (so the isRootActor check).
if (
@@ -1741,12 +1762,6 @@ class DebuggerProgressListener {
for (const win of windows) {
this._knownWindowIDs.delete(getWindowID(win));
}
-
- // We only reset it for parent process target actor as the flag should be set in parent
- // process, and thus is set elsewhere for other type of BrowsingContextActor.
- if (this._targetActor.typeName === "parentProcessTarget") {
- docShell.browsingContext.watchedByDevTools = false;
- }
}
_getWindowsInDocShell(docShell) {
diff --git a/devtools/server/actors/targets/worker.js b/devtools/server/actors/targets/worker.js
index 20b60cfa24..7604b5be6e 100644
--- a/devtools/server/actors/targets/worker.js
+++ b/devtools/server/actors/targets/worker.js
@@ -126,12 +126,6 @@ class WorkerTargetActor extends BaseTargetActor {
return this._sourcesManager;
}
- // This is called from the ThreadActor#onAttach method
- onThreadAttached() {
- // This isn't an RDP event and is only listened to from startup/worker.js.
- this.emit("worker-thread-attached");
- }
-
destroy() {
super.destroy();
diff --git a/devtools/server/actors/thread-configuration.js b/devtools/server/actors/thread-configuration.js
index f0c697bb51..d3b7e229bf 100644
--- a/devtools/server/actors/thread-configuration.js
+++ b/devtools/server/actors/thread-configuration.js
@@ -9,9 +9,10 @@ const {
threadConfigurationSpec,
} = require("resource://devtools/shared/specs/thread-configuration.js");
-const {
- SessionDataHelpers,
-} = require("resource://devtools/server/actors/watcher/SessionDataHelpers.jsm");
+const { SessionDataHelpers } = ChromeUtils.importESModule(
+ "resource://devtools/server/actors/watcher/SessionDataHelpers.sys.mjs",
+ { global: "contextual" }
+);
const {
SUPPORTED_DATA: { THREAD_CONFIGURATION },
} = SessionDataHelpers;
diff --git a/devtools/server/actors/thread.js b/devtools/server/actors/thread.js
index 07dcc27a6a..0042e76a2a 100644
--- a/devtools/server/actors/thread.js
+++ b/devtools/server/actors/thread.js
@@ -418,12 +418,6 @@ class ThreadActor extends Actor {
this.alreadyAttached = true;
this.dbg.enable();
- // Notify the target actor that we've finished attaching. If this is a worker
- // thread which was paused until attaching, this will allow content to
- // begin executing.
- if (this.targetActor.onThreadAttached) {
- this.targetActor.onThreadAttached();
- }
if (Services.obs) {
// Set a wrappedJSObject property so |this| can be sent via the observer service
// for the xpcshell harness.
@@ -535,6 +529,13 @@ class ThreadActor extends Actor {
}
async setBreakpoint(location, options) {
+ // Automatically initialize the thread actor if it wasn't yet done.
+ // Note that ideally, it should rather be done via reconfigure/thread configuration.
+ if (this._state === STATES.DETACHED) {
+ this.attach({});
+ this.addAllSources();
+ }
+
let actor = this.breakpointActorMap.get(location);
// Avoid resetting the exact same breakpoint twice
if (actor && JSON.stringify(actor.options) == JSON.stringify(options)) {
@@ -597,7 +598,9 @@ class ThreadActor extends Actor {
}
getAvailableEventBreakpoints() {
- return getAvailableEventBreakpoints(this.targetActor.window);
+ return getAvailableEventBreakpoints(
+ this.targetActor.window || this.targetActor.workerGlobal
+ );
}
getActiveEventBreakpoints() {
return Array.from(this._activeEventBreakpoints);
diff --git a/devtools/server/actors/tracer.js b/devtools/server/actors/tracer.js
index bf759cee5f..d98749ceb1 100644
--- a/devtools/server/actors/tracer.js
+++ b/devtools/server/actors/tracer.js
@@ -4,16 +4,14 @@
"use strict";
-// Bug 1827382, as this module can be used from the worker thread,
-// the following JSM may be loaded by the worker loader until
-// we have proper support for ESM from workers.
-const {
- startTracing,
- stopTracing,
- addTracingListener,
- removeTracingListener,
- NEXT_INTERACTION_MESSAGE,
-} = require("resource://devtools/server/tracer/tracer.jsm");
+const lazy = {};
+ChromeUtils.defineESModuleGetters(
+ lazy,
+ {
+ JSTracer: "resource://devtools/server/tracer/tracer.sys.mjs",
+ },
+ { global: "contextual" }
+);
const { Actor } = require("resource://devtools/shared/protocol.js");
const { tracerSpec } = require("resource://devtools/shared/specs/tracer.js");
@@ -136,10 +134,10 @@ class TracerActor extends Actor {
onTracingPending: this.onTracingPending.bind(this),
onTracingDOMMutation: this.onTracingDOMMutation.bind(this),
};
- addTracingListener(this.tracingListener);
+ lazy.JSTracer.addTracingListener(this.tracingListener);
this.traceValues = !!options.traceValues;
try {
- startTracing({
+ lazy.JSTracer.startTracing({
global: this.targetActor.window || this.targetActor.workerGlobal,
prefix: options.prefix || "",
// Enable receiving the `currentDOMEvent` being passed to `onTracingFrame`
@@ -170,10 +168,10 @@ class TracerActor extends Actor {
return;
}
// Remove before stopping to prevent receiving the stop notification
- removeTracingListener(this.tracingListener);
+ lazy.JSTracer.removeTracingListener(this.tracingListener);
this.tracingListener = null;
- stopTracing();
+ lazy.JSTracer.stopTracing();
this.logMethod = null;
}
@@ -230,7 +228,7 @@ class TracerActor extends Actor {
if (consoleMessageWatcher) {
consoleMessageWatcher.emitMessages([
{
- arguments: [NEXT_INTERACTION_MESSAGE],
+ arguments: [lazy.JSTracer.NEXT_INTERACTION_MESSAGE],
styles: [],
level: "jstracer",
chromeContext: false,
@@ -510,7 +508,7 @@ class TracerActor extends Actor {
* A string to be displayed as a prefix of any logged frame.
* @param {String} options.why
* A string to explain why the function stopped.
- * See tracer.jsm's FRAME_EXIT_REASONS.
+ * See tracer.sys.mjs's FRAME_EXIT_REASONS.
* @param {Debugger.Object|primitive} options.rv
* The returned value. It can be the returned value, or the thrown exception.
* It is either a primitive object, otherwise it is a Debugger.Object for any other JS Object type.
diff --git a/devtools/server/actors/utils/event-breakpoints.js b/devtools/server/actors/utils/event-breakpoints.js
index a7752b8201..8fbefec804 100644
--- a/devtools/server/actors/utils/event-breakpoints.js
+++ b/devtools/server/actors/utils/event-breakpoints.js
@@ -131,7 +131,8 @@ const AVAILABLE_BREAKPOINTS = [
items: [
// The condition should be removed when "dom.element.popover.enabled" is removed
generalEvent("control", "beforetoggle", () =>
- Services.prefs.getBoolPref("dom.element.popover.enabled")
+ // Services.prefs isn't available on worker targets
+ Services.prefs?.getBoolPref("dom.element.popover.enabled")
),
generalEvent("control", "blur"),
generalEvent("control", "change"),
@@ -139,7 +140,11 @@ const AVAILABLE_BREAKPOINTS = [
generalEvent("control", "focusin"),
generalEvent("control", "focusout"),
// The condition should be removed when "dom.element.invokers.enabled" is removed
- generalEvent("control", "invoke", win => "InvokeEvent" in win),
+ generalEvent(
+ "control",
+ "invoke",
+ global => global && "InvokeEvent" in global
+ ),
generalEvent("control", "reset"),
generalEvent("control", "resize"),
generalEvent("control", "scroll"),
@@ -483,17 +488,17 @@ exports.getAvailableEventBreakpoints = getAvailableEventBreakpoints;
/**
* Get all available event breakpoints
*
- * @param {Window} window
+ * @param {Window|WorkerGlobalScope} global
* @returns {Array<Object>} An array containing object with 2 properties, an id and a name,
* representing the event.
*/
-function getAvailableEventBreakpoints(window) {
+function getAvailableEventBreakpoints(global) {
const available = [];
for (const { name, items } of AVAILABLE_BREAKPOINTS) {
available.push({
name,
events: items
- .filter(item => !item.condition || item.condition(window))
+ .filter(item => !item.condition || item.condition(global))
.map(item => ({
id: item.id,
name: item.name,
diff --git a/devtools/server/actors/utils/style-utils.js b/devtools/server/actors/utils/style-utils.js
index 5f2e912002..1d52448fb6 100644
--- a/devtools/server/actors/utils/style-utils.js
+++ b/devtools/server/actors/utils/style-utils.js
@@ -4,8 +4,6 @@
"use strict";
-const { getCSSLexer } = require("resource://devtools/shared/css/lexer.js");
-
const XHTML_NS = "http://www.w3.org/1999/xhtml";
const FONT_PREVIEW_TEXT = "Abc";
const FONT_PREVIEW_FONT_SIZE = 40;
@@ -120,66 +118,12 @@ function getRuleText(initialText, line, column) {
throw new Error("Location information is missing");
}
- const { offset: textOffset, text } = getTextAtLineColumn(
- initialText,
- line,
- column
- );
- const lexer = getCSSLexer(text);
-
- // Search forward for the opening brace.
- while (true) {
- const token = lexer.nextToken();
- if (!token) {
- throw new Error("couldn't find start of the rule");
- }
- if (token.tokenType === "symbol" && token.text === "{") {
- break;
- }
- }
-
- // Now collect text until we see the matching close brace.
- let braceDepth = 1;
- let startOffset, endOffset;
- while (true) {
- const token = lexer.nextToken();
- if (!token) {
- break;
- }
- if (startOffset === undefined) {
- startOffset = token.startOffset;
- }
- if (token.tokenType === "symbol") {
- if (token.text === "{") {
- ++braceDepth;
- } else if (token.text === "}") {
- --braceDepth;
- if (braceDepth == 0) {
- break;
- }
- }
- }
- endOffset = token.endOffset;
- }
-
- // If the rule was of the form "selector {" with no closing brace
- // and no properties, just return an empty string.
- if (startOffset === undefined) {
- return { offset: 0, text: "" };
- }
- // If the input didn't have any tokens between the braces (e.g.,
- // "div {}"), then the endOffset won't have been set yet; so account
- // for that here.
- if (endOffset === undefined) {
- endOffset = startOffset;
+ const { text } = getTextAtLineColumn(initialText, line, column);
+ const res = InspectorUtils.getRuleBodyText(text);
+ if (res === null || typeof res === "undefined") {
+ throw new Error("Couldn't find rule");
}
-
- // Note that this approach will preserve comments, despite the fact
- // that cssTokenizer skips them.
- return {
- offset: textOffset + startOffset,
- text: text.substring(startOffset, endOffset),
- };
+ return res;
}
exports.getRuleText = getRuleText;
diff --git a/devtools/server/actors/utils/stylesheets-manager.js b/devtools/server/actors/utils/stylesheets-manager.js
index a9c0705e8d..1c065afd4e 100644
--- a/devtools/server/actors/utils/stylesheets-manager.js
+++ b/devtools/server/actors/utils/stylesheets-manager.js
@@ -446,10 +446,12 @@ class StyleSheetsManager extends EventEmitter {
InspectorUtils.parseStyleSheet(styleSheet, text);
modifiedStyleSheets.set(styleSheet, text);
- const { atRules, ruleCount } =
- this.getStyleSheetRuleCountAndAtRules(styleSheet);
-
+ // getStyleSheetRuleCountAndAtRules can be costly, so only call it when needed,
+ // i.e. when the whole stylesheet is modified, not when a rule body is.
+ let atRules, ruleCount;
if (kind !== UPDATE_PRESERVING_RULES) {
+ ({ atRules, ruleCount } =
+ this.getStyleSheetRuleCountAndAtRules(styleSheet));
this.#notifyPropertyChanged(resourceId, "ruleCount", ruleCount);
}
@@ -465,13 +467,15 @@ class StyleSheetsManager extends EventEmitter {
});
}
- this.#onStyleSheetUpdated({
- resourceId,
- updateKind: "at-rules-changed",
- updates: {
- resourceUpdates: { atRules },
- },
- });
+ if (kind !== UPDATE_PRESERVING_RULES) {
+ this.#onStyleSheetUpdated({
+ resourceId,
+ updateKind: "at-rules-changed",
+ updates: {
+ resourceUpdates: { atRules },
+ },
+ });
+ }
}
/**
@@ -705,6 +709,13 @@ class StyleSheetsManager extends EventEmitter {
line: InspectorUtils.getRelativeRuleLine(rule),
column: InspectorUtils.getRuleColumn(rule),
});
+ } else if (className === "CSSPropertyRule") {
+ atRules.push({
+ type: "property",
+ propertyName: rule.name,
+ line: InspectorUtils.getRelativeRuleLine(rule),
+ column: InspectorUtils.getRuleColumn(rule),
+ });
}
}
return {
diff --git a/devtools/server/actors/watcher.js b/devtools/server/actors/watcher.js
index 935d33faa8..10de102229 100644
--- a/devtools/server/actors/watcher.js
+++ b/devtools/server/actors/watcher.js
@@ -11,13 +11,12 @@ const { TargetActorRegistry } = ChromeUtils.importESModule(
"resource://devtools/server/actors/targets/target-actor-registry.sys.mjs",
{ global: "shared" }
);
-const { WatcherRegistry } = ChromeUtils.importESModule(
- "resource://devtools/server/actors/watcher/WatcherRegistry.sys.mjs",
- // WatcherRegistry needs to be a true singleton and loads ActorManagerParent
+const { ParentProcessWatcherRegistry } = ChromeUtils.importESModule(
+ "resource://devtools/server/actors/watcher/ParentProcessWatcherRegistry.sys.mjs",
+ // ParentProcessWatcherRegistry needs to be a true singleton and loads ActorManagerParent
// which also has to be a true singleton.
{ global: "shared" }
);
-const Targets = require("resource://devtools/server/actors/targets/index.js");
const { getAllBrowsingContextsForContext } = ChromeUtils.importESModule(
"resource://devtools/server/actors/watcher/browsing-context-helpers.sys.mjs",
{ global: "contextual" }
@@ -26,28 +25,6 @@ const {
SESSION_TYPES,
} = require("resource://devtools/server/actors/watcher/session-context.js");
-const TARGET_HELPERS = {};
-loader.lazyRequireGetter(
- TARGET_HELPERS,
- Targets.TYPES.FRAME,
- "resource://devtools/server/actors/watcher/target-helpers/frame-helper.js"
-);
-loader.lazyRequireGetter(
- TARGET_HELPERS,
- Targets.TYPES.PROCESS,
- "resource://devtools/server/actors/watcher/target-helpers/process-helper.js"
-);
-loader.lazyRequireGetter(
- TARGET_HELPERS,
- Targets.TYPES.SERVICE_WORKER,
- "devtools/server/actors/watcher/target-helpers/service-worker-helper"
-);
-loader.lazyRequireGetter(
- TARGET_HELPERS,
- Targets.TYPES.WORKER,
- "resource://devtools/server/actors/watcher/target-helpers/worker-helper.js"
-);
-
loader.lazyRequireGetter(
this,
"NetworkParentActor",
@@ -137,6 +114,14 @@ exports.WatcherActor = class WatcherActor extends Actor {
// but there are certain cases when a new target is available before the
// old target is destroyed.
this._currentWindowGlobalTargets = new Map();
+
+ // The Browser Toolbox requires to load modules in a distinct compartment in order
+ // to be able to debug system compartments modules (most of Firefox internal codebase).
+ // This is a requirement coming from SpiderMonkey Debugger API and relates to the thread actor.
+ this._jsActorName =
+ sessionContext.type == SESSION_TYPES.ALL
+ ? "BrowserToolboxDevToolsProcess"
+ : "DevToolsProcess";
}
get sessionContext() {
@@ -176,16 +161,33 @@ exports.WatcherActor = class WatcherActor extends Actor {
}
destroy() {
- // Force unwatching for all types, even if we weren't watching.
- // This is fine as unwatchTarget is NOOP if we weren't already watching for this target type.
- for (const targetType of Object.values(Targets.TYPES)) {
- this.unwatchTargets(targetType);
+ // Only try to notify content processes if the watcher was in the registry.
+ // Otherwise it means that it wasn't connected to any process and the JS Process Actor
+ // wouldn't be registered.
+ if (ParentProcessWatcherRegistry.getWatcher(this.actorID)) {
+ // Emit one IPC message on destroy to all the processes
+ const domProcesses = ChromeUtils.getAllDOMProcesses();
+ for (const domProcess of domProcesses) {
+ domProcess.getActor(this._jsActorName).destroyWatcher({
+ watcherActorID: this.actorID,
+ });
+ }
}
- this.unwatchResources(Object.values(Resources.TYPES));
- WatcherRegistry.unregisterWatcher(this);
+ // Ensure destroying all Resource Watcher instantiated in the parent process
+ Resources.unwatchResources(
+ this,
+ Resources.getParentProcessResourceTypes(Object.values(Resources.TYPES))
+ );
+
+ ParentProcessWatcherRegistry.unregisterWatcher(this.actorID);
- // Destroy the actor at the end so that its actorID keeps being defined.
+ // In case the watcher actor is leaked, prevent leaking the browser window
+ this._browserElement = null;
+
+ // Destroy the actor in order to ensure destroying all its children actors.
+ // As this actor is a pool with children actors, when the transport/connection closes
+ // we expect all actors and its children to be destroyed.
super.destroy();
}
@@ -196,7 +198,7 @@ exports.WatcherActor = class WatcherActor extends Actor {
* Returns the list of currently watched resource types.
*/
get sessionData() {
- return WatcherRegistry.getSessionData(this);
+ return ParentProcessWatcherRegistry.getSessionData(this);
}
form() {
@@ -225,11 +227,44 @@ exports.WatcherActor = class WatcherActor extends Actor {
* Type of context to observe. See Targets.TYPES object.
*/
async watchTargets(targetType) {
- WatcherRegistry.watchTargets(this, targetType);
+ ParentProcessWatcherRegistry.watchTargets(this, targetType);
+
+ // When debugging a tab, ensure processing the top level target first
+ // (for now, other session context types are instantiating the top level target
+ // from the descriptor's getTarget method instead of the Watcher)
+ let topLevelTargetProcess;
+ if (this.sessionContext.type == SESSION_TYPES.BROWSER_ELEMENT) {
+ topLevelTargetProcess =
+ this.browserElement.browsingContext.currentWindowGlobal?.domProcess;
+ if (topLevelTargetProcess) {
+ await topLevelTargetProcess.getActor(this._jsActorName).watchTargets({
+ watcherActorID: this.actorID,
+ targetType,
+ });
+ // Stop execution if we were destroyed in the meantime
+ if (this.isDestroyed()) {
+ return;
+ }
+ }
+ }
- const targetHelperModule = TARGET_HELPERS[targetType];
- // Await the registration in order to ensure receiving the already existing targets
- await targetHelperModule.createTargets(this);
+ // We have to reach out all the content processes as the page may navigate
+ // to any other content process when navigating to another origin.
+ // It may even run in the parent process when loading about:robots.
+ const domProcesses = ChromeUtils.getAllDOMProcesses();
+ const promises = [];
+ for (const domProcess of domProcesses) {
+ if (domProcess == topLevelTargetProcess) {
+ continue;
+ }
+ promises.push(
+ domProcess.getActor(this._jsActorName).watchTargets({
+ watcherActorID: this.actorID,
+ targetType,
+ })
+ );
+ }
+ await Promise.all(promises);
}
/**
@@ -242,7 +277,7 @@ exports.WatcherActor = class WatcherActor extends Actor {
* true when this is called as the result of a change to the devtools.browsertoolbox.scope pref
*/
unwatchTargets(targetType, options = {}) {
- const isWatchingTargets = WatcherRegistry.unwatchTargets(
+ const isWatchingTargets = ParentProcessWatcherRegistry.unwatchTargets(
this,
targetType,
options
@@ -251,14 +286,20 @@ exports.WatcherActor = class WatcherActor extends Actor {
return;
}
- const targetHelperModule = TARGET_HELPERS[targetType];
- targetHelperModule.destroyTargets(this, options);
+ const domProcesses = ChromeUtils.getAllDOMProcesses();
+ for (const domProcess of domProcesses) {
+ domProcess.getActor(this._jsActorName).unwatchTargets({
+ watcherActorID: this.actorID,
+ targetType,
+ options,
+ });
+ }
// Unregister the JS Actors if there is no more DevTools code observing any target/resource,
// unless we're switching mode (having both condition at the same time should only
// happen in tests).
if (!options.isModeSwitching) {
- WatcherRegistry.maybeUnregisterJSActors();
+ ParentProcessWatcherRegistry.maybeUnregisterJSActors();
}
}
@@ -301,7 +342,12 @@ exports.WatcherActor = class WatcherActor extends Actor {
this._flushIframeTargets(actor.innerWindowId);
if (this.sessionContext.type == SESSION_TYPES.BROWSER_ELEMENT) {
- this.updateDomainSessionDataForServiceWorkers(actor.url);
+ // Ignore any pending exception as this request may be pending
+ // while the toolbox closes. And we don't want to delay target emission
+ // on this as this is a implementation detail.
+ this.updateDomainSessionDataForServiceWorkers(actor.url).catch(
+ () => {}
+ );
}
} else if (this._currentWindowGlobalTargets.has(actor.topInnerWindowId)) {
// Emit the event immediately if the top-level target is already available
@@ -444,18 +490,18 @@ exports.WatcherActor = class WatcherActor extends Actor {
}
/**
- * Try to retrieve a parent process TargetActor which is ignored by the
- * TARGET_HELPERS. Examples:
- * - top level target for the browser toolbox
- * - xpcshell target for xpcshell debugging
+ * Try to retrieve Target Actors instantiated in the parent process which aren't
+ * instantiated via the Watcher actor (and its dependencies):
+ * - top level target for the browser toolboxes
+ * - xpcshell targets for xpcshell debugging
*
* See comment in `watchResources`.
*
- * @return {TargetActor|null} Matching target actor if any, null otherwise.
+ * @return {Set<TargetActor>} Matching target actors.
*/
- getTargetActorInParentProcess() {
- if (TargetActorRegistry.xpcShellTargetActor) {
- return TargetActorRegistry.xpcShellTargetActor;
+ getTargetActorsInParentProcess() {
+ if (TargetActorRegistry.xpcShellTargetActors.size) {
+ return TargetActorRegistry.xpcShellTargetActors;
}
// Note: For browser-element debugging, the WindowGlobalTargetActor returned here is created
@@ -467,12 +513,18 @@ exports.WatcherActor = class WatcherActor extends Actor {
switch (this.sessionContext.type) {
case "all":
- return actors.find(actor => actor.typeName === "parentProcessTarget");
+ const parentProcessTargetActor = actors.find(
+ actor => actor.typeName === "parentProcessTarget"
+ );
+ if (parentProcessTargetActor) {
+ return new Set([parentProcessTargetActor]);
+ }
+ return new Set();
case "browser-element":
case "webextension":
// All target actors for browser-element and webextension sessions
// should be created using the JS Window actors.
- return null;
+ return new Set();
default:
throw new Error(
"Unsupported session context type: " + this.sessionContext.type
@@ -497,41 +549,32 @@ exports.WatcherActor = class WatcherActor extends Actor {
);
// Bail out early if all resources were watched from parent process.
- // In this scenario, we do not need to update these resource types in the WatcherRegistry
+ // In this scenario, we do not need to update these resource types in the ParentProcessWatcherRegistry
// as targets do not care about them.
if (!Resources.hasResourceTypesForTargets(resourceTypes)) {
return;
}
- WatcherRegistry.watchResources(this, resourceTypes);
+ ParentProcessWatcherRegistry.watchResources(this, resourceTypes);
- // Fetch resources from all existing targets
- for (const targetType in TARGET_HELPERS) {
- // We process frame targets even if we aren't watching them,
- // because frame target helper codepath handles the top level target, if it runs in the *content* process.
- // It will do another check to `isWatchingTargets(FRAME)` internally.
- // Note that the workaround at the end of this method, using TargetActorRegistry
- // is specific to top level target running in the *parent* process.
- if (
- !WatcherRegistry.isWatchingTargets(this, targetType) &&
- targetType != Targets.TYPES.FRAME
- ) {
- continue;
- }
- const targetResourceTypes = Resources.getResourceTypesForTargetType(
- resourceTypes,
- targetType
+ const promises = [];
+ const domProcesses = ChromeUtils.getAllDOMProcesses();
+ for (const domProcess of domProcesses) {
+ promises.push(
+ domProcess.getActor(this._jsActorName).addOrSetSessionDataEntry({
+ watcherActorID: this.actorID,
+ sessionContext: this.sessionContext,
+ type: "resources",
+ entries: resourceTypes,
+ updateType: "add",
+ })
);
- if (!targetResourceTypes.length) {
- continue;
- }
- const targetHelperModule = TARGET_HELPERS[targetType];
- await targetHelperModule.addOrSetSessionDataEntry({
- watcher: this,
- type: "resources",
- entries: targetResourceTypes,
- updateType: "add",
- });
+ }
+ await Promise.all(promises);
+
+ // Stop execution if we were destroyed in the meantime
+ if (this.isDestroyed()) {
+ return;
}
/*
@@ -551,8 +594,8 @@ exports.WatcherActor = class WatcherActor extends Actor {
* We will eventually get rid of this code once all targets are properly supported by
* the Watcher Actor and we have target helpers for all of them.
*/
- const targetActor = this.getTargetActorInParentProcess();
- if (targetActor) {
+ const targetActors = this.getTargetActorsInParentProcess();
+ for (const targetActor of targetActors) {
const targetActorResourceTypes = Resources.getResourceTypesForTargetType(
resourceTypes,
targetActor.targetType
@@ -581,13 +624,13 @@ exports.WatcherActor = class WatcherActor extends Actor {
);
// Bail out early if all resources were all watched from parent process.
- // In this scenario, we do not need to update these resource types in the WatcherRegistry
+ // In this scenario, we do not need to update these resource types in the ParentProcessWatcherRegistry
// as targets do not care about them.
if (!Resources.hasResourceTypesForTargets(resourceTypes)) {
return;
}
- const isWatchingResources = WatcherRegistry.unwatchResources(
+ const isWatchingResources = ParentProcessWatcherRegistry.unwatchResources(
this,
resourceTypes
);
@@ -598,34 +641,20 @@ exports.WatcherActor = class WatcherActor extends Actor {
// Prevent trying to unwatch when the related BrowsingContext has already
// been destroyed
if (!this.isContextDestroyed()) {
- for (const targetType in TARGET_HELPERS) {
- // Frame target helper handles the top level target, if it runs in the content process
- // so we should always process it. It does a second check to isWatchingTargets.
- if (
- !WatcherRegistry.isWatchingTargets(this, targetType) &&
- targetType != Targets.TYPES.FRAME
- ) {
- continue;
- }
- const targetResourceTypes = Resources.getResourceTypesForTargetType(
- resourceTypes,
- targetType
- );
- if (!targetResourceTypes.length) {
- continue;
- }
- const targetHelperModule = TARGET_HELPERS[targetType];
- targetHelperModule.removeSessionDataEntry({
- watcher: this,
+ const domProcesses = ChromeUtils.getAllDOMProcesses();
+ for (const domProcess of domProcesses) {
+ domProcess.getActor(this._jsActorName).removeSessionDataEntry({
+ watcherActorID: this.actorID,
+ sessionContext: this.sessionContext,
type: "resources",
- entries: targetResourceTypes,
+ entries: resourceTypes,
});
}
}
// See comment in watchResources.
- const targetActor = this.getTargetActorInParentProcess();
- if (targetActor) {
+ const targetActors = this.getTargetActorsInParentProcess();
+ for (const targetActor of targetActors) {
const targetActorResourceTypes = Resources.getResourceTypesForTargetType(
resourceTypes,
targetActor.targetType
@@ -634,7 +663,7 @@ exports.WatcherActor = class WatcherActor extends Actor {
}
// Unregister the JS Window Actor if there is no more DevTools code observing any target/resource
- WatcherRegistry.maybeUnregisterJSActors();
+ ParentProcessWatcherRegistry.maybeUnregisterJSActors();
}
clearResources(resourceTypes) {
@@ -729,34 +758,36 @@ exports.WatcherActor = class WatcherActor extends Actor {
* "set" will update the data set with the new entries.
*/
async addOrSetDataEntry(type, entries, updateType) {
- WatcherRegistry.addOrSetSessionDataEntry(this, type, entries, updateType);
-
- await Promise.all(
- Object.values(Targets.TYPES)
- .filter(
- targetType =>
- // We process frame targets even if we aren't watching them,
- // because frame target helper codepath handles the top level target, if it runs in the *content* process.
- // It will do another check to `isWatchingTargets(FRAME)` internally.
- // Note that the workaround at the end of this method, using TargetActorRegistry
- // is specific to top level target running in the *parent* process.
- WatcherRegistry.isWatchingTargets(this, targetType) ||
- targetType === Targets.TYPES.FRAME
- )
- .map(async targetType => {
- const targetHelperModule = TARGET_HELPERS[targetType];
- await targetHelperModule.addOrSetSessionDataEntry({
- watcher: this,
- type,
- entries,
- updateType,
- });
- })
+ ParentProcessWatcherRegistry.addOrSetSessionDataEntry(
+ this,
+ type,
+ entries,
+ updateType
);
+ const promises = [];
+ const domProcesses = ChromeUtils.getAllDOMProcesses();
+ for (const domProcess of domProcesses) {
+ promises.push(
+ domProcess.getActor(this._jsActorName).addOrSetSessionDataEntry({
+ watcherActorID: this.actorID,
+ sessionContext: this.sessionContext,
+ type,
+ entries,
+ updateType,
+ })
+ );
+ }
+ await Promise.all(promises);
+
+ // Stop execution if we were destroyed in the meantime
+ if (this.isDestroyed()) {
+ return;
+ }
+
// See comment in watchResources
- const targetActor = this.getTargetActorInParentProcess();
- if (targetActor) {
+ const targetActors = this.getTargetActorsInParentProcess();
+ for (const targetActor of targetActors) {
await targetActor.addOrSetSessionDataEntry(
type,
entries,
@@ -777,27 +808,21 @@ exports.WatcherActor = class WatcherActor extends Actor {
* List of values to remove from this data type.
*/
removeDataEntry(type, entries) {
- WatcherRegistry.removeSessionDataEntry(this, type, entries);
-
- Object.values(Targets.TYPES)
- .filter(
- targetType =>
- // See comment in addOrSetDataEntry
- WatcherRegistry.isWatchingTargets(this, targetType) ||
- targetType === Targets.TYPES.FRAME
- )
- .forEach(targetType => {
- const targetHelperModule = TARGET_HELPERS[targetType];
- targetHelperModule.removeSessionDataEntry({
- watcher: this,
- type,
- entries,
- });
+ ParentProcessWatcherRegistry.removeSessionDataEntry(this, type, entries);
+
+ const domProcesses = ChromeUtils.getAllDOMProcesses();
+ for (const domProcess of domProcesses) {
+ domProcess.getActor(this._jsActorName).removeSessionDataEntry({
+ watcherActorID: this.actorID,
+ sessionContext: this.sessionContext,
+ type,
+ entries,
});
+ }
// See comment in addOrSetDataEntry
- const targetActor = this.getTargetActorInParentProcess();
- if (targetActor) {
+ const targetActors = this.getTargetActorsInParentProcess();
+ for (const targetActor of targetActors) {
targetActor.removeSessionDataEntry(type, entries);
}
}
@@ -827,35 +852,13 @@ exports.WatcherActor = class WatcherActor extends Actor {
host = new URL(newTargetUrl).host;
} catch (e) {}
- WatcherRegistry.addOrSetSessionDataEntry(
+ ParentProcessWatcherRegistry.addOrSetSessionDataEntry(
this,
"browser-element-host",
[host],
"set"
);
- // This SessionData attribute is only used when debugging service workers.
- // Avoid instantiating the JS Process Actors if we aren't watching for SW,
- // or if we aren't watching for them just yet.
- // But still update the WatcherRegistry, so that when we start watching
- // and instantiate the target, the host will be set to the right value.
- //
- // Note that it is very important to avoid calling Service worker target helper's
- // addOrSetSessionDataEntry. Otherwise, when we aren't watching for SW at all,
- // we won't call destroyTargets on watcher actor destruction,
- // and as a consequence never unregister the js process actor.
- if (
- !WatcherRegistry.isWatchingTargets(this, Targets.TYPES.SERVICE_WORKER)
- ) {
- return;
- }
-
- const targetHelperModule = TARGET_HELPERS[Targets.TYPES.SERVICE_WORKER];
- await targetHelperModule.addOrSetSessionDataEntry({
- watcher: this,
- type: "browser-element-host",
- entries: [host],
- updateType: "set",
- });
+ return this.addOrSetDataEntry("browser-element-host", [host], "set");
}
};
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,
-};
diff --git a/devtools/server/actors/webconsole/commands/manager.js b/devtools/server/actors/webconsole/commands/manager.js
index 025e197e3b..e96e0a617f 100644
--- a/devtools/server/actors/webconsole/commands/manager.js
+++ b/devtools/server/actors/webconsole/commands/manager.js
@@ -11,13 +11,6 @@ loader.lazyRequireGetter(
true
);
-loader.lazyRequireGetter(
- this,
- ["DOM_MUTATIONS"],
- "resource://devtools/server/tracer/tracer.jsm",
- true
-);
-
loader.lazyGetter(this, "l10n", () => {
return new Localization(
[
@@ -27,6 +20,16 @@ loader.lazyGetter(this, "l10n", () => {
true
);
});
+
+const lazy = {};
+ChromeUtils.defineESModuleGetters(
+ lazy,
+ {
+ JSTracer: "resource://devtools/server/tracer/tracer.sys.mjs",
+ },
+ { global: "contextual" }
+);
+
const USAGE_STRING_MAPPING = {
block: "webconsole-commands-usage-block",
trace: "webconsole-commands-usage-trace3",
@@ -888,7 +891,7 @@ WebConsoleCommandsManager.register({
} else if (typeof args["dom-mutations"] == "string") {
// Otherwise consider the value as coma seperated list and remove any white space.
traceDOMMutations = args["dom-mutations"].split(",").map(e => e.trim());
- const acceptedValues = Object.values(DOM_MUTATIONS);
+ const acceptedValues = Object.values(lazy.JSTracer.DOM_MUTATIONS);
if (!traceDOMMutations.every(e => acceptedValues.includes(e))) {
throw new Error(
`:trace --dom-mutations only accept a list of strings whose values can be: ${acceptedValues}`