summaryrefslogtreecommitdiffstats
path: root/devtools/server
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/server')
-rw-r--r--devtools/server/actors/accessibility/accessible.js8
-rw-r--r--devtools/server/actors/addon/addons.js10
-rw-r--r--devtools/server/actors/addon/webextension-inspected-window.js4
-rw-r--r--devtools/server/actors/animation-type-longhand.js1
-rw-r--r--devtools/server/actors/animation.js2
-rw-r--r--devtools/server/actors/breakpoint.js2
-rw-r--r--devtools/server/actors/descriptors/tab.js13
-rw-r--r--devtools/server/actors/descriptors/webextension.js6
-rw-r--r--devtools/server/actors/descriptors/worker.js3
-rw-r--r--devtools/server/actors/device.js3
-rw-r--r--devtools/server/actors/heap-snapshot-file.js2
-rw-r--r--devtools/server/actors/highlighters/css/highlighters.css4
-rw-r--r--devtools/server/actors/highlighters/shapes.js19
-rw-r--r--devtools/server/actors/highlighters/tabbing-order.js8
-rw-r--r--devtools/server/actors/inspector/constants.js2
-rw-r--r--devtools/server/actors/inspector/event-collector.js2
-rw-r--r--devtools/server/actors/inspector/inspector.js7
-rw-r--r--devtools/server/actors/inspector/walker.js44
-rw-r--r--devtools/server/actors/manifest.js10
-rw-r--r--devtools/server/actors/network-monitor/channel-event-sink.js5
-rw-r--r--devtools/server/actors/network-monitor/network-event-actor.js19
-rw-r--r--devtools/server/actors/object.js10
-rw-r--r--devtools/server/actors/object/previewers.js24
-rw-r--r--devtools/server/actors/object/property-iterator.js32
-rw-r--r--devtools/server/actors/objects-manager.js2
-rw-r--r--devtools/server/actors/page-style.js6
-rw-r--r--devtools/server/actors/preference.js2
-rw-r--r--devtools/server/actors/reflow.js4
-rw-r--r--devtools/server/actors/resources/extensions-backgroundscript-status.js2
-rw-r--r--devtools/server/actors/resources/network-events-content.js14
-rw-r--r--devtools/server/actors/resources/network-events-stacktraces.js19
-rw-r--r--devtools/server/actors/resources/network-events.js27
-rw-r--r--devtools/server/actors/resources/parent-process-document-event.js2
-rw-r--r--devtools/server/actors/resources/server-sent-events.js2
-rw-r--r--devtools/server/actors/resources/sources.js14
-rw-r--r--devtools/server/actors/resources/storage/extension-storage.js10
-rw-r--r--devtools/server/actors/resources/storage/indexed-db.js12
-rw-r--r--devtools/server/actors/resources/thread-states.js18
-rw-r--r--devtools/server/actors/resources/utils/content-process-storage.js12
-rw-r--r--devtools/server/actors/resources/utils/nsi-console-listener-watcher.js10
-rw-r--r--devtools/server/actors/resources/utils/parent-process-storage.js3
-rw-r--r--devtools/server/actors/resources/websockets.js4
-rw-r--r--devtools/server/actors/root.js2
-rw-r--r--devtools/server/actors/source.js6
-rw-r--r--devtools/server/actors/target-configuration.js3
-rw-r--r--devtools/server/actors/targets/content-process.js12
-rw-r--r--devtools/server/actors/targets/session-data-processors/blackboxing.js2
-rw-r--r--devtools/server/actors/targets/session-data-processors/breakpoints.js14
-rw-r--r--devtools/server/actors/targets/session-data-processors/event-breakpoints.js2
-rw-r--r--devtools/server/actors/targets/session-data-processors/resources.js2
-rw-r--r--devtools/server/actors/targets/session-data-processors/target-configuration.js9
-rw-r--r--devtools/server/actors/targets/session-data-processors/thread-configuration.js21
-rw-r--r--devtools/server/actors/targets/session-data-processors/xhr-breakpoints.js2
-rw-r--r--devtools/server/actors/targets/target-actor-registry.sys.mjs2
-rw-r--r--devtools/server/actors/targets/window-global.js26
-rw-r--r--devtools/server/actors/targets/worker.js2
-rw-r--r--devtools/server/actors/thread-configuration.js26
-rw-r--r--devtools/server/actors/thread.js111
-rw-r--r--devtools/server/actors/tracer.js154
-rw-r--r--devtools/server/actors/utils/custom-formatters.js4
-rw-r--r--devtools/server/actors/utils/inactive-property-helper.js39
-rw-r--r--devtools/server/actors/utils/logEvent.js4
-rw-r--r--devtools/server/actors/utils/sources-manager.js13
-rw-r--r--devtools/server/actors/utils/stylesheets-manager.js17
-rw-r--r--devtools/server/actors/watcher.js21
-rw-r--r--devtools/server/actors/watcher/WatcherRegistry.sys.mjs84
-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.js11
-rw-r--r--devtools/server/actors/watcher/target-helpers/moz.build1
-rw-r--r--devtools/server/actors/watcher/target-helpers/process-helper.js372
-rw-r--r--devtools/server/actors/webbrowser.js14
-rw-r--r--devtools/server/actors/webconsole.js2
-rw-r--r--devtools/server/actors/webconsole/commands/experimental-commands.ftl22
-rw-r--r--devtools/server/actors/webconsole/commands/manager.js35
-rw-r--r--devtools/server/actors/webconsole/eager-ecma-allowlist.js23
-rw-r--r--devtools/server/actors/webconsole/eval-with-debugger.js20
-rw-r--r--devtools/server/actors/webconsole/listeners/console-file-activity.js2
-rw-r--r--devtools/server/actors/webconsole/listeners/document-events.js10
-rw-r--r--devtools/server/actors/worker/service-worker-registration-list.js7
-rw-r--r--devtools/server/actors/worker/service-worker-registration.js5
-rw-r--r--devtools/server/actors/worker/worker-descriptor-actor-list.js3
-rw-r--r--devtools/server/connectors/content-process-connector.js10
-rw-r--r--devtools/server/connectors/frame-connector.js2
-rw-r--r--devtools/server/connectors/js-process-actor/DevToolsProcessChild.sys.mjs362
-rw-r--r--devtools/server/connectors/js-process-actor/DevToolsProcessParent.sys.mjs256
-rw-r--r--devtools/server/connectors/js-process-actor/moz.build10
-rw-r--r--devtools/server/connectors/js-window-actor/DevToolsFrameChild.sys.mjs16
-rw-r--r--devtools/server/connectors/js-window-actor/DevToolsFrameParent.sys.mjs8
-rw-r--r--devtools/server/connectors/js-window-actor/DevToolsWorkerParent.sys.mjs14
-rw-r--r--devtools/server/connectors/moz.build1
-rw-r--r--devtools/server/connectors/process-actor/DevToolsServiceWorkerParent.sys.mjs14
-rw-r--r--devtools/server/devtools-server-connection.js8
-rw-r--r--devtools/server/performance/memory.js10
-rw-r--r--devtools/server/socket/websocket-server.js4
-rw-r--r--devtools/server/startup/content-process-script.js4
-rw-r--r--devtools/server/tests/browser/browser_accessibility_keyboard_audit.js6
-rw-r--r--devtools/server/tests/browser/browser_connectToFrame.js2
-rw-r--r--devtools/server/tests/browser/browser_debugger_server.js2
-rw-r--r--devtools/server/tests/browser/browser_storage_updates.js2
-rw-r--r--devtools/server/tests/chrome/inactive-property-helper/align-content.mjs24
-rw-r--r--devtools/server/tests/chrome/memory-helpers.js2
-rw-r--r--devtools/server/tests/chrome/suspendTimeouts_content.js2
-rw-r--r--devtools/server/tests/chrome/test_inspector-hide.html2
-rw-r--r--devtools/server/tests/chrome/test_inspector-inactive-property-helper.html3
-rw-r--r--devtools/server/tests/chrome/test_inspector-pseudoclass-lock.html2
-rw-r--r--devtools/server/tests/chrome/test_memory_allocations_04.html2
-rw-r--r--devtools/server/tests/chrome/test_suspendTimeouts.js2
-rw-r--r--devtools/server/tests/xpcshell/registertestactors-lazy.js4
-rw-r--r--devtools/server/tests/xpcshell/test_blackboxing-01.js2
-rw-r--r--devtools/server/tests/xpcshell/test_blackboxing-02.js2
-rw-r--r--devtools/server/tests/xpcshell/test_blackboxing-05.js2
-rw-r--r--devtools/server/tests/xpcshell/test_breakpoint-03.js2
-rw-r--r--devtools/server/tests/xpcshell/test_breakpoint-05.js2
-rw-r--r--devtools/server/tests/xpcshell/test_breakpoint-06.js2
-rw-r--r--devtools/server/tests/xpcshell/test_breakpoint-07.js2
-rw-r--r--devtools/server/tests/xpcshell/test_breakpoint-08.js2
-rw-r--r--devtools/server/tests/xpcshell/test_breakpoint-09.js2
-rw-r--r--devtools/server/tests/xpcshell/test_breakpoint-11.js2
-rw-r--r--devtools/server/tests/xpcshell/test_breakpoint-12.js4
-rw-r--r--devtools/server/tests/xpcshell/test_breakpoint-14.js2
-rw-r--r--devtools/server/tests/xpcshell/test_breakpoint-16.js2
-rw-r--r--devtools/server/tests/xpcshell/test_client_request.js2
-rw-r--r--devtools/server/tests/xpcshell/test_conditional_breakpoint-04.js2
-rw-r--r--devtools/server/tests/xpcshell/test_dbgglobal.js4
-rw-r--r--devtools/server/tests/xpcshell/test_forwardingprefix.js6
-rw-r--r--devtools/server/tests/xpcshell/test_framearguments-01.js2
-rw-r--r--devtools/server/tests/xpcshell/test_framebindings-07.js2
-rw-r--r--devtools/server/tests/xpcshell/test_functiongrips-01.js2
-rw-r--r--devtools/server/tests/xpcshell/test_getRuleText.js81
-rw-r--r--devtools/server/tests/xpcshell/test_interrupt.js2
-rw-r--r--devtools/server/tests/xpcshell/test_logpoint-01.js2
-rw-r--r--devtools/server/tests/xpcshell/test_logpoint-02.js2
-rw-r--r--devtools/server/tests/xpcshell/test_logpoint-03.js4
-rw-r--r--devtools/server/tests/xpcshell/test_longstringgrips-01.js2
-rw-r--r--devtools/server/tests/xpcshell/test_objectgrips-02.js2
-rw-r--r--devtools/server/tests/xpcshell/test_objectgrips-03.js2
-rw-r--r--devtools/server/tests/xpcshell/test_objectgrips-04.js2
-rw-r--r--devtools/server/tests/xpcshell/test_objectgrips-05.js2
-rw-r--r--devtools/server/tests/xpcshell/test_objectgrips-06.js2
-rw-r--r--devtools/server/tests/xpcshell/test_objectgrips-07.js2
-rw-r--r--devtools/server/tests/xpcshell/test_objectgrips-08.js2
-rw-r--r--devtools/server/tests/xpcshell/test_objectgrips-16.js2
-rw-r--r--devtools/server/tests/xpcshell/test_objectgrips-17.js2
-rw-r--r--devtools/server/tests/xpcshell/test_objectgrips-18.js2
-rw-r--r--devtools/server/tests/xpcshell/test_objectgrips-19.js4
-rw-r--r--devtools/server/tests/xpcshell/test_objectgrips-20.js2
-rw-r--r--devtools/server/tests/xpcshell/test_objectgrips-21.js2
-rw-r--r--devtools/server/tests/xpcshell/test_objectgrips-22.js2
-rw-r--r--devtools/server/tests/xpcshell/test_objectgrips-25.js2
-rw-r--r--devtools/server/tests/xpcshell/test_objectgrips-fn-apply-01.js2
-rw-r--r--devtools/server/tests/xpcshell/test_objectgrips-fn-apply-02.js2
-rw-r--r--devtools/server/tests/xpcshell/test_objectgrips-fn-apply-03.js2
-rw-r--r--devtools/server/tests/xpcshell/test_objectgrips-nested-promise.js2
-rw-r--r--devtools/server/tests/xpcshell/test_objectgrips-nested-proxy.js2
-rw-r--r--devtools/server/tests/xpcshell/test_objectgrips-property-value-01.js2
-rw-r--r--devtools/server/tests/xpcshell/test_objectgrips-property-value-02.js2
-rw-r--r--devtools/server/tests/xpcshell/test_objectgrips-sparse-array.js2
-rw-r--r--devtools/server/tests/xpcshell/test_pause_exceptions-04.js2
-rw-r--r--devtools/server/tests/xpcshell/test_pauselifetime-02.js2
-rw-r--r--devtools/server/tests/xpcshell/test_pauselifetime-03.js2
-rw-r--r--devtools/server/tests/xpcshell/test_pauselifetime-04.js2
-rw-r--r--devtools/server/tests/xpcshell/test_promises_run_to_completion.js4
-rw-r--r--devtools/server/tests/xpcshell/test_restartFrame-01.js2
-rw-r--r--devtools/server/tests/xpcshell/test_source-01.js2
-rw-r--r--devtools/server/tests/xpcshell/test_source-02.js2
-rw-r--r--devtools/server/tests/xpcshell/test_source-03.js4
-rw-r--r--devtools/server/tests/xpcshell/test_source-04.js4
-rw-r--r--devtools/server/tests/xpcshell/test_stepping-01.js2
-rw-r--r--devtools/server/tests/xpcshell/test_stepping-18.js2
-rw-r--r--devtools/server/tests/xpcshell/test_stepping-19.js2
-rw-r--r--devtools/server/tests/xpcshell/test_threadlifetime-01.js2
-rw-r--r--devtools/server/tests/xpcshell/test_threadlifetime-02.js2
-rw-r--r--devtools/server/tests/xpcshell/test_threadlifetime-04.js2
-rw-r--r--devtools/server/tests/xpcshell/test_wasm_source-01.js4
-rw-r--r--devtools/server/tests/xpcshell/testactors.js6
-rw-r--r--devtools/server/tracer/tests/browser/browser_document_tracer.js2
-rw-r--r--devtools/server/tracer/tests/browser/browser_worker_tracer.js2
-rw-r--r--devtools/server/tracer/tests/xpcshell/test_tracer.js264
-rw-r--r--devtools/server/tracer/tracer.jsm336
179 files changed, 2282 insertions, 867 deletions
diff --git a/devtools/server/actors/accessibility/accessible.js b/devtools/server/actors/accessibility/accessible.js
index 1866d0a91b..451fa7f99a 100644
--- a/devtools/server/actors/accessibility/accessible.js
+++ b/devtools/server/actors/accessibility/accessible.js
@@ -67,11 +67,9 @@ loader.lazyGetter(
() =>
ChromeUtils.importESModule(
"resource://gre/modules/ContentDOMReference.sys.mjs",
- {
- // ContentDOMReference needs to be retrieved from the shared global
- // since it is a shared singleton.
- loadInDevToolsLoader: false,
- }
+ // ContentDOMReference needs to be retrieved from the shared global
+ // since it is a shared singleton.
+ { global: "shared" }
).ContentDOMReference
);
diff --git a/devtools/server/actors/addon/addons.js b/devtools/server/actors/addon/addons.js
index 95a3738d61..9d37df93e4 100644
--- a/devtools/server/actors/addon/addons.js
+++ b/devtools/server/actors/addon/addons.js
@@ -11,10 +11,11 @@ const {
const { AddonManager } = ChromeUtils.importESModule(
"resource://gre/modules/AddonManager.sys.mjs",
- { loadInDevToolsLoader: false }
+ { global: "shared" }
);
const { FileUtils } = ChromeUtils.importESModule(
- "resource://gre/modules/FileUtils.sys.mjs"
+ "resource://gre/modules/FileUtils.sys.mjs",
+ { global: "contextual" }
);
// This actor is used by DevTools as well as external tools such as webext-run
@@ -43,12 +44,13 @@ class AddonsActor extends Actor {
// so for now, there is no chance of calling this on Android.
if (openDevTools) {
// This module is typically loaded in the loader spawn by DevToolsStartup,
- // in a distinct compartment thanks to useDistinctSystemPrincipalLoader and loadInDevToolsLoader flag.
+ // in a distinct compartment thanks to useDistinctSystemPrincipalLoader
+ // and global flag.
// But here we want to reuse the shared module loader.
// We do not want to load devtools.js in the server's distinct module loader.
const loader = ChromeUtils.importESModule(
"resource://devtools/shared/loader/Loader.sys.mjs",
- { loadInDevToolsLoader: false }
+ { global: "shared" }
);
const {
gDevTools,
diff --git a/devtools/server/actors/addon/webextension-inspected-window.js b/devtools/server/actors/addon/webextension-inspected-window.js
index e69a206c9d..041045eec0 100644
--- a/devtools/server/actors/addon/webextension-inspected-window.js
+++ b/devtools/server/actors/addon/webextension-inspected-window.js
@@ -218,7 +218,7 @@ class CustomizedReload {
return this.waitForReloadCompleted;
}
- observe(subject, topic, data) {
+ observe(subject, topic) {
if (topic !== "initial-document-element-inserted") {
return;
}
@@ -319,7 +319,7 @@ class WebExtensionInspectedWindowActor extends Actor {
this.targetActor = targetActor;
}
- destroy(conn) {
+ destroy() {
super.destroy();
if (this.customizedReload) {
diff --git a/devtools/server/actors/animation-type-longhand.js b/devtools/server/actors/animation-type-longhand.js
index febf8457ad..6da49c7b4b 100644
--- a/devtools/server/actors/animation-type-longhand.js
+++ b/devtools/server/actors/animation-type-longhand.js
@@ -258,6 +258,7 @@ exports.ANIMATION_TYPE_FOR_LONGHANDS = [
"scroll-timeline-axis",
"scroll-timeline-name",
"size",
+ "transition-behavior",
"transition-delay",
"transition-duration",
"transition-property",
diff --git a/devtools/server/actors/animation.js b/devtools/server/actors/animation.js
index d8de85fd73..89a2f5e84d 100644
--- a/devtools/server/actors/animation.js
+++ b/devtools/server/actors/animation.js
@@ -171,7 +171,7 @@ class AnimationPlayerActor extends Actor {
*/
release() {}
- form(detail) {
+ form() {
const data = this.getCurrentState();
data.actor = this.actorID;
diff --git a/devtools/server/actors/breakpoint.js b/devtools/server/actors/breakpoint.js
index d1a469658c..bfa563bf55 100644
--- a/devtools/server/actors/breakpoint.js
+++ b/devtools/server/actors/breakpoint.js
@@ -90,7 +90,7 @@ class BreakpointActor {
/**
* Called on changes to this breakpoint's script offsets or options.
*/
- _newOffsetsOrOptions(script, offsets, oldOptions) {
+ _newOffsetsOrOptions(script, offsets) {
// Clear any existing handler first in case this is called multiple times
// after options change.
for (const offset of offsets) {
diff --git a/devtools/server/actors/descriptors/tab.js b/devtools/server/actors/descriptors/tab.js
index ea20d3fb36..f5980126cb 100644
--- a/devtools/server/actors/descriptors/tab.js
+++ b/devtools/server/actors/descriptors/tab.js
@@ -21,12 +21,17 @@ const {
connectToFrame,
} = require("resource://devtools/server/connectors/frame-connector.js");
const lazy = {};
-ChromeUtils.defineESModuleGetters(lazy, {
- PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs",
-});
+ChromeUtils.defineESModuleGetters(
+ lazy,
+ {
+ PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs",
+ },
+ { global: "contextual" }
+);
const { AppConstants } = ChromeUtils.importESModule(
- "resource://gre/modules/AppConstants.sys.mjs"
+ "resource://gre/modules/AppConstants.sys.mjs",
+ { global: "contextual" }
);
const {
createBrowserElementSessionContext,
diff --git a/devtools/server/actors/descriptors/webextension.js b/devtools/server/actors/descriptors/webextension.js
index 56e4abfc41..b202036aef 100644
--- a/devtools/server/actors/descriptors/webextension.js
+++ b/devtools/server/actors/descriptors/webextension.js
@@ -28,13 +28,13 @@ const lazy = {};
loader.lazyGetter(lazy, "AddonManager", () => {
return ChromeUtils.importESModule(
"resource://gre/modules/AddonManager.sys.mjs",
- { loadInDevToolsLoader: false }
+ { global: "shared" }
).AddonManager;
});
loader.lazyGetter(lazy, "ExtensionParent", () => {
return ChromeUtils.importESModule(
"resource://gre/modules/ExtensionParent.sys.mjs",
- { loadInDevToolsLoader: false }
+ { global: "shared" }
).ExtensionParent;
});
loader.lazyRequireGetter(
@@ -202,7 +202,7 @@ class WebExtensionDescriptorActor extends Actor {
*
* bypassCache has no impact for addon reloads.
*/
- reloadDescriptor({ bypassCache }) {
+ reloadDescriptor() {
return this.reload();
}
diff --git a/devtools/server/actors/descriptors/worker.js b/devtools/server/actors/descriptors/worker.js
index 89ca918e05..4c8689fac8 100644
--- a/devtools/server/actors/descriptors/worker.js
+++ b/devtools/server/actors/descriptors/worker.js
@@ -23,7 +23,8 @@ const {
DevToolsServer,
} = require("resource://devtools/server/devtools-server.js");
const { XPCOMUtils } = ChromeUtils.importESModule(
- "resource://gre/modules/XPCOMUtils.sys.mjs"
+ "resource://gre/modules/XPCOMUtils.sys.mjs",
+ { global: "contextual" }
);
const {
createWorkerSessionContext,
diff --git a/devtools/server/actors/device.js b/devtools/server/actors/device.js
index 2aa05959e5..69ec02a3ec 100644
--- a/devtools/server/actors/device.js
+++ b/devtools/server/actors/device.js
@@ -12,7 +12,8 @@ const {
} = require("resource://devtools/server/devtools-server.js");
const { getSystemInfo } = require("resource://devtools/shared/system.js");
const { AppConstants } = ChromeUtils.importESModule(
- "resource://gre/modules/AppConstants.sys.mjs"
+ "resource://gre/modules/AppConstants.sys.mjs",
+ { global: "contextual" }
);
exports.DeviceActor = class DeviceActor extends Actor {
diff --git a/devtools/server/actors/heap-snapshot-file.js b/devtools/server/actors/heap-snapshot-file.js
index f3fd9242b2..942f72a98b 100644
--- a/devtools/server/actors/heap-snapshot-file.js
+++ b/devtools/server/actors/heap-snapshot-file.js
@@ -27,7 +27,7 @@ loader.lazyRequireGetter(
* system.
*/
exports.HeapSnapshotFileActor = class HeapSnapshotFileActor extends Actor {
- constructor(conn, parent) {
+ constructor(conn) {
super(conn, heapSnapshotFileSpec);
if (
diff --git a/devtools/server/actors/highlighters/css/highlighters.css b/devtools/server/actors/highlighters/css/highlighters.css
index 33c8a04aae..04584e4bf1 100644
--- a/devtools/server/actors/highlighters/css/highlighters.css
+++ b/devtools/server/actors/highlighters/css/highlighters.css
@@ -1010,12 +1010,12 @@ button.paused-dbg-resume-button {
}
.accessible-infobar-audit .accessible-audit.WARNING::before {
- background-image: url(chrome://devtools/skin/images/alert-small.svg);
+ background-image: url(resource://devtools-shared-images/alert-small.svg);
fill: var(--yellow-60);
}
.accessible-infobar-audit .accessible-audit.BEST_PRACTICES::before {
- background-image: url(chrome://devtools/skin/images/info-small.svg);
+ background-image: url(resource://devtools-shared-images/info-small.svg);
}
.accessible-infobar-name {
diff --git a/devtools/server/actors/highlighters/shapes.js b/devtools/server/actors/highlighters/shapes.js
index a77e8c31be..6cc9194f57 100644
--- a/devtools/server/actors/highlighters/shapes.js
+++ b/devtools/server/actors/highlighters/shapes.js
@@ -519,7 +519,7 @@ class ShapesHighlighter extends AutoRefreshHighlighter {
}
// eslint-disable-next-line complexity
- handleEvent(event, id) {
+ handleEvent(event) {
// No event handling if the highlighter is hidden
if (this.areShapesHidden()) {
return;
@@ -1222,7 +1222,7 @@ class ShapesHighlighter extends AutoRefreshHighlighter {
coordinates.splice(point, 1);
let polygonDef = this.fillRule ? `${this.fillRule}, ` : "";
polygonDef += coordinates
- .map((coords, i) => {
+ .map(coords => {
return `${coords[0]} ${coords[1]}`;
})
.join(", ");
@@ -2735,11 +2735,8 @@ class ShapesHighlighter extends AutoRefreshHighlighter {
/**
* Update the SVG polygon to fit the CSS polygon.
- * @param {Number} width the width of the element quads
- * @param {Number} height the height of the element quads
- * @param {Number} zoom the zoom level of the window
*/
- _updatePolygonShape(width, height, zoom) {
+ _updatePolygonShape() {
// Draw and show the polygon.
const points = this.coordinates.map(point => point.join(",")).join(" ");
@@ -2758,11 +2755,8 @@ class ShapesHighlighter extends AutoRefreshHighlighter {
/**
* Update the SVG ellipse to fit the CSS circle or ellipse.
- * @param {Number} width the width of the element quads
- * @param {Number} height the height of the element quads
- * @param {Number} zoom the zoom level of the window
*/
- _updateEllipseShape(width, height, zoom) {
+ _updateEllipseShape() {
const { rx, ry, cx, cy } = this.coordinates;
const ellipseEl = this.getElement("ellipse");
ellipseEl.setAttribute("rx", rx);
@@ -2788,11 +2782,8 @@ class ShapesHighlighter extends AutoRefreshHighlighter {
/**
* Update the SVG rect to fit the CSS inset.
- * @param {Number} width the width of the element quads
- * @param {Number} height the height of the element quads
- * @param {Number} zoom the zoom level of the window
*/
- _updateInsetShape(width, height, zoom) {
+ _updateInsetShape() {
const { top, left, right, bottom } = this.coordinates;
const rectEl = this.getElement("rect");
rectEl.setAttribute("x", left);
diff --git a/devtools/server/actors/highlighters/tabbing-order.js b/devtools/server/actors/highlighters/tabbing-order.js
index ab96d30fe6..ccc70779bb 100644
--- a/devtools/server/actors/highlighters/tabbing-order.js
+++ b/devtools/server/actors/highlighters/tabbing-order.js
@@ -11,11 +11,9 @@ loader.lazyGetter(
() =>
ChromeUtils.importESModule(
"resource://gre/modules/ContentDOMReference.sys.mjs",
- {
- // ContentDOMReference needs to be retrieved from the shared global
- // since it is a shared singleton.
- loadInDevToolsLoader: false,
- }
+ // ContentDOMReference needs to be retrieved from the shared global
+ // since it is a shared singleton.
+ { global: "shared" }
).ContentDOMReference
);
loader.lazyRequireGetter(
diff --git a/devtools/server/actors/inspector/constants.js b/devtools/server/actors/inspector/constants.js
index c253c67b02..b55cd58389 100644
--- a/devtools/server/actors/inspector/constants.js
+++ b/devtools/server/actors/inspector/constants.js
@@ -10,7 +10,7 @@
*
* const someListener = () => {};
* someListener[EXCLUDED_LISTENER] = true;
- * eventListenerService.addSystemEventListener(node, "event", someListener);
+ * node.addEventListener("event", someListener);
*/
const EXCLUDED_LISTENER = Symbol("event-collector-excluded-listener");
diff --git a/devtools/server/actors/inspector/event-collector.js b/devtools/server/actors/inspector/event-collector.js
index 4ee8dc388f..7ae7ac45d7 100644
--- a/devtools/server/actors/inspector/event-collector.js
+++ b/devtools/server/actors/inspector/event-collector.js
@@ -241,7 +241,7 @@ class MainEventCollector {
* @return {Array}
* An array of event handlers.
*/
- getListeners(node, { checkOnly }) {
+ getListeners(_node, { checkOnly: _checkOnly }) {
throw new Error("You have to implement the method getListeners()!");
}
diff --git a/devtools/server/actors/inspector/inspector.js b/devtools/server/actors/inspector/inspector.js
index cdfa892889..95f7f76622 100644
--- a/devtools/server/actors/inspector/inspector.js
+++ b/devtools/server/actors/inspector/inspector.js
@@ -56,7 +56,8 @@ const {
} = require("resource://devtools/shared/specs/inspector.js");
const { setTimeout } = ChromeUtils.importESModule(
- "resource://gre/modules/Timer.sys.mjs"
+ "resource://gre/modules/Timer.sys.mjs",
+ { global: "contextual" }
);
const {
LongStringActor,
@@ -181,7 +182,7 @@ class InspectorActor extends Actor {
return this._pageStylePromise;
}
- this._pageStylePromise = this.getWalker().then(walker => {
+ this._pageStylePromise = this.getWalker().then(() => {
const pageStyle = new PageStyleActor(this);
this.manage(pageStyle);
return pageStyle;
@@ -263,7 +264,7 @@ class InspectorActor extends Actor {
return url;
}
- const baseURI = Services.io.newURI(document.location.href);
+ const baseURI = Services.io.newURI(document.baseURI);
return Services.io.newURI(url, null, baseURI).spec;
}
diff --git a/devtools/server/actors/inspector/walker.js b/devtools/server/actors/inspector/walker.js
index f8da1385e9..50df1720b7 100644
--- a/devtools/server/actors/inspector/walker.js
+++ b/devtools/server/actors/inspector/walker.js
@@ -111,11 +111,9 @@ if (!isWorker) {
() =>
ChromeUtils.importESModule(
"resource://gre/modules/ContentDOMReference.sys.mjs",
- {
- // ContentDOMReference needs to be retrieved from the shared global
- // since it is a shared singleton.
- loadInDevToolsLoader: false,
- }
+ // ContentDOMReference needs to be retrieved from the shared global
+ // since it is a shared singleton.
+ { global: "shared" }
).ContentDOMReference
);
}
@@ -341,7 +339,11 @@ class WalkerActor extends Actor {
return {
actor: this.actorID,
root: this.rootNode.form(),
- traits: {},
+ traits: {
+ // @backward-compat { version 125 } Indicate to the client that it can use getIdrefNode.
+ // This trait can be removed once 125 hits release.
+ hasGetIdrefNode: true,
+ },
};
}
@@ -524,7 +526,7 @@ class WalkerActor extends Actor {
* When a custom element is defined, send a customElementDefined mutation for all the
* NodeActors using this tag name.
*/
- onCustomElementDefined({ name, actors }) {
+ onCustomElementDefined({ actors }) {
actors.forEach(actor =>
this.queueMutation({
target: actor.actorID,
@@ -534,7 +536,7 @@ class WalkerActor extends Actor {
);
}
- _onReflows(reflows) {
+ _onReflows() {
// Going through the nodes the walker knows about, see which ones have had their
// containerType, display, scrollable or overflow state changed and send events if any.
const containerTypeChanges = [];
@@ -1070,6 +1072,32 @@ class WalkerActor extends Actor {
}
/**
+ * Return the node in the baseNode rootNode matching the passed id referenced in a
+ * idref/idreflist attribute, as those are scoped within a shadow root.
+ *
+ * @param NodeActor baseNode
+ * @param string id
+ */
+ getIdrefNode(baseNode, id) {
+ if (isNodeDead(baseNode)) {
+ return {};
+ }
+
+ // Get the document or the shadow root for baseNode
+ const rootNode = baseNode.rawNode.getRootNode({ composed: false });
+ if (!rootNode) {
+ return {};
+ }
+
+ const node = rootNode.getElementById(id);
+ if (!node) {
+ return {};
+ }
+
+ return this.attachElement(node);
+ }
+
+ /**
* Get a list of nodes that match the given selector in all known frames of
* the current content page.
* @param {String} selector.
diff --git a/devtools/server/actors/manifest.js b/devtools/server/actors/manifest.js
index 5436d4a53a..183ce1edea 100644
--- a/devtools/server/actors/manifest.js
+++ b/devtools/server/actors/manifest.js
@@ -11,9 +11,13 @@ const {
const lazy = {};
-ChromeUtils.defineESModuleGetters(lazy, {
- ManifestObtainer: "resource://gre/modules/ManifestObtainer.sys.mjs",
-});
+ChromeUtils.defineESModuleGetters(
+ lazy,
+ {
+ ManifestObtainer: "resource://gre/modules/ManifestObtainer.sys.mjs",
+ },
+ { global: "contextual" }
+);
/**
* An actor for a Web Manifest
diff --git a/devtools/server/actors/network-monitor/channel-event-sink.js b/devtools/server/actors/network-monitor/channel-event-sink.js
index 8ff00302f9..3db6bf148e 100644
--- a/devtools/server/actors/network-monitor/channel-event-sink.js
+++ b/devtools/server/actors/network-monitor/channel-event-sink.js
@@ -5,11 +5,12 @@
"use strict";
const { ComponentUtils } = ChromeUtils.importESModule(
- "resource://gre/modules/ComponentUtils.sys.mjs"
+ "resource://gre/modules/ComponentUtils.sys.mjs",
+ { global: "contextual" }
);
/**
- * This is a nsIChannelEventSink implementation that monitors channel redirects and
+ * THIS is a nsIChannelEventSink implementation that monitors channel redirects and
* informs the registered "collectors" about the old and new channels.
*/
const SINK_CLASS_DESCRIPTION = "NetworkMonitor Channel Event Sink";
diff --git a/devtools/server/actors/network-monitor/network-event-actor.js b/devtools/server/actors/network-monitor/network-event-actor.js
index e59738dd38..38607279d4 100644
--- a/devtools/server/actors/network-monitor/network-event-actor.js
+++ b/devtools/server/actors/network-monitor/network-event-actor.js
@@ -18,10 +18,14 @@ const {
const lazy = {};
-ChromeUtils.defineESModuleGetters(lazy, {
- NetworkUtils:
- "resource://devtools/shared/network-observer/NetworkUtils.sys.mjs",
-});
+ChromeUtils.defineESModuleGetters(
+ lazy,
+ {
+ NetworkUtils:
+ "resource://devtools/shared/network-observer/NetworkUtils.sys.mjs",
+ },
+ { global: "contextual" }
+);
const CONTENT_TYPE_REGEXP = /^content-type/i;
@@ -559,13 +563,8 @@ class NetworkEventActor extends Actor {
* @param object content
* The response content.
* @param object
- * - boolean discardedResponseBody
- * Tells if the response content was recorded or not.
*/
- addResponseContent(
- content,
- { discardResponseBody, blockedReason, blockingExtension }
- ) {
+ addResponseContent(content, { blockedReason, blockingExtension }) {
// Ignore calls when this actor is already destroyed
if (this.isDestroyed()) {
return;
diff --git a/devtools/server/actors/object.js b/devtools/server/actors/object.js
index 9b52c4ebe7..1d16eb2b75 100644
--- a/devtools/server/actors/object.js
+++ b/devtools/server/actors/object.js
@@ -63,11 +63,9 @@ if (!isWorker) {
() =>
ChromeUtils.importESModule(
"resource://gre/modules/ContentDOMReference.sys.mjs",
- {
- // ContentDOMReference needs to be retrieved from the shared global
- // since it is a shared singleton.
- loadInDevToolsLoader: false,
- }
+ // ContentDOMReference needs to be retrieved from the shared global
+ // since it is a shared singleton.
+ { global: "shared" }
).ContentDOMReference
);
}
@@ -171,7 +169,7 @@ class ObjectActor extends Actor {
}
// Only process custom formatters if the feature is enabled.
- if (this.thread?._parent?.customFormatters) {
+ if (this.thread?.targetActor?.customFormatters) {
const result = customFormatterHeader(this);
if (result) {
const { formatter, ...header } = result;
diff --git a/devtools/server/actors/object/previewers.js b/devtools/server/actors/object/previewers.js
index 451858a826..55217a72ee 100644
--- a/devtools/server/actors/object/previewers.js
+++ b/devtools/server/actors/object/previewers.js
@@ -612,6 +612,30 @@ const previewers = {
return true;
},
],
+
+ CustomStateSet: [
+ function(objectActor, grip) {
+ const size = DevToolsUtils.getProperty(objectActor.obj, "size");
+ if (typeof size != "number") {
+ return false;
+ }
+
+ grip.preview = {
+ kind: "ArrayLike",
+ length: size,
+ };
+
+ const items = (grip.preview.items = []);
+ for (const item of PropertyIterators.enumCustomStateSetEntries(objectActor)) {
+ items.push(item);
+ if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
+ break;
+ }
+ }
+
+ return true;
+ },
+ ],
};
/**
diff --git a/devtools/server/actors/object/property-iterator.js b/devtools/server/actors/object/property-iterator.js
index 7bd2c0a704..e7a4d2c041 100644
--- a/devtools/server/actors/object/property-iterator.js
+++ b/devtools/server/actors/object/property-iterator.js
@@ -75,6 +75,8 @@ class PropertyIteratorActor extends Actor {
this.iterator = enumMidiInputMapEntries(objectActor);
} else if (cls == "MIDIOutputMap") {
this.iterator = enumMidiOutputMapEntries(objectActor);
+ } else if (cls == "CustomStateSet") {
+ this.iterator = enumCustomStateSetEntries(objectActor);
} else {
throw new Error(
"Unsupported class to enumerate entries from: " + cls
@@ -658,6 +660,35 @@ function enumWeakSetEntries(objectActor) {
};
}
+function enumCustomStateSetEntries(objectActor) {
+ let raw = objectActor.obj.unsafeDereference();
+ // We need to waive `raw` as we can't get the iterator from the Xray for SetLike (See Bug 1173651).
+ // We also need to waive Xrays on the result of the call to `values` as we don't have
+ // Xrays to Iterator objects (see Bug 1023984)
+ const values = Array.from(
+ waiveXrays(CustomStateSet.prototype.values.call(waiveXrays(raw)))
+ );
+
+ return {
+ [Symbol.iterator]: function*() {
+ for (const item of values) {
+ yield gripFromEntry(objectActor, item);
+ }
+ },
+ size: values.length,
+ propertyName(index) {
+ return index;
+ },
+ propertyDescription(index) {
+ const val = values[index];
+ return {
+ enumerable: true,
+ value: gripFromEntry(objectActor, val),
+ };
+ },
+ };
+}
+
/**
* Returns true if the parameter can be stored as a 32-bit unsigned integer.
* If so, it will be suitable for use as the length of an array object.
@@ -672,6 +703,7 @@ function isUint32(num) {
module.exports = {
PropertyIteratorActor,
+ enumCustomStateSetEntries,
enumMapEntries,
enumMidiInputMapEntries,
enumMidiOutputMapEntries,
diff --git a/devtools/server/actors/objects-manager.js b/devtools/server/actors/objects-manager.js
index 529b33b246..9338f7c8f6 100644
--- a/devtools/server/actors/objects-manager.js
+++ b/devtools/server/actors/objects-manager.js
@@ -14,7 +14,7 @@ const {
* inspected by DevTools. Typically from the Console or Debugger.
*/
class ObjectsManagerActor extends Actor {
- constructor(conn, targetActor) {
+ constructor(conn) {
super(conn, objectsManagerSpec);
}
diff --git a/devtools/server/actors/page-style.js b/devtools/server/actors/page-style.js
index cfaa35ed46..1783b58a8f 100644
--- a/devtools/server/actors/page-style.js
+++ b/devtools/server/actors/page-style.js
@@ -727,9 +727,13 @@ class PageStyleActor extends Actor {
case "::-moz-range-progress":
case "::-moz-range-thumb":
case "::-moz-range-track":
+ case "::slider-fill":
+ case "::slider-thumb":
+ case "::slider-track":
return node.nodeName == "INPUT" && node.type == "range";
default:
- throw Error("Unhandled pseudo-element " + pseudo);
+ console.error("Unhandled pseudo-element " + pseudo);
+ return false;
}
}
diff --git a/devtools/server/actors/preference.js b/devtools/server/actors/preference.js
index 3435fe9eb1..17a3116d9e 100644
--- a/devtools/server/actors/preference.js
+++ b/devtools/server/actors/preference.js
@@ -57,7 +57,7 @@ class PreferenceActor extends Actor {
getAllPrefs() {
const prefs = {};
- Services.prefs.getChildList("").forEach(function (name, index) {
+ Services.prefs.getChildList("").forEach(function (name) {
// append all key/value pairs into a huge json object.
try {
let value;
diff --git a/devtools/server/actors/reflow.js b/devtools/server/actors/reflow.js
index 3eda67cc8c..6fd3962d75 100644
--- a/devtools/server/actors/reflow.js
+++ b/devtools/server/actors/reflow.js
@@ -162,11 +162,11 @@ class Observable {
}
}
- _startListeners(windows) {
+ _startListeners() {
// To be implemented by sub-classes.
}
- _stopListeners(windows) {
+ _stopListeners() {
// To be implemented by sub-classes.
}
diff --git a/devtools/server/actors/resources/extensions-backgroundscript-status.js b/devtools/server/actors/resources/extensions-backgroundscript-status.js
index 08f51a23f5..f6c7b3067d 100644
--- a/devtools/server/actors/resources/extensions-backgroundscript-status.js
+++ b/devtools/server/actors/resources/extensions-backgroundscript-status.js
@@ -35,7 +35,7 @@ class ExtensionsBackgroundScriptStatusWatcher {
Services.obs.addObserver(this, "extension:background-script-status");
}
- observe(subject, topic, data) {
+ observe(subject, topic) {
switch (topic) {
case "extension:background-script-status": {
const { addonId, isRunning } = subject.wrappedJSObject;
diff --git a/devtools/server/actors/resources/network-events-content.js b/devtools/server/actors/resources/network-events-content.js
index 5135583fab..2b4b09dfa7 100644
--- a/devtools/server/actors/resources/network-events-content.js
+++ b/devtools/server/actors/resources/network-events-content.js
@@ -13,10 +13,14 @@ loader.lazyRequireGetter(
const lazy = {};
-ChromeUtils.defineESModuleGetters(lazy, {
- NetworkUtils:
- "resource://devtools/shared/network-observer/NetworkUtils.sys.mjs",
-});
+ChromeUtils.defineESModuleGetters(
+ lazy,
+ {
+ NetworkUtils:
+ "resource://devtools/shared/network-observer/NetworkUtils.sys.mjs",
+ },
+ { global: "contextual" }
+);
/**
* Handles network events from the content process
@@ -64,7 +68,7 @@ class NetworkEventContentWatcher {
this.networkEvents.clear();
}
- httpFailedOpeningRequest(subject, topic) {
+ httpFailedOpeningRequest(subject) {
const channel = subject.QueryInterface(Ci.nsIHttpChannel);
// Ignore preload requests to avoid duplicity request entries in
diff --git a/devtools/server/actors/resources/network-events-stacktraces.js b/devtools/server/actors/resources/network-events-stacktraces.js
index a458278680..da082069f5 100644
--- a/devtools/server/actors/resources/network-events-stacktraces.js
+++ b/devtools/server/actors/resources/network-events-stacktraces.js
@@ -17,10 +17,14 @@ loader.lazyRequireGetter(
const lazy = {};
-ChromeUtils.defineESModuleGetters(lazy, {
- NetworkUtils:
- "resource://devtools/shared/network-observer/NetworkUtils.sys.mjs",
-});
+ChromeUtils.defineESModuleGetters(
+ lazy,
+ {
+ NetworkUtils:
+ "resource://devtools/shared/network-observer/NetworkUtils.sys.mjs",
+ },
+ { global: "contextual" }
+);
class NetworkEventStackTracesWatcher {
/**
@@ -53,11 +57,8 @@ class NetworkEventStackTracesWatcher {
/**
* Stop watching for network event's strack traces related to a given Target Actor.
- *
- * @param TargetActor targetActor
- * The target actor from which we should stop observing the strack traces
*/
- destroy(targetActor) {
+ destroy() {
this.clear();
Services.obs.removeObserver(this, "http-on-opening-request");
Services.obs.removeObserver(this, "document-on-opening-request");
@@ -65,7 +66,7 @@ class NetworkEventStackTracesWatcher {
ChannelEventSinkFactory.getService().unregisterCollector(this);
}
- onChannelRedirect(oldChannel, newChannel, flags) {
+ onChannelRedirect(oldChannel, newChannel) {
// We can be called with any nsIChannel, but are interested only in HTTP channels
try {
oldChannel.QueryInterface(Ci.nsIHttpChannel);
diff --git a/devtools/server/actors/resources/network-events.js b/devtools/server/actors/resources/network-events.js
index c1440f2c8d..909c16e052 100644
--- a/devtools/server/actors/resources/network-events.js
+++ b/devtools/server/actors/resources/network-events.js
@@ -6,26 +6,29 @@
const { Pool } = require("resource://devtools/shared/protocol/Pool.js");
const { isWindowGlobalPartOfContext } = ChromeUtils.importESModule(
- "resource://devtools/server/actors/watcher/browsing-context-helpers.sys.mjs"
+ "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
- // which also has to be a true singleton.
- loadInDevToolsLoader: false,
- }
+ // WatcherRegistry 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 lazy = {};
-ChromeUtils.defineESModuleGetters(lazy, {
- NetworkObserver:
- "resource://devtools/shared/network-observer/NetworkObserver.sys.mjs",
- NetworkUtils:
- "resource://devtools/shared/network-observer/NetworkUtils.sys.mjs",
-});
+ChromeUtils.defineESModuleGetters(
+ lazy,
+ {
+ NetworkObserver:
+ "resource://devtools/shared/network-observer/NetworkObserver.sys.mjs",
+ NetworkUtils:
+ "resource://devtools/shared/network-observer/NetworkUtils.sys.mjs",
+ },
+ { global: "contextual" }
+);
loader.lazyRequireGetter(
this,
diff --git a/devtools/server/actors/resources/parent-process-document-event.js b/devtools/server/actors/resources/parent-process-document-event.js
index e156a32fe5..27f929190c 100644
--- a/devtools/server/actors/resources/parent-process-document-event.js
+++ b/devtools/server/actors/resources/parent-process-document-event.js
@@ -98,7 +98,7 @@ class ParentProcessDocumentEventWatcher {
return Promise.resolve();
}
- onStateChange(progress, request, flag, status) {
+ onStateChange(progress, request, flag) {
const isStart = flag & Ci.nsIWebProgressListener.STATE_START;
const isDocument = flag & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;
if (isDocument && isStart) {
diff --git a/devtools/server/actors/resources/server-sent-events.js b/devtools/server/actors/resources/server-sent-events.js
index 5b16f8bb9f..894bbc76bc 100644
--- a/devtools/server/actors/resources/server-sent-events.js
+++ b/devtools/server/actors/resources/server-sent-events.js
@@ -102,7 +102,7 @@ class ServerSentEventWatcher {
}
// nsIEventSourceEventService specific functions
- eventSourceConnectionOpened(httpChannelId) {}
+ eventSourceConnectionOpened() {}
eventSourceConnectionClosed(httpChannelId) {
const resource = ServerSentEventWatcher.createResource(
diff --git a/devtools/server/actors/resources/sources.js b/devtools/server/actors/resources/sources.js
index 6b3ab1d5e1..c4f0601106 100644
--- a/devtools/server/actors/resources/sources.js
+++ b/devtools/server/actors/resources/sources.js
@@ -69,13 +69,13 @@ class SourceWatcher {
// Before fetching all sources, process existing ones.
// The ThreadActor is already up and running before this code runs
// and have sources already registered and for which newSource event already fired.
- onAvailable(
- threadActor.sourcesManager.iter().map(s => {
- const resource = s.form();
- resource.resourceType = SOURCE;
- return resource;
- })
- );
+ const sources = [];
+ for (const sourceActor of threadActor.sourcesManager.iter()) {
+ const resource = sourceActor.form();
+ resource.resourceType = SOURCE;
+ sources.push(resource);
+ }
+ onAvailable(sources);
// Requesting all sources should end up emitting newSource on threadActor.sourcesManager
threadActor.addAllSources();
diff --git a/devtools/server/actors/resources/storage/extension-storage.js b/devtools/server/actors/resources/storage/extension-storage.js
index d14d3320c7..be98c917b3 100644
--- a/devtools/server/actors/resources/storage/extension-storage.js
+++ b/devtools/server/actors/resources/storage/extension-storage.js
@@ -13,25 +13,25 @@ const {
const {
LongStringActor,
} = require("resource://devtools/server/actors/string.js");
-// Use loadInDevToolsLoader: false for these extension modules, because these
+// Use global: "shared" for these extension modules, because these
// are singletons with shared state, and we must not create a new instance if a
// dedicated loader was used to load this module.
loader.lazyGetter(this, "ExtensionParent", () => {
return ChromeUtils.importESModule(
"resource://gre/modules/ExtensionParent.sys.mjs",
- { loadInDevToolsLoader: false }
+ { global: "shared" }
).ExtensionParent;
});
loader.lazyGetter(this, "ExtensionProcessScript", () => {
return ChromeUtils.importESModule(
"resource://gre/modules/ExtensionProcessScript.sys.mjs",
- { loadInDevToolsLoader: false }
+ { global: "shared" }
).ExtensionProcessScript;
});
loader.lazyGetter(this, "ExtensionStorageIDB", () => {
return ChromeUtils.importESModule(
"resource://gre/modules/ExtensionStorageIDB.sys.mjs",
- { loadInDevToolsLoader: false }
+ { global: "shared" }
).ExtensionStorageIDB;
});
@@ -310,7 +310,7 @@ class ExtensionStorageActor extends BaseStorageActor {
});
}
- async editItem({ host, field, items, oldValue }) {
+ async editItem({ host, items }) {
const db = this.dbConnectionForHost.get(host);
if (!db) {
return;
diff --git a/devtools/server/actors/resources/storage/indexed-db.js b/devtools/server/actors/resources/storage/indexed-db.js
index 8ded705c4f..05c523ac57 100644
--- a/devtools/server/actors/resources/storage/indexed-db.js
+++ b/devtools/server/actors/resources/storage/indexed-db.js
@@ -30,9 +30,13 @@ loader.lazyGetter(this, "indexedDBForStorage", () => {
}
});
const lazy = {};
-ChromeUtils.defineESModuleGetters(lazy, {
- Sqlite: "resource://gre/modules/Sqlite.sys.mjs",
-});
+ChromeUtils.defineESModuleGetters(
+ lazy,
+ {
+ Sqlite: "resource://gre/modules/Sqlite.sys.mjs",
+ },
+ { global: "contextual" }
+);
/**
* An async method equivalent to setTimeout but using Promises
@@ -882,7 +886,7 @@ class IndexedDBStorageActor extends BaseStorageActor {
const { name } = this.splitNameAndStorage(dbName);
const request = this.openWithPrincipal(principal, name, storage);
- return new Promise((resolve, reject) => {
+ return new Promise(resolve => {
let { objectStore, id, index, offset, size } = requestOptions;
const data = [];
let db;
diff --git a/devtools/server/actors/resources/thread-states.js b/devtools/server/actors/resources/thread-states.js
index 9ac79088d2..11aef81734 100644
--- a/devtools/server/actors/resources/thread-states.js
+++ b/devtools/server/actors/resources/thread-states.js
@@ -7,6 +7,7 @@
const {
TYPES: { THREAD_STATE },
} = require("resource://devtools/server/actors/resources/index.js");
+const Targets = require("resource://devtools/server/actors/targets/index.js");
const {
PAUSE_REASONS,
@@ -48,6 +49,18 @@ class BreakpointWatcher {
* This will be called for each resource.
*/
async watch(targetActor, { onAvailable }) {
+ // When debugging the whole browser (via the Browser Toolbox), we instantiate both content process and window global (FRAME) targets.
+ // But the debugger will only use the content process target's thread actor.
+ // Thread actor, Sources and Breakpoints have to be only managed for the content process target,
+ // and we should explicitly ignore the window global target.
+ if (
+ targetActor.sessionContext.type == "all" &&
+ targetActor.targetType === Targets.TYPES.FRAME &&
+ targetActor.typeName != "parentProcessTarget"
+ ) {
+ return;
+ }
+
const { threadActor } = targetActor;
this.threadActor = threadActor;
this.onAvailable = onAvailable;
@@ -87,6 +100,9 @@ class BreakpointWatcher {
* Stop watching for breakpoints
*/
destroy() {
+ if (!this.threadActor) {
+ return;
+ }
this.threadActor.off("paused", this.onPaused);
this.threadActor.off("resumed", this.onResumed);
}
@@ -116,7 +132,7 @@ class BreakpointWatcher {
]);
}
- onResumed(packet) {
+ onResumed() {
// NOTE: resumed events are suppressed while interrupted
// to prevent unintentional behavior.
if (this.isInterrupted) {
diff --git a/devtools/server/actors/resources/utils/content-process-storage.js b/devtools/server/actors/resources/utils/content-process-storage.js
index 7e126ce3f7..80d7e319a8 100644
--- a/devtools/server/actors/resources/utils/content-process-storage.js
+++ b/devtools/server/actors/resources/utils/content-process-storage.js
@@ -7,10 +7,14 @@
const EventEmitter = require("resource://devtools/shared/event-emitter.js");
const lazy = {};
-ChromeUtils.defineESModuleGetters(lazy, {
- getAddonIdForWindowGlobal:
- "resource://devtools/server/actors/watcher/browsing-context-helpers.sys.mjs",
-});
+ChromeUtils.defineESModuleGetters(
+ lazy,
+ {
+ getAddonIdForWindowGlobal:
+ "resource://devtools/server/actors/watcher/browsing-context-helpers.sys.mjs",
+ },
+ { global: "contextual" }
+);
// ms of delay to throttle updates
const BATCH_DELAY = 200;
diff --git a/devtools/server/actors/resources/utils/nsi-console-listener-watcher.js b/devtools/server/actors/resources/utils/nsi-console-listener-watcher.js
index 8d1ed43612..d4bde43818 100644
--- a/devtools/server/actors/resources/utils/nsi-console-listener-watcher.js
+++ b/devtools/server/actors/resources/utils/nsi-console-listener-watcher.js
@@ -79,7 +79,7 @@ class nsIConsoleListenerWatcher {
* @param {TargetActor} targetActor
* @return {Boolean}
*/
- shouldHandleTarget(targetActor) {
+ shouldHandleTarget() {
return true;
}
@@ -91,7 +91,7 @@ class nsIConsoleListenerWatcher {
* @param {nsIScriptError|nsIConsoleMessage} message
* @return {Boolean}
*/
- shouldHandleMessage(targetActor, message) {
+ shouldHandleMessage() {
throw new Error(
"'shouldHandleMessage' should be implemented in the class that extends nsIConsoleListenerWatcher"
);
@@ -101,12 +101,12 @@ class nsIConsoleListenerWatcher {
* Prepare the resource to be sent to the client. This should be implemented on the
* child class.
*
- * @param targetActor
- * @param nsIScriptError|nsIConsoleMessage message
+ * @param _targetActor
+ * @param nsIScriptError|nsIConsoleMessage _message
* @return object
* The object you can send to the remote client.
*/
- buildResource(targetActor, message) {
+ buildResource(_targetActor, _message) {
throw new Error(
"'buildResource' should be implemented in the class that extends nsIConsoleListenerWatcher"
);
diff --git a/devtools/server/actors/resources/utils/parent-process-storage.js b/devtools/server/actors/resources/utils/parent-process-storage.js
index 423d13b6b5..760e6e4d38 100644
--- a/devtools/server/actors/resources/utils/parent-process-storage.js
+++ b/devtools/server/actors/resources/utils/parent-process-storage.js
@@ -6,7 +6,8 @@
const EventEmitter = require("resource://devtools/shared/event-emitter.js");
const { isWindowGlobalPartOfContext } = ChromeUtils.importESModule(
- "resource://devtools/server/actors/watcher/browsing-context-helpers.sys.mjs"
+ "resource://devtools/server/actors/watcher/browsing-context-helpers.sys.mjs",
+ { global: "contextual" }
);
// ms of delay to throttle updates
diff --git a/devtools/server/actors/resources/websockets.js b/devtools/server/actors/resources/websockets.js
index 5845357a9c..58507f9fbb 100644
--- a/devtools/server/actors/resources/websockets.js
+++ b/devtools/server/actors/resources/websockets.js
@@ -97,7 +97,7 @@ class WebSocketWatcher {
}
// methods for the nsIWebSocketEventService
- webSocketCreated(webSocketSerialID, uri, protocols) {}
+ webSocketCreated() {}
webSocketOpened(
webSocketSerialID,
@@ -117,7 +117,7 @@ class WebSocketWatcher {
this.onAvailable([resource]);
}
- webSocketMessageAvailable(webSocketSerialID, data, messageType) {}
+ webSocketMessageAvailable() {}
webSocketClosed(webSocketSerialID, wasClean, code, reason) {
const httpChannelId = this.connections.get(webSocketSerialID);
diff --git a/devtools/server/actors/root.js b/devtools/server/actors/root.js
index df16c70b2f..90836d524e 100644
--- a/devtools/server/actors/root.js
+++ b/devtools/server/actors/root.js
@@ -135,8 +135,6 @@ class RootActor extends Actor {
"dom.worker.console.dispatch_events_to_main_thread"
)
: true,
- // @backward-compat { version 123 } A new Objects Manager front has a new "releaseActors" method.
- supportsReleaseActors: true,
};
}
diff --git a/devtools/server/actors/source.js b/devtools/server/actors/source.js
index ff08bcb4c2..e246e11b8c 100644
--- a/devtools/server/actors/source.js
+++ b/devtools/server/actors/source.js
@@ -128,7 +128,7 @@ class SourceActor extends Actor {
// because we can't easily fetch the full html content of the srcdoc attribute.
this.__isInlineSource =
source.introductionType === "inlineScript" &&
- !resolveSourceURL(source.displayURL, this.threadActor._parent) &&
+ !resolveSourceURL(source.displayURL, this.threadActor.targetActor) &&
!this.url.startsWith("about:srcdoc");
}
return this.__isInlineSource;
@@ -148,7 +148,7 @@ class SourceActor extends Actor {
}
get url() {
if (this._url === undefined) {
- this._url = getSourceURL(this._source, this.threadActor._parent);
+ this._url = getSourceURL(this._source, this.threadActor.targetActor);
}
return this._url;
}
@@ -205,7 +205,7 @@ class SourceActor extends Actor {
isBlackBoxed: this.sourcesManager.isBlackBoxed(this.url),
sourceMapBaseURL: getSourcemapBaseURL(
this.url,
- this.threadActor._parent.window
+ this.threadActor.targetActor.window
),
sourceMapURL: source.sourceMapURL,
introductionType,
diff --git a/devtools/server/actors/target-configuration.js b/devtools/server/actors/target-configuration.js
index 35340ee668..b6db235143 100644
--- a/devtools/server/actors/target-configuration.js
+++ b/devtools/server/actors/target-configuration.js
@@ -13,7 +13,8 @@ const {
SessionDataHelpers,
} = require("resource://devtools/server/actors/watcher/SessionDataHelpers.jsm");
const { isBrowsingContextPartOfContext } = ChromeUtils.importESModule(
- "resource://devtools/server/actors/watcher/browsing-context-helpers.sys.mjs"
+ "resource://devtools/server/actors/watcher/browsing-context-helpers.sys.mjs",
+ { global: "contextual" }
);
const { SUPPORTED_DATA } = SessionDataHelpers;
const { TARGET_CONFIGURATION } = SUPPORTED_DATA;
diff --git a/devtools/server/actors/targets/content-process.js b/devtools/server/actors/targets/content-process.js
index 56b1934ef1..cb6c34cea6 100644
--- a/devtools/server/actors/targets/content-process.js
+++ b/devtools/server/actors/targets/content-process.js
@@ -31,9 +31,7 @@ const {
} = require("resource://devtools/server/actors/targets/base-target-actor.js");
const { TargetActorRegistry } = ChromeUtils.importESModule(
"resource://devtools/server/actors/targets/target-actor-registry.sys.mjs",
- {
- loadInDevToolsLoader: false,
- }
+ { global: "shared" }
);
loader.lazyRequireGetter(
@@ -67,7 +65,7 @@ class ContentProcessTargetActor extends BaseTargetActor {
this.makeDebugger = makeDebugger.bind(null, {
findDebuggees: dbg =>
dbg.findAllGlobals().map(g => g.unsafeDereference()),
- shouldAddNewGlobalAsDebuggee: global => true,
+ shouldAddNewGlobalAsDebuggee: () => true,
});
const sandboxPrototype = {
@@ -149,7 +147,7 @@ class ContentProcessTargetActor extends BaseTargetActor {
}
if (!this.threadActor) {
- this.threadActor = new ThreadActor(this, null);
+ this.threadActor = new ThreadActor(this);
this.manage(this.threadActor);
}
if (!this.memoryActor) {
@@ -218,7 +216,7 @@ class ContentProcessTargetActor extends BaseTargetActor {
this.ensureWorkerList().workerPauser.setPauseServiceWorkers(request.origin);
}
- destroy() {
+ destroy({ isModeSwitching } = {}) {
// Avoid reentrancy. We will destroy the Transport when emitting "destroyed",
// which will force destroying all actors.
if (this.destroying) {
@@ -230,7 +228,7 @@ class ContentProcessTargetActor extends BaseTargetActor {
// otherwise you might have leaks reported when running browser_browser_toolbox_netmonitor.js in debug builds
Resources.unwatchAllResources(this);
- this.emit("destroyed");
+ this.emit("destroyed", { isModeSwitching });
super.destroy();
diff --git a/devtools/server/actors/targets/session-data-processors/blackboxing.js b/devtools/server/actors/targets/session-data-processors/blackboxing.js
index 70f4397a72..92bfa74569 100644
--- a/devtools/server/actors/targets/session-data-processors/blackboxing.js
+++ b/devtools/server/actors/targets/session-data-processors/blackboxing.js
@@ -20,7 +20,7 @@ module.exports = {
}
},
- removeSessionDataEntry(targetActor, entries, isDocumentCreation) {
+ removeSessionDataEntry(targetActor, entries) {
for (const { url, range } of entries) {
targetActor.sourcesManager.unblackBox(url, range);
}
diff --git a/devtools/server/actors/targets/session-data-processors/breakpoints.js b/devtools/server/actors/targets/session-data-processors/breakpoints.js
index ff7cb7ec0a..67c270654d 100644
--- a/devtools/server/actors/targets/session-data-processors/breakpoints.js
+++ b/devtools/server/actors/targets/session-data-processors/breakpoints.js
@@ -7,6 +7,7 @@
const {
STATES: THREAD_STATES,
} = require("resource://devtools/server/actors/thread.js");
+const Targets = require("resource://devtools/server/actors/targets/index.js");
module.exports = {
async addOrSetSessionDataEntry(
@@ -15,6 +16,17 @@ module.exports = {
isDocumentCreation,
updateType
) {
+ // When debugging the whole browser (via the Browser Toolbox), we instantiate both content process and window global (FRAME) targets.
+ // But the debugger will only use the content process target's thread actor.
+ // Thread actor, Sources and Breakpoints have to be only managed for the content process target,
+ // and we should explicitly ignore the window global target.
+ if (
+ targetActor.sessionContext.type == "all" &&
+ targetActor.targetType === Targets.TYPES.FRAME &&
+ targetActor.typeName != "parentProcessTarget"
+ ) {
+ return;
+ }
const { threadActor } = targetActor;
if (updateType == "set") {
threadActor.removeAllBreakpoints();
@@ -37,7 +49,7 @@ module.exports = {
}
},
- removeSessionDataEntry(targetActor, entries, isDocumentCreation) {
+ removeSessionDataEntry(targetActor, entries) {
for (const { location } of entries) {
targetActor.threadActor.removeBreakpoint(location);
}
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 c0a2fb7ffe..4eb9e4f3a8 100644
--- a/devtools/server/actors/targets/session-data-processors/event-breakpoints.js
+++ b/devtools/server/actors/targets/session-data-processors/event-breakpoints.js
@@ -30,7 +30,7 @@ module.exports = {
}
},
- removeSessionDataEntry(targetActor, entries, isDocumentCreation) {
+ removeSessionDataEntry(targetActor, entries) {
targetActor.threadActor.removeEventBreakpoints(entries);
},
};
diff --git a/devtools/server/actors/targets/session-data-processors/resources.js b/devtools/server/actors/targets/session-data-processors/resources.js
index 8f33ba8e0f..1e08397256 100644
--- a/devtools/server/actors/targets/session-data-processors/resources.js
+++ b/devtools/server/actors/targets/session-data-processors/resources.js
@@ -19,7 +19,7 @@ module.exports = {
await Resources.watchResources(targetActor, entries);
},
- removeSessionDataEntry(targetActor, entries, isDocumentCreation) {
+ removeSessionDataEntry(targetActor, entries) {
Resources.unwatchResources(targetActor, entries);
},
};
diff --git a/devtools/server/actors/targets/session-data-processors/target-configuration.js b/devtools/server/actors/targets/session-data-processors/target-configuration.js
index f68e82d69f..8f10692178 100644
--- a/devtools/server/actors/targets/session-data-processors/target-configuration.js
+++ b/devtools/server/actors/targets/session-data-processors/target-configuration.js
@@ -5,12 +5,7 @@
"use strict";
module.exports = {
- async addOrSetSessionDataEntry(
- targetActor,
- entries,
- isDocumentCreation,
- updateType
- ) {
+ async addOrSetSessionDataEntry(targetActor, entries, isDocumentCreation) {
// Only WindowGlobalTargetActor implements updateTargetConfiguration,
// skip targetActor data entry update for other targets.
if (typeof targetActor.updateTargetConfiguration == "function") {
@@ -26,7 +21,7 @@ module.exports = {
}
},
- removeSessionDataEntry(targetActor, entries, isDocumentCreation) {
+ removeSessionDataEntry() {
// configuration data entries are always added/updated, never removed.
},
};
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 716d2a9b21..ad5c0fe024 100644
--- a/devtools/server/actors/targets/session-data-processors/thread-configuration.js
+++ b/devtools/server/actors/targets/session-data-processors/thread-configuration.js
@@ -7,14 +7,21 @@
const {
STATES: THREAD_STATES,
} = require("resource://devtools/server/actors/thread.js");
+const Targets = require("resource://devtools/server/actors/targets/index.js");
module.exports = {
- async addOrSetSessionDataEntry(
- targetActor,
- entries,
- isDocumentCreation,
- updateType
- ) {
+ async addOrSetSessionDataEntry(targetActor, entries) {
+ // When debugging the whole browser (via the Browser Toolbox), we instantiate both content process and window global (FRAME) targets.
+ // But the debugger will only use the content process target's thread actor.
+ // Thread actor, Sources and Breakpoints have to be only managed for the content process target,
+ // and we should explicitly ignore the window global target.
+ if (
+ targetActor.sessionContext.type == "all" &&
+ targetActor.targetType === Targets.TYPES.FRAME &&
+ targetActor.typeName != "parentProcessTarget"
+ ) {
+ return;
+ }
const threadOptions = {};
for (const { key, value } of entries) {
@@ -35,7 +42,7 @@ module.exports = {
}
},
- removeSessionDataEntry(targetActor, entries, isDocumentCreation) {
+ removeSessionDataEntry() {
// configuration data entries are always added/updated, never removed.
},
};
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 7a0fd815aa..3bbcf54aaf 100644
--- a/devtools/server/actors/targets/session-data-processors/xhr-breakpoints.js
+++ b/devtools/server/actors/targets/session-data-processors/xhr-breakpoints.js
@@ -36,7 +36,7 @@ module.exports = {
);
},
- removeSessionDataEntry(targetActor, entries, isDocumentCreation) {
+ removeSessionDataEntry(targetActor, entries) {
for (const { path, method } of entries) {
targetActor.threadActor.removeXHRBreakpoint(path, method);
}
diff --git a/devtools/server/actors/targets/target-actor-registry.sys.mjs b/devtools/server/actors/targets/target-actor-registry.sys.mjs
index 4cb6d13868..25c1ac1234 100644
--- a/devtools/server/actors/targets/target-actor-registry.sys.mjs
+++ b/devtools/server/actors/targets/target-actor-registry.sys.mjs
@@ -24,7 +24,7 @@ export var TargetActorRegistry = {
xpcShellTargetActor = targetActor;
},
- unregisterXpcShellTargetActor(targetActor) {
+ unregisterXpcShellTargetActor() {
xpcShellTargetActor = null;
},
diff --git a/devtools/server/actors/targets/window-global.js b/devtools/server/actors/targets/window-global.js
index 5d2bb10164..6719f0518d 100644
--- a/devtools/server/actors/targets/window-global.js
+++ b/devtools/server/actors/targets/window-global.js
@@ -33,12 +33,11 @@ var makeDebugger = require("resource://devtools/server/actors/utils/make-debugge
const Targets = require("resource://devtools/server/actors/targets/index.js");
const { TargetActorRegistry } = ChromeUtils.importESModule(
"resource://devtools/server/actors/targets/target-actor-registry.sys.mjs",
- {
- loadInDevToolsLoader: false,
- }
+ { global: "shared" }
);
const { PrivateBrowsingUtils } = ChromeUtils.importESModule(
- "resource://gre/modules/PrivateBrowsingUtils.sys.mjs"
+ "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
+ { global: "contextual" }
);
const EXTENSION_CONTENT_SYS_MJS =
@@ -82,7 +81,7 @@ loader.lazyGetter(lazy, "ExtensionContent", () => {
// main loader. Note that the user of lazy.ExtensionContent elsewhere in
// this file (at webextensionsContentScriptGlobals) looks up the module
// via Cu.isESModuleLoaded, which also uses the main loader as desired.
- loadInDevToolsLoader: false,
+ global: "shared",
}).ExtensionContent;
});
@@ -888,7 +887,7 @@ class WindowGlobalTargetActor extends BaseTargetActor {
return {};
}
- listFrames(request) {
+ listFrames() {
const windows = this._docShellsToWindows(this.docShells);
return { frames: windows };
}
@@ -912,7 +911,7 @@ class WindowGlobalTargetActor extends BaseTargetActor {
);
}
- listWorkers(request) {
+ listWorkers() {
return this.ensureWorkerDescriptorActorList()
.getList()
.then(actors => {
@@ -960,7 +959,7 @@ class WindowGlobalTargetActor extends BaseTargetActor {
this.emit("workerListChanged");
}
- _onConsoleApiProfilerEvent(subject, topic, data) {
+ _onConsoleApiProfilerEvent() {
// TODO: We will receive console-api-profiler events for any browser running
// in the same process as this target. We should filter irrelevant events,
// but console-api-profiler currently doesn't emit any information to identify
@@ -977,7 +976,7 @@ class WindowGlobalTargetActor extends BaseTargetActor {
});
}
- observe(subject, topic, data) {
+ observe(subject, topic) {
// Ignore any event that comes before/after the actor is attached.
// That typically happens during Firefox shutdown.
if (this.isDestroyed()) {
@@ -1165,7 +1164,7 @@ class WindowGlobalTargetActor extends BaseTargetActor {
* This sets up the content window for being debugged
*/
_createThreadActor() {
- this.threadActor = new ThreadActor(this, this.window);
+ this.threadActor = new ThreadActor(this);
this.manage(this.threadActor);
}
@@ -1187,7 +1186,7 @@ class WindowGlobalTargetActor extends BaseTargetActor {
// Protocol Request Handlers
- detach(request) {
+ detach() {
// Destroy the actor in the next event loop in order
// to ensure responding to the `detach` request.
DevToolsUtils.executeSoon(() => {
@@ -1824,7 +1823,7 @@ class DebuggerProgressListener {
this._knownWindowIDs.delete(getWindowID(window));
}, "DebuggerProgressListener.prototype.onWindowHidden");
- observe = DevToolsUtils.makeInfallible(function (subject, topic) {
+ observe = DevToolsUtils.makeInfallible(function (subject) {
if (this._targetActor.isDestroyed()) {
return;
}
@@ -1859,8 +1858,7 @@ class DebuggerProgressListener {
onStateChange = DevToolsUtils.makeInfallible(function (
progress,
request,
- flag,
- status
+ flag
) {
if (this._targetActor.isDestroyed()) {
return;
diff --git a/devtools/server/actors/targets/worker.js b/devtools/server/actors/targets/worker.js
index cf5f7b83c9..20b60cfa24 100644
--- a/devtools/server/actors/targets/worker.js
+++ b/devtools/server/actors/targets/worker.js
@@ -72,7 +72,7 @@ class WorkerTargetActor extends BaseTargetActor {
});
// needed by the console actor
- this.threadActor = new ThreadActor(this, this.workerGlobal);
+ this.threadActor = new ThreadActor(this);
// needed by the thread actor to communicate with the console when evaluating logpoints.
this._consoleActor = new WebConsoleActor(this.conn, this);
diff --git a/devtools/server/actors/thread-configuration.js b/devtools/server/actors/thread-configuration.js
index 8cd28f5887..f0c697bb51 100644
--- a/devtools/server/actors/thread-configuration.js
+++ b/devtools/server/actors/thread-configuration.js
@@ -17,29 +17,33 @@ const {
} = SessionDataHelpers;
// List of options supported by this thread configuration actor.
+/* eslint sort-keys: "error" */
const SUPPORTED_OPTIONS = {
- // Controls pausing on debugger statement.
- // (This is enabled by default if omitted)
- shouldPauseOnDebuggerStatement: true,
- // Enable pausing on exceptions.
- pauseOnExceptions: true,
// Disable pausing on caught exceptions.
ignoreCaughtExceptions: true,
- // Include previously saved stack frames when paused.
- shouldIncludeSavedFrames: true,
- // Include async stack frames when paused.
- shouldIncludeAsyncLiveFrames: true,
- // Stop pausing on breakpoints.
- skipBreakpoints: true,
// Log the event break points.
logEventBreakpoints: true,
// Enable debugging asm & wasm.
// See https://searchfox.org/mozilla-central/source/js/src/doc/Debugger/Debugger.md#16-26
observeAsmJS: true,
observeWasm: true,
+ // Enable pausing on exceptions.
+ pauseOnExceptions: true,
+ // Boolean to know if we should display the overlay when pausing
+ pauseOverlay: true,
// Should pause all the workers untill thread has attached.
pauseWorkersUntilAttach: true,
+ // Include async stack frames when paused.
+ shouldIncludeAsyncLiveFrames: true,
+ // Include previously saved stack frames when paused.
+ shouldIncludeSavedFrames: true,
+ // Controls pausing on debugger statement.
+ // (This is enabled by default if omitted)
+ shouldPauseOnDebuggerStatement: true,
+ // Stop pausing on breakpoints.
+ skipBreakpoints: true,
};
+/* eslint-disable sort-keys */
/**
* This actor manages the configuration options which apply to thread actor for all the targets.
diff --git a/devtools/server/actors/thread.js b/devtools/server/actors/thread.js
index d33b3e5eb2..07dcc27a6a 100644
--- a/devtools/server/actors/thread.js
+++ b/devtools/server/actors/thread.js
@@ -32,6 +32,7 @@ const {
const {
logEvent,
} = require("resource://devtools/server/actors/utils/logEvent.js");
+const Targets = require("devtools/server/actors/targets/index");
loader.lazyRequireGetter(
this,
@@ -169,24 +170,23 @@ class ThreadActor extends Actor {
*
* ThreadActors manage execution/inspection of debuggees.
*
- * @param parent TargetActor
- * This |ThreadActor|'s parent actor. i.e. one of the many Target actors.
- * @param aGlobal object [optional]
- * An optional (for content debugging only) reference to the content
- * window.
+ * @param {TargetActor} targetActor
+ * This `ThreadActor`'s parent actor. i.e. one of the many Target actors.
*/
- constructor(parent, global) {
- super(parent.conn, threadSpec);
+ constructor(targetActor) {
+ super(targetActor.conn, threadSpec);
+
+ // This attribute is used by various other actors to find the target actor
+ this.targetActor = targetActor;
this._state = STATES.DETACHED;
- this._parent = parent;
- this.global = global;
this._options = {
skipBreakpoints: false,
};
this._gripDepth = 0;
- this._parentClosed = false;
+ this._targetActorClosed = false;
this._observingNetwork = false;
+ this._shouldShowPauseOverlay = true;
this._frameActors = [];
this._xhrBreakpoints = [];
@@ -233,9 +233,9 @@ class ThreadActor extends Actor {
this._onWillNavigate = this._onWillNavigate.bind(this);
this._onNavigate = this._onNavigate.bind(this);
- this._parent.on("window-ready", this._onWindowReady);
- this._parent.on("will-navigate", this._onWillNavigate);
- this._parent.on("navigate", this._onNavigate);
+ this.targetActor.on("window-ready", this._onWindowReady);
+ this.targetActor.on("will-navigate", this._onWillNavigate);
+ this.targetActor.on("navigate", this._onNavigate);
this._firstStatementBreakpoint = null;
this._debuggerNotificationObserver = new DebuggerNotificationObserver();
@@ -246,7 +246,7 @@ class ThreadActor extends Actor {
get dbg() {
if (!this._dbg) {
- this._dbg = this._parent.dbg;
+ this._dbg = this.targetActor.dbg;
// Keep the debugger disabled until a client attaches.
if (this._state === STATES.DETACHED) {
this._dbg.disable();
@@ -289,11 +289,11 @@ class ThreadActor extends Actor {
}
get sourcesManager() {
- return this._parent.sourcesManager;
+ return this.targetActor.sourcesManager;
}
get breakpoints() {
- return this._parent.breakpoints;
+ return this.targetActor.breakpoints;
}
get youngestFrame() {
@@ -360,9 +360,9 @@ class ThreadActor extends Actor {
} catch (e) {}
}
- this._parent.off("window-ready", this._onWindowReady);
- this._parent.off("will-navigate", this._onWillNavigate);
- this._parent.off("navigate", this._onNavigate);
+ this.targetActor.off("window-ready", this._onWindowReady);
+ this.targetActor.off("will-navigate", this._onWillNavigate);
+ this.targetActor.off("navigate", this._onNavigate);
this.sourcesManager.off("newSource", this.onNewSourceEvent);
this.clearDebuggees();
@@ -418,11 +418,11 @@ class ThreadActor extends Actor {
this.alreadyAttached = true;
this.dbg.enable();
- // Notify the parent that we've finished attaching. If this is a worker
+ // 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._parent.onThreadAttached) {
- this._parent.onThreadAttached();
+ if (this.targetActor.onThreadAttached) {
+ this.targetActor.onThreadAttached();
}
if (Services.obs) {
// Set a wrappedJSObject property so |this| can be sent via the observer service
@@ -443,7 +443,7 @@ class ThreadActor extends Actor {
}
const env = new HighlighterEnvironment();
- env.initFromTargetActor(this._parent);
+ env.initFromTargetActor(this.targetActor);
const highlighter = new PausedDebuggerOverlay(env, {
resume: () => this.resume(null),
stepOver: () => this.resume({ type: "next" }),
@@ -453,7 +453,13 @@ class ThreadActor extends Actor {
}
_canShowOverlay() {
- const { window } = this._parent;
+ // Only attempt to show on overlay on WindowGlobal targets, which displays a document.
+ // Workers and content processes can't display any overlay.
+ if (this.targetActor.targetType != Targets.TYPES.FRAME) {
+ return false;
+ }
+
+ const { window } = this.targetActor;
// The CanvasFrameAnonymousContentHelper class we're using to create the paused overlay
// need to have access to a documentElement.
@@ -473,21 +479,22 @@ class ThreadActor extends Actor {
async showOverlay() {
if (
- this.isPaused() &&
- this._canShowOverlay() &&
- this._parent.on &&
- this.pauseOverlay
+ !this._shouldShowPauseOverlay ||
+ !this.isPaused() ||
+ !this._canShowOverlay()
) {
- const reason = this._priorPause.why.type;
- await this.pauseOverlay.isReady;
+ return;
+ }
- // we might not be paused anymore.
- if (!this.isPaused()) {
- return;
- }
+ const reason = this._priorPause.why.type;
+ await this.pauseOverlay.isReady;
- this.pauseOverlay.show(reason);
+ // we might not be paused anymore.
+ if (!this.isPaused()) {
+ return;
}
+
+ this.pauseOverlay.show(reason);
}
hideOverlay() {
@@ -590,7 +597,7 @@ class ThreadActor extends Actor {
}
getAvailableEventBreakpoints() {
- return getAvailableEventBreakpoints(this._parent.window);
+ return getAvailableEventBreakpoints(this.targetActor.window);
}
getActiveEventBreakpoints() {
return Array.from(this._activeEventBreakpoints);
@@ -805,12 +812,15 @@ class ThreadActor extends Actor {
if ("observeWasm" in options) {
this.dbg.allowUnobservedWasm = !options.observeWasm;
}
+ if ("pauseOverlay" in options) {
+ this._shouldShowPauseOverlay = !!options.pauseOverlay;
+ }
if (
"pauseWorkersUntilAttach" in options &&
- this._parent.pauseWorkersUntilAttach
+ this.targetActor.pauseWorkersUntilAttach
) {
- this._parent.pauseWorkersUntilAttach(options.pauseWorkersUntilAttach);
+ this.targetActor.pauseWorkersUntilAttach(options.pauseWorkersUntilAttach);
}
if (options.breakpoints) {
@@ -977,10 +987,10 @@ class ThreadActor extends Actor {
// If the parent actor has been closed, terminate the debuggee script
// instead of continuing. Executing JS after the content window is gone is
// a bad idea.
- return this._parentClosed ? null : undefined;
+ return this._targetActorClosed ? null : undefined;
}
- _makeOnEnterFrame({ pauseAndRespond }) {
+ _makeOnEnterFrame() {
return frame => {
if (this._requestedFrameRestart) {
return null;
@@ -1089,7 +1099,7 @@ class ThreadActor extends Actor {
return line !== newLocation.line || column !== newLocation.column;
}
- _makeOnStep({ pauseAndRespond, startFrame, steppingType, completion }) {
+ _makeOnStep({ pauseAndRespond, startFrame, completion }) {
const thread = this;
return function () {
if (thread._validFrameStepOffset(this, startFrame, this.offset)) {
@@ -1336,7 +1346,7 @@ class ThreadActor extends Actor {
* when we do not want to notify the front end of a resume, for example when
* we are shutting down.
*/
- doResume({ resumeLimit } = {}) {
+ doResume() {
this._state = STATES.RUNNING;
// Drop the actors in the pause actor pool.
@@ -1520,7 +1530,7 @@ class ThreadActor extends Actor {
}
}
- sources(request) {
+ sources() {
this.addAllSources();
// No need to flush the new source packets here, as we are sending the
@@ -1528,7 +1538,11 @@ class ThreadActor extends Actor {
// overhead of an RDP packet for every source right now. Let the default
// timeout flush the buffered packets.
- return this.sourcesManager.iter().map(s => s.form());
+ const forms = [];
+ for (const source of this.sourcesManager.iter()) {
+ forms.push(source.form());
+ }
+ return forms;
}
/**
@@ -1809,7 +1823,7 @@ class ThreadActor extends Actor {
this.threadLifetimePool.objectActors.set(actor.obj, actor);
}
- _onWindowReady({ isTopLevel, isBFCache, window }) {
+ _onWindowReady({ isTopLevel, isBFCache }) {
// Note that this code relates to the disabling of Debugger API from will-navigate listener.
// And should only be triggered when the target actor doesn't follow WindowGlobal lifecycle.
// i.e. when the Thread Actor manages more than one top level WindowGlobal.
@@ -1817,9 +1831,6 @@ class ThreadActor extends Actor {
this.sourcesManager.reset();
this.clearDebuggees();
this.dbg.enable();
- // Update the global no matter if the debugger is on or off,
- // otherwise the global will be wrong when enabled later.
- this.global = window;
}
// Refresh the debuggee list when a new window object appears (top window or
@@ -2119,7 +2130,7 @@ class ThreadActor extends Actor {
// when debugging a tab (i.e. browser-element). As we still want to debug them
// from the browser toolbox.
if (
- this._parent.sessionContext.type == "browser-element" &&
+ this.targetActor.sessionContext.type == "browser-element" &&
source.url.endsWith("ExtensionContent.sys.mjs")
) {
return false;
@@ -2208,7 +2219,7 @@ class ThreadActor extends Actor {
// HTML files can contain any number of inline sources. We have to find
// all the inline sources and their start line without running any of the
// scripts on the page. The approach used here is approximate.
- if (!this._parent.window) {
+ if (!this.targetActor.window) {
return;
}
diff --git a/devtools/server/actors/tracer.js b/devtools/server/actors/tracer.js
index 028d084584..bf759cee5f 100644
--- a/devtools/server/actors/tracer.js
+++ b/devtools/server/actors/tracer.js
@@ -129,29 +129,40 @@ class TracerActor extends Actor {
this.tracingListener = {
onTracingFrame: this.onTracingFrame.bind(this),
+ onTracingFrameStep: this.onTracingFrameStep.bind(this),
onTracingFrameExit: this.onTracingFrameExit.bind(this),
onTracingInfiniteLoop: this.onTracingInfiniteLoop.bind(this),
onTracingToggled: this.onTracingToggled.bind(this),
onTracingPending: this.onTracingPending.bind(this),
+ onTracingDOMMutation: this.onTracingDOMMutation.bind(this),
};
addTracingListener(this.tracingListener);
this.traceValues = !!options.traceValues;
- startTracing({
- global: this.targetActor.window || this.targetActor.workerGlobal,
- prefix: options.prefix || "",
- // Enable receiving the `currentDOMEvent` being passed to `onTracingFrame`
- traceDOMEvents: true,
- // Enable tracing function arguments as well as returned values
- traceValues: !!options.traceValues,
- // Enable tracing only on next user interaction
- traceOnNextInteraction: !!options.traceOnNextInteraction,
- // Notify about frame exit / function call returning
- traceFunctionReturn: !!options.traceFunctionReturn,
- // Ignore frames beyond the given depth
- maxDepth: options.maxDepth,
- // Stop the tracing after a number of top level frames
- maxRecords: options.maxRecords,
- });
+ try {
+ startTracing({
+ global: this.targetActor.window || this.targetActor.workerGlobal,
+ prefix: options.prefix || "",
+ // Enable receiving the `currentDOMEvent` being passed to `onTracingFrame`
+ traceDOMEvents: true,
+ // Enable tracing DOM Mutations
+ traceDOMMutations: options.traceDOMMutations,
+ // Enable tracing function arguments as well as returned values
+ traceValues: !!options.traceValues,
+ // Enable tracing only on next user interaction
+ traceOnNextInteraction: !!options.traceOnNextInteraction,
+ // Notify about frame exit / function call returning
+ traceFunctionReturn: !!options.traceFunctionReturn,
+ // Ignore frames beyond the given depth
+ maxDepth: options.maxDepth,
+ // Stop the tracing after a number of top level frames
+ maxRecords: options.maxRecords,
+ });
+ } catch (e) {
+ // If startTracing throws, it probably rejected one of its options and we should
+ // unregister the tracing listener.
+ this.stopTracing();
+ throw e;
+ }
}
stopTracing() {
@@ -188,12 +199,10 @@ class TracerActor extends Actor {
*
* @param {Boolean} enabled
* True if the tracer starts tracing, false it it stops.
- * @param {String} reason
- * Optional string to justify why the tracer stopped.
* @return {Boolean}
* Return true, if the JavaScriptTracer should log a message to stdout.
*/
- onTracingToggled(enabled, reason) {
+ onTracingToggled(enabled) {
// stopTracing will clear `logMethod`, so compute this before calling it.
const shouldLogToStdout = this.logMethod == LOG_METHODS.STDOUT;
@@ -266,11 +275,111 @@ class TracerActor extends Actor {
}
/**
+ * Called by JavaScriptTracer class when a new mutation happened on any DOM Element.
+ *
+ * @param {Object} options
+ * @param {Number} options.depth
+ * Represents the depth of the frame in the call stack.
+ * @param {String} options.prefix
+ * A string to be displayed as a prefix of any logged frame.
+ * @param {nsIStackFrame} options.caller
+ * The JS Callsite which caused this mutation.
+ * @param {String} options.type
+ * Type of DOM Mutation:
+ * - "add": Node being added,
+ * - "attributes": Node whose attributes changed,
+ * - "remove": Node being removed,
+ * @param {DOMNode} options.element
+ * The DOM Node related to the current mutation.
+ */
+ onTracingDOMMutation({ depth, prefix, type, caller, element }) {
+ // Delegate to JavaScriptTracer to log to stdout
+ if (this.logMethod == LOG_METHODS.STDOUT) {
+ return true;
+ }
+
+ if (this.logMethod == LOG_METHODS.CONSOLE) {
+ const dbgObj = makeDebuggeeValue(this.targetActor, element);
+ this.throttledTraces.push({
+ resourceType: JSTRACER_TRACE,
+ prefix,
+ timeStamp: ChromeUtils.dateNow(),
+
+ filename: caller?.filename,
+ lineNumber: caller?.lineNumber,
+ columnNumber: caller?.columnNumber,
+ sourceId: caller.sourceId,
+
+ depth,
+ mutationType: type,
+ mutationElement: createValueGripForTarget(this.targetActor, dbgObj),
+ });
+ this.throttleEmitTraces();
+ return false;
+ }
+ return false;
+ }
+
+ /**
+ * Called by JavaScriptTracer class on each step of a function call.
+ *
+ * @param {Object} options
+ * @param {Debugger.Frame} options.frame
+ * A descriptor object for the JavaScript frame.
+ * @param {Number} options.depth
+ * Represents the depth of the frame in the call stack.
+ * @param {String} options.prefix
+ * A string to be displayed as a prefix of any logged frame.
+ * @return {Boolean}
+ * Return true, if the JavaScriptTracer should log the step to stdout.
+ */
+ onTracingFrameStep({ frame, depth, prefix }) {
+ const { script } = frame;
+ const { lineNumber, columnNumber } = script.getOffsetMetadata(frame.offset);
+ const url = script.source.url;
+
+ // NOTE: Debugger.Script.prototype.getOffsetMetadata returns
+ // columnNumber in 1-based.
+ // Convert to 0-based, while keeping the wasm's column (1) as is.
+ // (bug 1863878)
+ const columnBase = script.format === "wasm" ? 0 : 1;
+
+ // Ignore blackboxed sources
+ if (
+ this.sourcesManager.isBlackBoxed(
+ url,
+ lineNumber,
+ columnNumber - columnBase
+ )
+ ) {
+ return false;
+ }
+
+ if (this.logMethod == LOG_METHODS.STDOUT) {
+ // By returning true, we let JavaScriptTracer class log the message to stdout.
+ return true;
+ }
+
+ if (this.logMethod == LOG_METHODS.CONSOLE) {
+ this.throttledTraces.push({
+ resourceType: JSTRACER_TRACE,
+ prefix,
+ timeStamp: ChromeUtils.dateNow(),
+
+ depth,
+ filename: url,
+ lineNumber,
+ columnNumber: columnNumber - columnBase,
+ sourceId: script.source.id,
+ });
+ this.throttleEmitTraces();
+ }
+
+ return false;
+ }
+ /**
* Called by JavaScriptTracer class when a new JavaScript frame is executed.
*
- * @param {Number} frameId
- * Unique identifier for the current frame.
- * This should match a frame notified via onTracingFrameExit.
* @param {Debugger.Frame} frame
* A descriptor object for the JavaScript frame.
* @param {Number} depth
@@ -287,7 +396,6 @@ class TracerActor extends Actor {
* Return true, if the JavaScriptTracer should log the frame to stdout.
*/
onTracingFrame({
- frameId,
frame,
depth,
formatedDisplayName,
diff --git a/devtools/server/actors/utils/custom-formatters.js b/devtools/server/actors/utils/custom-formatters.js
index e4ae20dad7..f6d8cab797 100644
--- a/devtools/server/actors/utils/custom-formatters.js
+++ b/devtools/server/actors/utils/custom-formatters.js
@@ -71,7 +71,7 @@ function customFormatterHeader(objectActor) {
return null;
}
- const targetActor = objectActor.thread._parent;
+ const { targetActor } = objectActor.thread;
const {
customFormatterConfigDbgObj: configDbgObj,
@@ -253,7 +253,7 @@ async function customFormatterBody(objectActor, formatter) {
const customFormatterIndex = global.devtoolsFormatters.indexOf(formatter);
- const targetActor = objectActor.thread._parent;
+ const { targetActor } = objectActor.thread;
try {
const { customFormatterConfigDbgObj, customFormatterObjectTagDepth } =
objectActor.hooks;
diff --git a/devtools/server/actors/utils/inactive-property-helper.js b/devtools/server/actors/utils/inactive-property-helper.js
index 759c2e6215..3f6e748167 100644
--- a/devtools/server/actors/utils/inactive-property-helper.js
+++ b/devtools/server/actors/utils/inactive-property-helper.js
@@ -21,6 +21,11 @@ const TEXT_WRAP_BALANCE_LIMIT = Services.prefs.getIntPref(
10
);
+const ALIGN_CONTENT_BLOCKS = Services.prefs.getBoolPref(
+ "layout.css.align-content.blocks.enabled",
+ false
+);
+
const VISITED_MDN_LINK = "https://developer.mozilla.org/docs/Web/CSS/:visited";
const VISITED_INVALID_PROPERTIES = allCssPropertiesExcept([
"all",
@@ -227,12 +232,29 @@ class InactivePropertyHelper {
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1598730
{
invalidProperties: ["align-content"],
- when: () =>
- !this.style["align-content"].includes("baseline") &&
- !this.gridContainer &&
- !this.flexContainer,
- fixId: "inactive-css-not-grid-or-flex-container-fix",
- msgId: "inactive-css-not-grid-or-flex-container",
+ when: () => {
+ if (this.style["align-content"].includes("baseline")) {
+ return false;
+ }
+ const supportedDisplay = [
+ "flex",
+ "inline-flex",
+ "grid",
+ "inline-grid",
+ // Uncomment table-cell when Bug 1883357 is fixed.
+ // "table-cell"
+ ];
+ if (ALIGN_CONTENT_BLOCKS) {
+ supportedDisplay.push("block", "inline-block");
+ }
+ return !this.checkComputedStyle("display", supportedDisplay);
+ },
+ fixId: ALIGN_CONTENT_BLOCKS
+ ? "inactive-css-not-grid-or-flex-or-block-container-fix"
+ : "inactive-css-not-grid-or-flex-container-fix",
+ msgId: ALIGN_CONTENT_BLOCKS
+ ? "inactive-css-property-because-of-display"
+ : "inactive-css-not-grid-or-flex-container",
},
// column-gap and shorthands used on non-grid or non-flex or non-multi-col container.
{
@@ -1223,11 +1245,8 @@ class InactivePropertyHelper {
/**
* Check if a node is a grid item.
- *
- * @param {DOMNode} node
- * The node to check.
*/
- isGridItem(node) {
+ isGridItem() {
return !!this.getParentGridElement(this.node);
}
diff --git a/devtools/server/actors/utils/logEvent.js b/devtools/server/actors/utils/logEvent.js
index 88b166619e..5f40085fde 100644
--- a/devtools/server/actors/utils/logEvent.js
+++ b/devtools/server/actors/utils/logEvent.js
@@ -34,7 +34,7 @@ function logEvent({ threadActor, frame, level, expression, bindings }) {
// TODO remove this branch when (#1592584) lands (#1609540)
if (isWorker) {
- threadActor._parent._consoleActor.evaluateJS({
+ threadActor.targetActor._consoleActor.evaluateJS({
text: `console.log(...${expression})`,
bindings: { displayName, ...bindings },
url: sourceActor.url,
@@ -76,7 +76,7 @@ function logEvent({ threadActor, frame, level, expression, bindings }) {
value = value.unsafeDereference();
}
- const targetActor = threadActor._parent;
+ const targetActor = threadActor.targetActor;
const message = {
filename: sourceActor.url,
lineNumber: line,
diff --git a/devtools/server/actors/utils/sources-manager.js b/devtools/server/actors/utils/sources-manager.js
index b80da69bfa..fda37a3184 100644
--- a/devtools/server/actors/utils/sources-manager.js
+++ b/devtools/server/actors/utils/sources-manager.js
@@ -341,8 +341,13 @@ class SourcesManager extends EventEmitter {
return this.blackBoxedSources.set(url, ranges);
}
+ /**
+ * List all currently registered source actors.
+ *
+ * @return Iterator<SourceActor>
+ */
iter() {
- return [...this._sourceActors.values()];
+ return this._sourceActors.values();
}
/**
@@ -429,15 +434,15 @@ class SourcesManager extends EventEmitter {
// Without this check, the cache may return stale data that doesn't match
// the document shown in the browser.
let loadFromCache = canUseCache;
- if (canUseCache && this._thread._parent.browsingContext) {
+ if (canUseCache && this._thread.targetActor.browsingContext) {
loadFromCache = !(
- this._thread._parent.browsingContext.defaultLoadFlags ===
+ this._thread.targetActor.browsingContext.defaultLoadFlags ===
Ci.nsIRequest.LOAD_BYPASS_CACHE
);
}
// Fetch the sources with the same principal as the original document
- const win = this._thread._parent.window;
+ const win = this._thread.targetActor.window;
let principal, cacheKey;
// On xpcshell, we don't have a window but a Sandbox
if (!isWorker && win instanceof Ci.nsIDOMWindow) {
diff --git a/devtools/server/actors/utils/stylesheets-manager.js b/devtools/server/actors/utils/stylesheets-manager.js
index 838e5be602..a9c0705e8d 100644
--- a/devtools/server/actors/utils/stylesheets-manager.js
+++ b/devtools/server/actors/utils/stylesheets-manager.js
@@ -640,10 +640,14 @@ class StyleSheetsManager extends EventEmitter {
return win;
};
- const styleSheetRules =
- InspectorUtils.getAllStyleSheetCSSStyleRules(styleSheet);
- const ruleCount = styleSheetRules.length;
- // We need to go through nested rules to extract all the rules we're interested in
+ // This returns the following type of at-rules:
+ // - CSSMediaRule
+ // - CSSContainerRule
+ // - CSSSupportsRule
+ // - CSSLayerBlockRule
+ // New types can be added from InpsectorUtils.cpp `CollectAtRules`
+ const { atRules: styleSheetRules, ruleCount } =
+ InspectorUtils.getStyleSheetRuleCountAndAtRules(styleSheet);
const atRules = [];
for (const rule of styleSheetRules) {
const className = ChromeUtils.getClassName(rule);
@@ -703,7 +707,10 @@ class StyleSheetsManager extends EventEmitter {
});
}
}
- return { ruleCount, atRules };
+ return {
+ ruleCount,
+ atRules,
+ };
}
/**
diff --git a/devtools/server/actors/watcher.js b/devtools/server/actors/watcher.js
index 97d2be01e4..935d33faa8 100644
--- a/devtools/server/actors/watcher.js
+++ b/devtools/server/actors/watcher.js
@@ -9,21 +9,18 @@ const { watcherSpec } = require("resource://devtools/shared/specs/watcher.js");
const Resources = require("resource://devtools/server/actors/resources/index.js");
const { TargetActorRegistry } = ChromeUtils.importESModule(
"resource://devtools/server/actors/targets/target-actor-registry.sys.mjs",
- {
- loadInDevToolsLoader: false,
- }
+ { global: "shared" }
);
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.
- loadInDevToolsLoader: false,
- }
+ // WatcherRegistry 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"
+ "resource://devtools/server/actors/watcher/browsing-context-helpers.sys.mjs",
+ { global: "contextual" }
);
const {
SESSION_TYPES,
@@ -257,11 +254,11 @@ exports.WatcherActor = class WatcherActor extends Actor {
const targetHelperModule = TARGET_HELPERS[targetType];
targetHelperModule.destroyTargets(this, options);
- // Unregister the JS Window Actor if there is no more DevTools code observing any target/resource,
+ // 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.maybeUnregisteringJSWindowActor();
+ WatcherRegistry.maybeUnregisterJSActors();
}
}
@@ -637,7 +634,7 @@ exports.WatcherActor = class WatcherActor extends Actor {
}
// Unregister the JS Window Actor if there is no more DevTools code observing any target/resource
- WatcherRegistry.maybeUnregisteringJSWindowActor();
+ WatcherRegistry.maybeUnregisterJSActors();
}
clearResources(resourceTypes) {
diff --git a/devtools/server/actors/watcher/WatcherRegistry.sys.mjs b/devtools/server/actors/watcher/WatcherRegistry.sys.mjs
index 1068a253c9..ac8bc7f0c8 100644
--- a/devtools/server/actors/watcher/WatcherRegistry.sys.mjs
+++ b/devtools/server/actors/watcher/WatcherRegistry.sys.mjs
@@ -180,6 +180,9 @@ export const WatcherRegistry = {
// Register the JS Window Actor the first time we start watching for something (e.g. resource, target, …).
registerJSWindowActor();
+ if (sessionData?.targets?.includes("process")) {
+ registerJSProcessActor();
+ }
persistMapToSharedData();
},
@@ -245,7 +248,17 @@ export const WatcherRegistry = {
unregisterWatcher(watcher) {
sessionDataByWatcherActor.delete(watcher.actorID);
watcherActors.delete(watcher.actorID);
- this.maybeUnregisteringJSWindowActor();
+ this.maybeUnregisterJSActors();
+ },
+
+ /**
+ * Unregister the JS Actors if there is no more DevTools code observing any target/resource.
+ */
+ maybeUnregisterJSActors() {
+ if (sessionDataByWatcherActor.size == 0) {
+ unregisterJSWindowActor();
+ unregisterJSProcessActor();
+ }
},
/**
@@ -319,15 +332,6 @@ export const WatcherRegistry = {
resourceTypes
);
},
-
- /**
- * Unregister the JS Window Actor if there is no more DevTools code observing any target/resource.
- */
- maybeUnregisteringJSWindowActor() {
- if (sessionDataByWatcherActor.size == 0) {
- unregisterJSWindowActor();
- }
- },
};
// Boolean flag to know if the DevToolsFrame JS Window Actor is currently registered
@@ -395,3 +399,63 @@ function unregisterJSWindowActor() {
ChromeUtils.unregisterWindowActor(JSWindowActorName);
}
}
+
+// Boolean flag to know if the DevToolsProcess JS Process Actor is currently registered
+let isJSProcessActorRegistered = false;
+
+const JSProcessActorConfig = {
+ parent: {
+ esModuleURI:
+ "resource://devtools/server/connectors/js-process-actor/DevToolsProcessParent.sys.mjs",
+ },
+ child: {
+ esModuleURI:
+ "resource://devtools/server/connectors/js-process-actor/DevToolsProcessChild.sys.mjs",
+ // There is no good observer service notification we can listen to to instantiate the JSProcess Actor
+ // reliably as soon as the process start.
+ // So manually spawn our JSProcessActor from a process script emitting a custom observer service notification...
+ observers: ["init-devtools-content-process-actor"],
+ },
+ // 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,
+
+ // 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).
+ // DevTools modules should be loaded in a distinct global in order to be able to debug this privileged code.
+ // There is a strong requirement in spidermonkey for the debuggee and debugger to be using distinct compartments.
+ // This flag will force both parent and child modules to be loaded via a dedicated loader (See mozJSModuleLoader::GetOrCreateDevToolsLoader)
+ //
+ // Note that as a side effect, it makes these modules and all their dependencies to be invisible to the debugger.
+ loadInDevToolsLoader: true,
+};
+
+const PROCESS_SCRIPT_URL =
+ "resource://devtools/server/actors/watcher/target-helpers/content-process-jsprocessactor-startup.js";
+
+function registerJSProcessActor() {
+ if (isJSProcessActorRegistered) {
+ return;
+ }
+ isJSProcessActorRegistered = true;
+ ChromeUtils.registerProcessActor("DevToolsProcess", JSProcessActorConfig);
+
+ // 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;
+ }
+ isJSProcessActorRegistered = false;
+ try {
+ ChromeUtils.unregisterProcessActor("DevToolsProcess");
+ } catch (e) {
+ // If any pending query was still ongoing, this would throw
+ }
+ Services.ppmm.removeDelayedProcessScript(PROCESS_SCRIPT_URL);
+}
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
new file mode 100644
index 0000000000..1765bcc66c
--- /dev/null
+++ b/devtools/server/actors/watcher/target-helpers/content-process-jsprocessactor-startup.js
@@ -0,0 +1,26 @@
+/* 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
index 0e6f4f80d3..18d4d8f92e 100644
--- a/devtools/server/actors/watcher/target-helpers/frame-helper.js
+++ b/devtools/server/actors/watcher/target-helpers/frame-helper.js
@@ -6,14 +6,13 @@
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.
- loadInDevToolsLoader: false,
- }
+ // 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"
+ "resource://devtools/server/connectors/js-window-actor/WindowGlobalLogger.sys.mjs",
+ { global: "contextual" }
);
const Targets = require("resource://devtools/server/actors/targets/index.js");
diff --git a/devtools/server/actors/watcher/target-helpers/moz.build b/devtools/server/actors/watcher/target-helpers/moz.build
index b7c8983590..3b00f0ef47 100644
--- a/devtools/server/actors/watcher/target-helpers/moz.build
+++ b/devtools/server/actors/watcher/target-helpers/moz.build
@@ -5,6 +5,7 @@
# 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",
diff --git a/devtools/server/actors/watcher/target-helpers/process-helper.js b/devtools/server/actors/watcher/target-helpers/process-helper.js
index 8895d7ed66..e36f0a204c 100644
--- a/devtools/server/actors/watcher/target-helpers/process-helper.js
+++ b/devtools/server/actors/watcher/target-helpers/process-helper.js
@@ -4,205 +4,36 @@
"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.
- loadInDevToolsLoader: false,
- }
-);
-
-loader.lazyRequireGetter(
- this,
- "ChildDebuggerTransport",
- "resource://devtools/shared/transport/child-transport.js",
- true
-);
-
-const CONTENT_PROCESS_SCRIPT =
- "resource://devtools/server/startup/content-process-script.js";
-
/**
- * Map a MessageManager key to an Array of ContentProcessTargetActor "description" objects.
- * A single MessageManager might be linked to several ContentProcessTargetActors if there are several
- * Watcher actors instantiated on the DevToolsServer, via a single connection (in theory), but rather
- * via distinct connections (ex: a content toolbox and the browser toolbox).
- * Note that if we spawn two DevToolsServer, this module will be instantiated twice.
+ * Return the list of all DOM Processes except the one for the parent process
*
- * Each ContentProcessTargetActor "description" object is structured as follows
- * - {Object} actor: form of the content process target actor
- * - {String} prefix: forwarding prefix used to redirect all packet to the right content process's transport
- * - {ChildDebuggerTransport} childTransport: Transport forwarding all packets to the target's content process
- * - {WatcherActor} watcher: The Watcher actor for which we instantiated this content process target actor
+ * @return Array<nsIDOMProcessParent>
*/
-const actors = new WeakMap();
-
-// Save the list of all watcher actors that are watching for processes
-const watchers = new Set();
-
-function onContentProcessActorCreated(msg) {
- const { watcherActorID, prefix, actor } = msg.data;
- const watcher = WatcherRegistry.getWatcher(watcherActorID);
- if (!watcher) {
- throw new Error(
- `Receiving a content process actor without a watcher actor ${watcherActorID}`
- );
- }
- // Ignore watchers of other connections.
- // We may have two browser toolbox connected to the same process.
- // This will spawn two distinct Watcher actor and two distinct process target helper module.
- // Avoid processing the event many times, otherwise we will notify about the same target
- // multiple times.
- if (!watchers.has(watcher)) {
- return;
- }
- const messageManager = msg.target;
- const connection = watcher.conn;
-
- // Pipe Debugger message from/to parent/child via the message manager
- const childTransport = new ChildDebuggerTransport(messageManager, prefix);
- childTransport.hooks = {
- onPacket: connection.send.bind(connection),
- };
- childTransport.ready();
-
- connection.setForwarding(prefix, childTransport);
-
- const list = actors.get(messageManager) || [];
- list.push({
- prefix,
- childTransport,
- actor,
- watcher,
- });
- actors.set(messageManager, list);
-
- watcher.notifyTargetAvailable(actor);
-}
-
-function onContentProcessActorDestroyed(msg) {
- const { watcherActorID } = msg.data;
- const watcher = WatcherRegistry.getWatcher(watcherActorID);
- if (!watcher) {
- throw new Error(
- `Receiving a content process actor destruction without a watcher actor ${watcherActorID}`
- );
- }
- // Ignore watchers of other connections.
- // We may have two browser toolbox connected to the same process.
- // This will spawn two distinct Watcher actor and two distinct process target helper module.
- // Avoid processing the event many times, otherwise we will notify about the same target
- // multiple times.
- if (!watchers.has(watcher)) {
- return;
- }
- const messageManager = msg.target;
- unregisterWatcherForMessageManager(watcher, messageManager);
-}
-
-function onMessageManagerClose(messageManager, topic, data) {
- const list = actors.get(messageManager);
- if (!list || !list.length) {
- return;
- }
- for (const { prefix, childTransport, actor, watcher } of list) {
- watcher.notifyTargetDestroyed(actor);
-
- // If we have a child transport, the actor has already
- // been created. We need to stop using this message manager.
- childTransport.close();
- watcher.conn.cancelForwarding(prefix);
- }
- actors.delete(messageManager);
-}
-
-/**
- * Unregister everything created for a given watcher against a precise message manager:
- * - clear up things from `actors` WeakMap,
- * - notify all related target actors as being destroyed,
- * - close all DevTools Transports being created for each Message Manager.
- *
- * @param {WatcherActor} watcher
- * @param {MessageManager}
- * @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 unregisterWatcherForMessageManager(watcher, messageManager, options) {
- const targetActorDescriptions = actors.get(messageManager);
- if (!targetActorDescriptions || !targetActorDescriptions.length) {
- return;
- }
-
- // Destroy all transports related to this watcher and tells the client to purge all related actors
- const matchingTargetActorDescriptions = targetActorDescriptions.filter(
- item => item.watcher === watcher
+function getAllContentProcesses() {
+ return ChromeUtils.getAllDOMProcesses().filter(
+ process => process.childID !== 0
);
- for (const {
- prefix,
- childTransport,
- actor,
- } of matchingTargetActorDescriptions) {
- watcher.notifyTargetDestroyed(actor, options);
-
- childTransport.close();
- watcher.conn.cancelForwarding(prefix);
- }
-
- // Then update global `actors` WeakMap by stripping all data about this watcher
- const remainingTargetActorDescriptions = targetActorDescriptions.filter(
- item => item.watcher !== watcher
- );
- if (!remainingTargetActorDescriptions.length) {
- actors.delete(messageManager);
- } else {
- actors.set(messageManager, remainingTargetActorDescriptions);
- }
}
/**
- * Destroy everything related to a given watcher that has been created in this module:
- * (See unregisterWatcherForMessageManager)
+ * Instantiate all Content Process targets in all the DOM Processes.
*
* @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 closeWatcherTransports(watcher, options) {
- for (let i = 0; i < Services.ppmm.childCount; i++) {
- const messageManager = Services.ppmm.getChildAt(i);
- unregisterWatcherForMessageManager(watcher, messageManager, options);
- }
-}
-
-function maybeRegisterMessageListeners(watcher) {
- const sizeBefore = watchers.size;
- watchers.add(watcher);
- if (sizeBefore == 0 && watchers.size == 1) {
- Services.ppmm.addMessageListener(
- "debug:content-process-actor",
- onContentProcessActorCreated
- );
- Services.ppmm.addMessageListener(
- "debug:content-process-actor-destroyed",
- onContentProcessActorDestroyed
+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,
+ })
);
- Services.obs.addObserver(onMessageManagerClose, "message-manager-close");
-
- // Load the content process server startup script only once,
- // otherwise it will be evaluated twice, listen to events twice and create
- // target actors twice.
- // We may try to load it twice when opening one Browser Toolbox via about:debugging
- // and another regular Browser Toolbox. Both will spawn a WatcherActor and watch for processes.
- const isContentProcessScripLoaded = Services.ppmm
- .getDelayedProcessScripts()
- .some(([uri]) => uri === CONTENT_PROCESS_SCRIPT);
- if (!isContentProcessScripLoaded) {
- Services.ppmm.loadProcessScript(CONTENT_PROCESS_SCRIPT, true);
- }
}
+ await Promise.all(promises);
}
/**
@@ -211,96 +42,16 @@ function maybeRegisterMessageListeners(watcher) {
* @param {boolean} options.isModeSwitching
* true when this is called as the result of a change to the devtools.browsertoolbox.scope pref
*/
-function maybeUnregisterMessageListeners(watcher, options = {}) {
- const sizeBefore = watchers.size;
- watchers.delete(watcher);
- closeWatcherTransports(watcher, options);
-
- if (sizeBefore == 1 && watchers.size == 0) {
- Services.ppmm.removeMessageListener(
- "debug:content-process-actor",
- onContentProcessActorCreated
- );
- Services.ppmm.removeMessageListener(
- "debug:content-process-actor-destroyed",
- onContentProcessActorDestroyed
- );
- Services.obs.removeObserver(onMessageManagerClose, "message-manager-close");
-
- // We inconditionally remove the process script, while we should only remove it
- // once the last DevToolsServer stop watching for processes.
- // We might have many server, using distinct loaders, so that this module
- // will be spawn many times and we should remove the script only once the last
- // module unregister the last watcher of all.
- Services.ppmm.removeDelayedProcessScript(CONTENT_PROCESS_SCRIPT);
-
- Services.ppmm.broadcastAsyncMessage("debug:destroy-process-script", {
- options,
+function destroyTargets(watcher, options) {
+ for (const domProcess of getAllContentProcesses()) {
+ const processActor = domProcess.getActor("DevToolsProcess");
+ processActor.destroyTarget({
+ watcherActorID: watcher.actorID,
+ isModeSwitching: options.isModeSwitching,
});
}
}
-async function createTargets(watcher) {
- // XXX: Should this move to WatcherRegistry??
- maybeRegisterMessageListeners(watcher);
-
- // Bug 1648499: This could be simplified when migrating to JSProcessActor by using sendQuery.
- // For now, hack into WatcherActor in order to know when we created one target
- // actor for each existing content process.
- // Also, we substract one as the parent process has a message manager and is counted
- // in `childCount`, but we ignore it from the process script and it won't reply.
- let contentProcessCount = Services.ppmm.childCount - 1;
- if (contentProcessCount == 0) {
- return;
- }
- const onTargetsCreated = new Promise(resolve => {
- let receivedTargetCount = 0;
- const listener = () => {
- receivedTargetCount++;
- mayBeResolve();
- };
- watcher.on("target-available-form", listener);
- const onContentProcessClosed = () => {
- // Update the content process count as one has been just destroyed
- contentProcessCount--;
- mayBeResolve();
- };
- Services.obs.addObserver(onContentProcessClosed, "message-manager-close");
- function mayBeResolve() {
- if (receivedTargetCount >= contentProcessCount) {
- watcher.off("target-available-form", listener);
- Services.obs.removeObserver(
- onContentProcessClosed,
- "message-manager-close"
- );
- resolve();
- }
- }
- });
-
- Services.ppmm.broadcastAsyncMessage("debug:instantiate-already-available", {
- watcherActorID: watcher.actorID,
- connectionPrefix: watcher.conn.prefix,
- sessionData: watcher.sessionData,
- });
-
- await onTargetsCreated;
-}
-
-/**
- * @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) {
- maybeUnregisterMessageListeners(watcher, options);
-
- Services.ppmm.broadcastAsyncMessage("debug:destroy-target", {
- watcherActorID: watcher.actorID,
- });
-}
-
/**
* Go over all existing content processes in order to communicate about new data entries
*
@@ -321,51 +72,19 @@ async function addOrSetSessionDataEntry({
entries,
updateType,
}) {
- let expectedCount = Services.ppmm.childCount - 1;
- if (expectedCount == 0) {
- return;
- }
- const onAllReplied = new Promise(resolve => {
- let count = 0;
- const listener = msg => {
- if (msg.data.watcherActorID != watcher.actorID) {
- return;
- }
- count++;
- maybeResolve();
- };
- Services.ppmm.addMessageListener(
- "debug:add-or-set-session-data-entry-done",
- listener
+ const promises = [];
+ for (const domProcess of getAllContentProcesses()) {
+ const processActor = domProcess.getActor("DevToolsProcess");
+ promises.push(
+ processActor.addOrSetSessionDataEntry({
+ watcherActorID: watcher.actorID,
+ type,
+ entries,
+ updateType,
+ })
);
- const onContentProcessClosed = (messageManager, topic, data) => {
- expectedCount--;
- maybeResolve();
- };
- const maybeResolve = () => {
- if (count == expectedCount) {
- Services.ppmm.removeMessageListener(
- "debug:add-or-set-session-data-entry-done",
- listener
- );
- Services.obs.removeObserver(
- onContentProcessClosed,
- "message-manager-close"
- );
- resolve();
- }
- };
- Services.obs.addObserver(onContentProcessClosed, "message-manager-close");
- });
-
- Services.ppmm.broadcastAsyncMessage("debug:add-or-set-session-data-entry", {
- watcherActorID: watcher.actorID,
- type,
- entries,
- updateType,
- });
-
- await onAllReplied;
+ }
+ await Promise.all(promises);
}
/**
@@ -373,12 +92,19 @@ async function addOrSetSessionDataEntry({
*
* See addOrSetSessionDataEntry for argument documentation.
*/
-function removeSessionDataEntry({ watcher, type, entries }) {
- Services.ppmm.broadcastAsyncMessage("debug:remove-session-data-entry", {
- watcherActorID: watcher.actorID,
- type,
- entries,
- });
+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 = {
diff --git a/devtools/server/actors/webbrowser.js b/devtools/server/actors/webbrowser.js
index c05a863839..89db57a642 100644
--- a/devtools/server/actors/webbrowser.js
+++ b/devtools/server/actors/webbrowser.js
@@ -52,7 +52,7 @@ const lazy = {};
loader.lazyGetter(lazy, "AddonManager", () => {
return ChromeUtils.importESModule(
"resource://gre/modules/AddonManager.sys.mjs",
- { loadInDevToolsLoader: false }
+ { global: "shared" }
).AddonManager;
});
@@ -710,35 +710,35 @@ Object.defineProperty(BrowserAddonList.prototype, "onListChanged", {
/**
* AddonManager listener must implement onDisabled.
*/
-BrowserAddonList.prototype.onDisabled = function (addon) {
+BrowserAddonList.prototype.onDisabled = function () {
this._onAddonManagerUpdated();
};
/**
* AddonManager listener must implement onEnabled.
*/
-BrowserAddonList.prototype.onEnabled = function (addon) {
+BrowserAddonList.prototype.onEnabled = function () {
this._onAddonManagerUpdated();
};
/**
* AddonManager listener must implement onInstalled.
*/
-BrowserAddonList.prototype.onInstalled = function (addon) {
+BrowserAddonList.prototype.onInstalled = function () {
this._onAddonManagerUpdated();
};
/**
* AddonManager listener must implement onOperationCancelled.
*/
-BrowserAddonList.prototype.onOperationCancelled = function (addon) {
+BrowserAddonList.prototype.onOperationCancelled = function () {
this._onAddonManagerUpdated();
};
/**
* AddonManager listener must implement onUninstalling.
*/
-BrowserAddonList.prototype.onUninstalling = function (addon) {
+BrowserAddonList.prototype.onUninstalling = function () {
this._onAddonManagerUpdated();
};
@@ -750,7 +750,7 @@ BrowserAddonList.prototype.onUninstalled = function (addon) {
this._onAddonManagerUpdated();
};
-BrowserAddonList.prototype._onAddonManagerUpdated = function (addon) {
+BrowserAddonList.prototype._onAddonManagerUpdated = function () {
this._notifyListChanged();
this._adjustListener();
};
diff --git a/devtools/server/actors/webconsole.js b/devtools/server/actors/webconsole.js
index 14401b3fbe..3de636be96 100644
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -1704,7 +1704,7 @@ class WebConsoleActor extends Actor {
* The "will-navigate" progress listener. This is used to clear the current
* eval scope.
*/
- _onWillNavigate({ window, isTopLevel }) {
+ _onWillNavigate({ isTopLevel }) {
if (isTopLevel) {
this._evalGlobal = null;
EventEmitter.off(this.parentActor, "will-navigate", this._onWillNavigate);
diff --git a/devtools/server/actors/webconsole/commands/experimental-commands.ftl b/devtools/server/actors/webconsole/commands/experimental-commands.ftl
index b11c29006b..66cb9d151e 100644
--- a/devtools/server/actors/webconsole/commands/experimental-commands.ftl
+++ b/devtools/server/actors/webconsole/commands/experimental-commands.ftl
@@ -9,13 +9,29 @@
webconsole-commands-usage-trace3 =
:trace
- Toggles the JavaScript tracer
+ Toggles the JavaScript tracer.
+
+ The tracer will display all functions being called by your page.
It supports the following arguments:
- --logMethod to be set to ‘console’ for logging to the web console (the default), or ‘stdout’ for logging to the standard output,
+ --logMethod to be set to ‘console’ for logging to the web console (the default), or ‘stdout’ for logging to the standard output.
+
+ --return Optional flag to be passed to also log when functions return.
+
--values Optional flag to be passed to log function call arguments as well as returned values (when returned frames are enabled).
+
--on-next-interaction Optional flag, when set, the tracer will only start on next mousedown or keydown event.
+
+ --dom-mutations Optional flag, when set, the tracer will log all DOM Mutations.
+ When passing a value, you can restrict to a particular mutation type via a coma-separated list:
+ - ‘add’ will only track DOM Node being added,
+ - ‘attributes’ will only track DOM Node whose attributes changed,
+ - ‘remove’ will only track DOM Node being removed.
+
--max-depth Optional flag, will restrict logging trace to a given depth passed as argument.
+
--max-records Optional flag, will automatically stop the tracer after having logged the passed amount of top level frames.
- --prefix Optional string which will be logged in front of all the trace logs,
+
+ --prefix Optional string which will be logged in front of all the trace logs.
+
--help or --usage to show this message.
diff --git a/devtools/server/actors/webconsole/commands/manager.js b/devtools/server/actors/webconsole/commands/manager.js
index 538ee7eac1..025e197e3b 100644
--- a/devtools/server/actors/webconsole/commands/manager.js
+++ b/devtools/server/actors/webconsole/commands/manager.js
@@ -11,6 +11,13 @@ loader.lazyRequireGetter(
true
);
+loader.lazyRequireGetter(
+ this,
+ ["DOM_MUTATIONS"],
+ "resource://devtools/server/tracer/tracer.jsm",
+ true
+);
+
loader.lazyGetter(this, "l10n", () => {
return new Localization(
[
@@ -88,7 +95,7 @@ const WebConsoleCommandsManager = {
* }
* });
*/
- register({ name, isSideEffectFree, command, validArguments, usage }) {
+ register({ name, isSideEffectFree, command, validArguments }) {
if (
typeof command != "function" &&
!(typeof command == "object" && typeof command.get == "function")
@@ -684,7 +691,7 @@ WebConsoleCommandsManager.register({
WebConsoleCommandsManager.register({
name: "help",
isSideEffectFree: false,
- command(owner, args) {
+ command(owner) {
owner.helperResult = { type: "help" };
},
});
@@ -873,13 +880,35 @@ WebConsoleCommandsManager.register({
const tracerActor =
owner.consoleActor.parentActor.getTargetScopedActor("tracer");
const logMethod = args.logMethod || "console";
+ let traceDOMMutations = null;
+ if ("dom-mutations" in args) {
+ // When no value is passed, track all types of mutations
+ if (args["dom-mutations"] === true) {
+ traceDOMMutations = ["add", "attributes", "remove"];
+ } 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);
+ if (!traceDOMMutations.every(e => acceptedValues.includes(e))) {
+ throw new Error(
+ `:trace --dom-mutations only accept a list of strings whose values can be: ${acceptedValues}`
+ );
+ }
+ } else {
+ throw new Error(
+ ":trace --dom-mutations accept only no arguments, or a list mutation type strings (add,attributes,remove)"
+ );
+ }
+ }
// Note that toggleTracing does some sanity checks and will throw meaningful error
// when the arguments are wrong.
const enabled = tracerActor.toggleTracing({
logMethod,
prefix: args.prefix || null,
+ traceFunctionReturn: !!args.returns,
traceValues: !!args.values,
traceOnNextInteraction: args["on-next-interaction"] || null,
+ traceDOMMutations,
maxDepth: args["max-depth"] || null,
maxRecords: args["max-records"] || null,
});
@@ -895,7 +924,9 @@ WebConsoleCommandsManager.register({
"max-depth",
"max-records",
"on-next-interaction",
+ "dom-mutations",
"prefix",
+ "returns",
"values",
],
});
diff --git a/devtools/server/actors/webconsole/eager-ecma-allowlist.js b/devtools/server/actors/webconsole/eager-ecma-allowlist.js
index defe98ad8b..041a4c4194 100644
--- a/devtools/server/actors/webconsole/eager-ecma-allowlist.js
+++ b/devtools/server/actors/webconsole/eager-ecma-allowlist.js
@@ -46,7 +46,11 @@ const functionAllowList = [
Array.prototype.reduceRight,
Array.prototype.slice,
Array.prototype.some,
+ Array.prototype.toReversed,
+ Array.prototype.toSorted,
+ Array.prototype.toSpliced,
Array.prototype.values,
+ Array.prototype.with,
ArrayBuffer,
ArrayBuffer.isView,
ArrayBuffer.prototype.slice,
@@ -94,7 +98,10 @@ const functionAllowList = [
TypedArray.prototype.slice,
TypedArray.prototype.some,
TypedArray.prototype.subarray,
+ TypedArray.prototype.toReversed,
+ TypedArray.prototype.toSorted,
TypedArray.prototype.values,
+ TypedArray.prototype.with,
...allProperties(JSON),
Map,
Map.prototype.forEach,
@@ -230,20 +237,4 @@ const getterAllowList = [
getter(TypedArray, Symbol.species),
];
-// TODO: Integrate in main list when changes array by copy ships by default
-const changesArrayByCopy = [
- Array.prototype.toReversed,
- Array.prototype.toSorted,
- Array.prototype.toSpliced,
- Array.prototype.with,
- TypedArray.prototype.toReversed,
- TypedArray.prototype.toSorted,
- TypedArray.prototype.with,
-];
-for (const fn of changesArrayByCopy) {
- if (typeof fn == "function") {
- functionAllowList.push(fn);
- }
-}
-
module.exports = { functions: functionAllowList, getters: getterAllowList };
diff --git a/devtools/server/actors/webconsole/eval-with-debugger.js b/devtools/server/actors/webconsole/eval-with-debugger.js
index d422d6cd5e..34836c354f 100644
--- a/devtools/server/actors/webconsole/eval-with-debugger.js
+++ b/devtools/server/actors/webconsole/eval-with-debugger.js
@@ -8,9 +8,15 @@ const Debugger = require("Debugger");
const DevToolsUtils = require("resource://devtools/shared/DevToolsUtils.js");
const lazy = {};
-ChromeUtils.defineESModuleGetters(lazy, {
- Reflect: "resource://gre/modules/reflect.sys.mjs",
-});
+if (!isWorker) {
+ ChromeUtils.defineESModuleGetters(
+ lazy,
+ {
+ Reflect: "resource://gre/modules/reflect.sys.mjs",
+ },
+ { global: "contextual" }
+ );
+}
loader.lazyRequireGetter(
this,
["isCommand"],
@@ -600,8 +606,12 @@ function nativeIsEagerlyEvaluateable(fn) {
return true;
}
+ // This needs to use isSameNativeWithJitInfo instead of isSameNative, given
+ // DOM methods share single native function with different JSJitInto,
+ // and isSameNative cannot distinguish between side-effect-free methods
+ // and others.
const natives = gSideEffectFreeNatives.get(fn.name);
- return natives && natives.some(n => fn.isSameNative(n));
+ return natives && natives.some(n => fn.isSameNativeWithJitInfo(n));
}
function updateConsoleInputEvaluation(dbg, webConsole) {
@@ -616,7 +626,7 @@ function updateConsoleInputEvaluation(dbg, webConsole) {
}
}
-function getEvalInput(string, bindings) {
+function getEvalInput(string) {
const trimmedString = string.trim();
// Add easter egg for console.mihai().
if (
diff --git a/devtools/server/actors/webconsole/listeners/console-file-activity.js b/devtools/server/actors/webconsole/listeners/console-file-activity.js
index 7e5ae0d1a8..ccc28c3b2a 100644
--- a/devtools/server/actors/webconsole/listeners/console-file-activity.js
+++ b/devtools/server/actors/webconsole/listeners/console-file-activity.js
@@ -82,7 +82,7 @@ ConsoleFileActivityListener.prototype = {
* URI has been loaded, then the remote Web Console instance is notified.
* @private
*/
- _checkFileActivity(progress, request, state, status) {
+ _checkFileActivity(progress, request, state) {
if (!(state & Ci.nsIWebProgressListener.STATE_START)) {
return;
}
diff --git a/devtools/server/actors/webconsole/listeners/document-events.js b/devtools/server/actors/webconsole/listeners/document-events.js
index 1c1f926436..42296bf62e 100644
--- a/devtools/server/actors/webconsole/listeners/document-events.js
+++ b/devtools/server/actors/webconsole/listeners/document-events.js
@@ -90,13 +90,7 @@ DocumentEventsListener.prototype = {
});
},
- onWillNavigate({
- window,
- isTopLevel,
- newURI,
- navigationStart,
- isFrameSwitching,
- }) {
+ onWillNavigate({ isTopLevel, newURI, navigationStart, isFrameSwitching }) {
// Ignore iframes
if (!isTopLevel) {
return;
@@ -177,7 +171,7 @@ DocumentEventsListener.prototype = {
});
},
- onStateChange(progress, request, flag, status) {
+ onStateChange(progress, request, flag) {
progress.QueryInterface(Ci.nsIDocShell);
// Ignore destroyed, or progress for same-process iframes
if (progress.isBeingDestroyed() || progress != this.webProgress) {
diff --git a/devtools/server/actors/worker/service-worker-registration-list.js b/devtools/server/actors/worker/service-worker-registration-list.js
index 9821108faf..6093edc97e 100644
--- a/devtools/server/actors/worker/service-worker-registration-list.js
+++ b/devtools/server/actors/worker/service-worker-registration-list.js
@@ -5,7 +5,8 @@
"use strict";
const { XPCOMUtils } = ChromeUtils.importESModule(
- "resource://gre/modules/XPCOMUtils.sys.mjs"
+ "resource://gre/modules/XPCOMUtils.sys.mjs",
+ { global: "contextual" }
);
loader.lazyRequireGetter(
this,
@@ -102,11 +103,11 @@ class ServiceWorkerRegistrationActorList {
this._mustNotify = false;
}
- onRegister(registration) {
+ onRegister() {
this._notifyListChanged();
}
- onUnregister(registration) {
+ onUnregister() {
this._notifyListChanged();
}
}
diff --git a/devtools/server/actors/worker/service-worker-registration.js b/devtools/server/actors/worker/service-worker-registration.js
index 1e5e80ae8b..1276711191 100644
--- a/devtools/server/actors/worker/service-worker-registration.js
+++ b/devtools/server/actors/worker/service-worker-registration.js
@@ -10,7 +10,8 @@ const {
} = require("resource://devtools/shared/specs/worker/service-worker-registration.js");
const { XPCOMUtils } = ChromeUtils.importESModule(
- "resource://gre/modules/XPCOMUtils.sys.mjs"
+ "resource://gre/modules/XPCOMUtils.sys.mjs",
+ { global: "contextual" }
);
const {
PushSubscriptionActor,
@@ -210,7 +211,7 @@ class ServiceWorkerRegistrationActor extends Actor {
if (pushSubscriptionActor) {
return Promise.resolve(pushSubscriptionActor);
}
- return new Promise((resolve, reject) => {
+ return new Promise(resolve => {
PushService.getSubscription(
registration.scope,
registration.principal,
diff --git a/devtools/server/actors/worker/worker-descriptor-actor-list.js b/devtools/server/actors/worker/worker-descriptor-actor-list.js
index 10bdb5d5d3..9826791511 100644
--- a/devtools/server/actors/worker/worker-descriptor-actor-list.js
+++ b/devtools/server/actors/worker/worker-descriptor-actor-list.js
@@ -5,7 +5,8 @@
"use strict";
const { XPCOMUtils } = ChromeUtils.importESModule(
- "resource://gre/modules/XPCOMUtils.sys.mjs"
+ "resource://gre/modules/XPCOMUtils.sys.mjs",
+ { global: "contextual" }
);
loader.lazyRequireGetter(
this,
diff --git a/devtools/server/connectors/content-process-connector.js b/devtools/server/connectors/content-process-connector.js
index ea95a5d6ab..a2d359e4a5 100644
--- a/devtools/server/connectors/content-process-connector.js
+++ b/devtools/server/connectors/content-process-connector.js
@@ -109,13 +109,11 @@ function connectToContentProcess(connection, mm, onDestroy) {
}
}
- const onMessageManagerClose = DevToolsUtils.makeInfallible(
- (subject, topic, data) => {
- if (subject == mm) {
- onClose();
- }
+ const onMessageManagerClose = DevToolsUtils.makeInfallible(subject => {
+ if (subject == mm) {
+ onClose();
}
- );
+ });
Services.obs.addObserver(onMessageManagerClose, "message-manager-close");
EventEmitter.on(connection, "closed", onClose);
diff --git a/devtools/server/connectors/frame-connector.js b/devtools/server/connectors/frame-connector.js
index 789d405d90..06fbcfd1e4 100644
--- a/devtools/server/connectors/frame-connector.js
+++ b/devtools/server/connectors/frame-connector.js
@@ -149,7 +149,7 @@ function connectToFrame(
trackMessageManager();
// Listen for app process exit
- const onMessageManagerClose = function (subject, topic, data) {
+ const onMessageManagerClose = function (subject) {
if (subject == mm) {
destroy();
}
diff --git a/devtools/server/connectors/js-process-actor/DevToolsProcessChild.sys.mjs b/devtools/server/connectors/js-process-actor/DevToolsProcessChild.sys.mjs
new file mode 100644
index 0000000000..9e8ad64eea
--- /dev/null
+++ b/devtools/server/connectors/js-process-actor/DevToolsProcessChild.sys.mjs
@@ -0,0 +1,362 @@
+/* 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/. */
+
+import { EventEmitter } from "resource://gre/modules/EventEmitter.sys.mjs";
+
+const lazy = {};
+ChromeUtils.defineESModuleGetters(
+ lazy,
+ {
+ releaseDistinctSystemPrincipalLoader:
+ "resource://devtools/shared/loader/DistinctSystemPrincipalLoader.sys.mjs",
+ useDistinctSystemPrincipalLoader:
+ "resource://devtools/shared/loader/DistinctSystemPrincipalLoader.sys.mjs",
+ },
+ { global: "contextual" }
+);
+
+// Name of the attribute into which we save data in `sharedData` object.
+const SHARED_DATA_KEY_NAME = "DevTools:watchedPerWatcher";
+
+// If true, log info about DOMProcess's being created.
+const DEBUG = false;
+
+/**
+ * Print information about operation being done against each content process.
+ *
+ * @param {nsIDOMProcessChild} domProcessChild
+ * The process for which we should log a message.
+ * @param {String} message
+ * Message to log.
+ */
+function logDOMProcess(domProcessChild, message) {
+ if (!DEBUG) {
+ return;
+ }
+ dump(" [pid:" + domProcessChild + "] " + message + "\n");
+}
+
+export class DevToolsProcessChild extends JSProcessActorChild {
+ constructor() {
+ super();
+
+ // The map is indexed by the Watcher Actor ID.
+ // The values are objects containing the following properties:
+ // - connection: the DevToolsServerConnection itself
+ // - actor: the ContentProcessTargetActor instance
+ this._connections = new Map();
+
+ this._onConnectionChange = this._onConnectionChange.bind(this);
+ EventEmitter.decorate(this);
+ }
+
+ instantiate() {
+ const { sharedData } = Services.cpmm;
+ const watchedDataByWatcherActor = sharedData.get(SHARED_DATA_KEY_NAME);
+ if (!watchedDataByWatcherActor) {
+ throw new Error(
+ "Request to instantiate the target(s) for the process, but `sharedData` is empty about watched targets"
+ );
+ }
+
+ // Create one Target actor for each prefix/client which listen to processes
+ for (const [watcherActorID, sessionData] of watchedDataByWatcherActor) {
+ const { connectionPrefix } = sessionData;
+
+ if (sessionData.targets?.includes("process")) {
+ this._createTargetActor(watcherActorID, connectionPrefix, sessionData);
+ }
+ }
+ }
+
+ /**
+ * Instantiate a new ProcessTarget for the given connection.
+ *
+ * @param String watcherActorID
+ * The ID of the WatcherActor who requested to observe and create these target actors.
+ * @param String parentConnectionPrefix
+ * The prefix of the DevToolsServerConnection of the Watcher Actor.
+ * This is used to compute a unique ID for the target actor.
+ * @param Object sessionData
+ * All data managed by the Watcher Actor and WatcherRegistry.sys.mjs, containing
+ * target types, resources types to be listened as well as breakpoints and any
+ * other data meant to be shared across processes and threads.
+ */
+ _createTargetActor(watcherActorID, parentConnectionPrefix, sessionData) {
+ // This method will be concurrently called from `observe()` and `DevToolsProcessParent:instantiate-already-available`
+ // When the JSprocessActor initializes itself and when the watcher want to force instantiating existing targets.
+ // Simply ignore the second call as there is nothing to return, neither to wait for as this method is synchronous.
+ if (this._connections.has(watcherActorID)) {
+ return;
+ }
+
+ // Compute a unique prefix, just for this DOM Process,
+ // which will be used to create a JSWindowActorTransport pair between content and parent processes.
+ // This is slightly hacky as we typicaly compute Prefix and Actor ID via `DevToolsServerConnection.allocID()`,
+ // but here, we can't have access to any DevTools connection as we are really early in the content process startup
+ // XXX: nsIDOMProcessChild's childID should be unique across processes, I think. So that should be safe?
+ // (this.manager == nsIDOMProcessChild interface)
+ // Ensure appending a final slash, otherwise the prefix may be the same between childID 1 and 10...
+ const forwardingPrefix =
+ parentConnectionPrefix + "contentProcess" + this.manager.childID + "/";
+
+ logDOMProcess(
+ this.manager,
+ "Instantiate ContentProcessTarget with prefix: " + forwardingPrefix
+ );
+
+ const { connection, targetActor } = this._createConnectionAndActor(
+ watcherActorID,
+ forwardingPrefix,
+ sessionData
+ );
+ this._connections.set(watcherActorID, {
+ connection,
+ actor: targetActor,
+ });
+
+ // Immediately queue a message for the parent process,
+ // in order to ensure that the JSWindowActorTransport is instantiated
+ // before any packet is sent from the content process.
+ // As the order of messages is guaranteed to be delivered in the order they
+ // were queued, we don't have to wait for anything around this sendAsyncMessage call.
+ // In theory, the ContentProcessTargetActor may emit events in its constructor.
+ // If it does, such RDP packets may be lost. But in practice, no events
+ // are emitted during its construction. Instead the frontend will start
+ // the communication first.
+ this.sendAsyncMessage("DevToolsProcessChild:connectFromContent", {
+ watcherActorID,
+ forwardingPrefix,
+ actor: targetActor.form(),
+ });
+
+ // Pass initialization data to the target actor
+ for (const type in sessionData) {
+ // `sessionData` will also contain `browserId` as well as entries with empty arrays,
+ // which shouldn't be processed.
+ const entries = sessionData[type];
+ if (!Array.isArray(entries) || !entries.length) {
+ continue;
+ }
+ targetActor.addOrSetSessionDataEntry(
+ type,
+ sessionData[type],
+ false,
+ "set"
+ );
+ }
+ }
+
+ _destroyTargetActor(watcherActorID, isModeSwitching) {
+ const connectionInfo = this._connections.get(watcherActorID);
+ // This connection has already been cleaned?
+ if (!connectionInfo) {
+ throw new Error(
+ `Trying to destroy a target actor that doesn't exists, or has already been destroyed. Watcher Actor ID:${watcherActorID}`
+ );
+ }
+ connectionInfo.connection.close({ isModeSwitching });
+ this._connections.delete(watcherActorID);
+ if (this._connections.size == 0) {
+ this.didDestroy({ isModeSwitching });
+ }
+ }
+
+ _createConnectionAndActor(watcherActorID, forwardingPrefix, sessionData) {
+ if (!this.loader) {
+ this.loader = lazy.useDistinctSystemPrincipalLoader(this);
+ }
+ const { DevToolsServer } = this.loader.require(
+ "devtools/server/devtools-server"
+ );
+
+ const { ContentProcessTargetActor } = this.loader.require(
+ "devtools/server/actors/targets/content-process"
+ );
+
+ DevToolsServer.init();
+
+ // For browser content toolbox, we do need a regular root actor and all tab
+ // actors, but don't need all the "browser actors" that are only useful when
+ // debugging the parent process via the browser toolbox.
+ DevToolsServer.registerActors({ target: true });
+ DevToolsServer.on("connectionchange", this._onConnectionChange);
+
+ const connection = DevToolsServer.connectToParentWindowActor(
+ this,
+ forwardingPrefix,
+ "DevToolsProcessChild:packet"
+ );
+
+ // Create the actual target actor.
+ const targetActor = new ContentProcessTargetActor(connection, {
+ sessionContext: sessionData.sessionContext,
+ });
+ // There is no root actor in content processes and so
+ // the target actor can't be managed by it, but we do have to manage
+ // the actor to have it working and be registered in the DevToolsServerConnection.
+ // We make it manage itself and become a top level actor.
+ targetActor.manage(targetActor);
+
+ const form = targetActor.form();
+ targetActor.once("destroyed", options => {
+ // This will destroy the content process one
+ this._destroyTargetActor(watcherActorID, options.isModeSwitching);
+ // And this will destroy the parent process one
+ try {
+ this.sendAsyncMessage("DevToolsProcessChild:destroy", {
+ actors: [
+ {
+ watcherActorID,
+ form,
+ },
+ ],
+ options,
+ });
+ } catch (e) {
+ // Ignore exception when the JSProcessActorChild has already been destroyed.
+ // We often try to emit this message while the process is being destroyed,
+ // but sendAsyncMessage doesn't have time to complete and throws.
+ if (
+ !e.message.includes("JSProcessActorChild cannot send at the moment")
+ ) {
+ throw e;
+ }
+ }
+ });
+
+ return { connection, targetActor };
+ }
+
+ /**
+ * Destroy the server once its last connection closes. Note that multiple
+ * frame scripts may be running in parallel and reuse the same server.
+ */
+ _onConnectionChange() {
+ if (this._destroyed) {
+ return;
+ }
+ this._destroyed = true;
+
+ const { DevToolsServer } = this.loader.require(
+ "devtools/server/devtools-server"
+ );
+
+ // Only destroy the server if there is no more connections to it. It may be
+ // used to debug another tab running in the same process.
+ if (DevToolsServer.hasConnection() || DevToolsServer.keepAlive) {
+ return;
+ }
+
+ DevToolsServer.off("connectionchange", this._onConnectionChange);
+ DevToolsServer.destroy();
+ }
+
+ /**
+ * Supported Queries
+ */
+
+ sendPacket(packet, prefix) {
+ this.sendAsyncMessage("DevToolsProcessChild:packet", { packet, prefix });
+ }
+
+ /**
+ * JsWindowActor API
+ */
+
+ async sendQuery(msg, args) {
+ try {
+ const res = await super.sendQuery(msg, args);
+ return res;
+ } catch (e) {
+ console.error("Failed to sendQuery in DevToolsProcessChild", msg);
+ console.error(e.toString());
+ throw e;
+ }
+ }
+
+ receiveMessage(message) {
+ switch (message.name) {
+ case "DevToolsProcessParent:instantiate-already-available": {
+ const { watcherActorID, connectionPrefix, sessionData } = message.data;
+ return this._createTargetActor(
+ watcherActorID,
+ connectionPrefix,
+ sessionData
+ );
+ }
+ case "DevToolsProcessParent:destroy": {
+ const { watcherActorID, isModeSwitching } = message.data;
+ return this._destroyTargetActor(watcherActorID, isModeSwitching);
+ }
+ case "DevToolsProcessParent:addOrSetSessionDataEntry": {
+ const { watcherActorID, type, entries, updateType } = message.data;
+ return this._addOrSetSessionDataEntry(
+ watcherActorID,
+ type,
+ entries,
+ updateType
+ );
+ }
+ case "DevToolsProcessParent:removeSessionDataEntry": {
+ const { watcherActorID, type, entries } = message.data;
+ return this._removeSessionDataEntry(watcherActorID, type, entries);
+ }
+ case "DevToolsProcessParent:packet":
+ return this.emit("packet-received", message);
+ default:
+ throw new Error(
+ "Unsupported message in DevToolsProcessParent: " + message.name
+ );
+ }
+ }
+
+ _getTargetActorForWatcherActorID(watcherActorID) {
+ const connectionInfo = this._connections.get(watcherActorID);
+ return connectionInfo?.actor;
+ }
+
+ _addOrSetSessionDataEntry(watcherActorID, type, entries, updateType) {
+ const targetActor = this._getTargetActorForWatcherActorID(watcherActorID);
+ if (!targetActor) {
+ throw new Error(
+ `No target actor for this Watcher Actor ID:"${watcherActorID}"`
+ );
+ }
+ return targetActor.addOrSetSessionDataEntry(
+ type,
+ entries,
+ false,
+ updateType
+ );
+ }
+
+ _removeSessionDataEntry(watcherActorID, type, entries) {
+ const targetActor = this._getTargetActorForWatcherActorID(watcherActorID);
+ // By the time we are calling this, the target may already have been destroyed.
+ if (!targetActor) {
+ return null;
+ }
+ return targetActor.removeSessionDataEntry(type, entries);
+ }
+
+ observe(subject, topic) {
+ if (topic === "init-devtools-content-process-actor") {
+ // This is triggered by the process actor registration and some code in process-helper.js
+ // which defines a unique topic to be observed
+ this.instantiate();
+ }
+ }
+
+ didDestroy(options) {
+ for (const { connection } of this._connections.values()) {
+ connection.close(options);
+ }
+ this._connections.clear();
+ if (this.loader) {
+ lazy.releaseDistinctSystemPrincipalLoader(this);
+ this.loader = null;
+ }
+ }
+}
diff --git a/devtools/server/connectors/js-process-actor/DevToolsProcessParent.sys.mjs b/devtools/server/connectors/js-process-actor/DevToolsProcessParent.sys.mjs
new file mode 100644
index 0000000000..28e11def68
--- /dev/null
+++ b/devtools/server/connectors/js-process-actor/DevToolsProcessParent.sys.mjs
@@ -0,0 +1,256 @@
+/* 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/. */
+
+import { loader } from "resource://devtools/shared/loader/Loader.sys.mjs";
+import { EventEmitter } from "resource://gre/modules/EventEmitter.sys.mjs";
+
+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 lazy = {};
+loader.lazyRequireGetter(
+ lazy,
+ "JsWindowActorTransport",
+ "devtools/shared/transport/js-window-actor-transport",
+ true
+);
+
+export class DevToolsProcessParent extends JSProcessActorParent {
+ constructor() {
+ super();
+
+ this._destroyed = false;
+
+ // Map of DevToolsServerConnection's used to forward the messages from/to
+ // the client. The connections run in the parent process, as this code. We
+ // may have more than one when there is more than one client debugging the
+ // same frame. For example, a content toolbox and the browser toolbox.
+ //
+ // The map is indexed by the connection prefix.
+ // The values are objects containing the following properties:
+ // - actor: the frame target actor(as a form)
+ // - connection: the DevToolsServerConnection used to communicate with the
+ // frame target actor
+ // - prefix: the forwarding prefix used by the connection to know
+ // how to forward packets to the frame target
+ // - transport: the JsWindowActorTransport
+ //
+ // Reminder about prefixes: all DevToolsServerConnections have a `prefix`
+ // which can be considered as a kind of id. On top of this, parent process
+ // DevToolsServerConnections also have forwarding prefixes because they are
+ // responsible for forwarding messages to content process connections.
+ this._connections = new Map();
+
+ this._onConnectionClosed = this._onConnectionClosed.bind(this);
+ EventEmitter.decorate(this);
+ }
+
+ /**
+ * Request the content process to create the ContentProcessTarget
+ */
+ instantiateTarget({
+ watcherActorID,
+ connectionPrefix,
+ sessionContext,
+ sessionData,
+ }) {
+ return this.sendQuery(
+ "DevToolsProcessParent:instantiate-already-available",
+ {
+ watcherActorID,
+ connectionPrefix,
+ sessionContext,
+ sessionData,
+ }
+ );
+ }
+
+ destroyTarget({ watcherActorID, isModeSwitching }) {
+ this.sendAsyncMessage("DevToolsProcessParent:destroy", {
+ watcherActorID,
+ isModeSwitching,
+ });
+ }
+
+ /**
+ * Communicate to the content process that some data have been added.
+ */
+ addOrSetSessionDataEntry({ watcherActorID, type, entries, updateType }) {
+ return this.sendQuery("DevToolsProcessParent:addOrSetSessionDataEntry", {
+ watcherActorID,
+ type,
+ entries,
+ updateType,
+ });
+ }
+
+ /**
+ * Communicate to the content process that some data have been removed.
+ */
+ removeSessionDataEntry({ watcherActorID, type, entries }) {
+ this.sendAsyncMessage("DevToolsProcessParent:removeSessionDataEntry", {
+ watcherActorID,
+ type,
+ entries,
+ });
+ }
+
+ connectFromContent({ watcherActorID, forwardingPrefix, actor }) {
+ const watcher = WatcherRegistry.getWatcher(watcherActorID);
+
+ if (!watcher) {
+ throw new Error(
+ `Watcher Actor with ID '${watcherActorID}' can't be found.`
+ );
+ }
+ const connection = watcher.conn;
+
+ connection.on("closed", this._onConnectionClosed);
+
+ // Create a js-window-actor based transport.
+ const transport = new lazy.JsWindowActorTransport(
+ this,
+ forwardingPrefix,
+ "DevToolsProcessParent:packet"
+ );
+ transport.hooks = {
+ onPacket: connection.send.bind(connection),
+ onClosed() {},
+ };
+ transport.ready();
+
+ connection.setForwarding(forwardingPrefix, transport);
+
+ this._connections.set(watcher.conn.prefix, {
+ watcher,
+ connection,
+ // This prefix is the prefix of the DevToolsServerConnection, running
+ // in the content process, for which we should forward packets to, based on its prefix.
+ // While `watcher.connection` is also a DevToolsServerConnection, but from this process,
+ // the parent process. It is the one receiving Client packets and the one, from which
+ // we should forward packets from.
+ forwardingPrefix,
+ transport,
+ actor,
+ });
+
+ watcher.notifyTargetAvailable(actor);
+ }
+
+ _onConnectionClosed(status, prefix) {
+ if (this._connections.has(prefix)) {
+ const { connection } = this._connections.get(prefix);
+ this._cleanupConnection(connection);
+ }
+ }
+
+ /**
+ * Close and unregister a given DevToolsServerConnection.
+ *
+ * @param {DevToolsServerConnection} connection
+ * @param {object} options
+ * @param {boolean} options.isModeSwitching
+ * true when this is called as the result of a change to the devtools.browsertoolbox.scope pref
+ */
+ async _cleanupConnection(connection, options = {}) {
+ const connectionInfo = this._connections.get(connection.prefix);
+ if (!connectionInfo) {
+ return;
+ }
+ const { forwardingPrefix, transport } = connectionInfo;
+
+ connection.off("closed", this._onConnectionClosed);
+ if (transport) {
+ // If we have a child transport, the actor has already
+ // been created. We need to stop using this transport.
+ transport.close(options);
+ }
+
+ this._connections.delete(connection.prefix);
+ if (!this._connections.size) {
+ this._destroy(options);
+ }
+
+ // When cancelling the forwarding, one RDP event is sent to the client to purge all requests
+ // and actors related to a given prefix. Do this *after* calling _destroy which will emit
+ // the target-destroyed RDP event. This helps the Watcher Front retrieve the related target front,
+ // otherwise it would be too eagerly destroyed by the purge event.
+ connection.cancelForwarding(forwardingPrefix);
+ }
+
+ /**
+ * Destroy and cleanup everything for this DOM Process.
+ *
+ * @param {object} options
+ * @param {boolean} options.isModeSwitching
+ * true when this is called as the result of a change to the devtools.browsertoolbox.scope pref
+ */
+ _destroy(options) {
+ if (this._destroyed) {
+ return;
+ }
+ this._destroyed = true;
+
+ for (const { actor, connection, watcher } of this._connections.values()) {
+ watcher.notifyTargetDestroyed(actor, options);
+ this._cleanupConnection(connection, options);
+ }
+ }
+
+ /**
+ * Supported Queries
+ */
+
+ sendPacket(packet, prefix) {
+ this.sendAsyncMessage("DevToolsProcessParent:packet", { packet, prefix });
+ }
+
+ /**
+ * JsWindowActor API
+ */
+
+ async sendQuery(msg, args) {
+ try {
+ const res = await super.sendQuery(msg, args);
+ return res;
+ } catch (e) {
+ console.error("Failed to sendQuery in DevToolsProcessParent", msg);
+ console.error(e.toString());
+ throw e;
+ }
+ }
+
+ receiveMessage(message) {
+ switch (message.name) {
+ case "DevToolsProcessChild:connectFromContent":
+ return this.connectFromContent(message.data);
+ case "DevToolsProcessChild:packet":
+ return this.emit("packet-received", message);
+ case "DevToolsProcessChild:destroy":
+ for (const { form, watcherActorID } of message.data.actors) {
+ const watcher = WatcherRegistry.getWatcher(watcherActorID);
+ // As we instruct to destroy all targets when the watcher is destroyed,
+ // we may easily receive the target destruction notification *after*
+ // the watcher has been removed from the registry.
+ if (watcher) {
+ watcher.notifyTargetDestroyed(form, message.data.options);
+ this._cleanupConnection(watcher.conn, message.data.options);
+ }
+ }
+ return null;
+ default:
+ throw new Error(
+ "Unsupported message in DevToolsProcessParent: " + message.name
+ );
+ }
+ }
+
+ didDestroy() {
+ this._destroy();
+ }
+}
diff --git a/devtools/server/connectors/js-process-actor/moz.build b/devtools/server/connectors/js-process-actor/moz.build
new file mode 100644
index 0000000000..e1a1f5dc9d
--- /dev/null
+++ b/devtools/server/connectors/js-process-actor/moz.build
@@ -0,0 +1,10 @@
+# -*- 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(
+ "DevToolsProcessChild.sys.mjs",
+ "DevToolsProcessParent.sys.mjs",
+)
diff --git a/devtools/server/connectors/js-window-actor/DevToolsFrameChild.sys.mjs b/devtools/server/connectors/js-window-actor/DevToolsFrameChild.sys.mjs
index 519cd10325..acb5e97110 100644
--- a/devtools/server/connectors/js-window-actor/DevToolsFrameChild.sys.mjs
+++ b/devtools/server/connectors/js-window-actor/DevToolsFrameChild.sys.mjs
@@ -173,7 +173,7 @@ export class DevToolsFrameChild extends JSWindowActorChild {
* The prefix of the DevToolsServerConnection of the Watcher Actor.
* This is used to compute a unique ID for the target actor.
* @param Object options.sessionData
- * All data managed by the Watcher Actor and WatcherRegistry.jsm, containing
+ * All data managed by the Watcher Actor and WatcherRegistry.sys.mjs, containing
* target types, resources types to be listened as well as breakpoints and any
* other data meant to be shared across processes and threads.
* @param Boolean options.isDocumentCreation
@@ -364,6 +364,10 @@ export class DevToolsFrameChild extends JSWindowActorChild {
ignoreSubFrames: isEveryFrameTargetEnabled,
sessionContext: sessionData.sessionContext,
});
+ // There is no root actor in content processes and so
+ // the target actor can't be managed by it, but we do have to manage
+ // the actor to have it working and be registered in the DevToolsServerConnection.
+ // We make it manage itself and become a top level actor.
targetActor.manage(targetActor);
targetActor.createdFromJsWindowActor = true;
@@ -554,10 +558,10 @@ export class DevToolsFrameChild extends JSWindowActorChild {
sessionContext,
});
// By the time we are calling this, the target may already have been destroyed.
- if (targetActor) {
- return targetActor.removeSessionDataEntry(type, entries);
+ if (!targetActor) {
+ return null;
}
- return null;
+ return targetActor.removeSessionDataEntry(type, entries);
}
handleEvent({ type, persisted, target }) {
@@ -691,8 +695,8 @@ export class DevToolsFrameChild extends JSWindowActorChild {
didDestroy(options) {
logWindowGlobal(this.manager, "Destroy WindowGlobalTarget");
- for (const [, connectionInfo] of this._connections) {
- connectionInfo.connection.close(options);
+ for (const { connection } of this._connections.values()) {
+ connection.close(options);
}
this._connections.clear();
diff --git a/devtools/server/connectors/js-window-actor/DevToolsFrameParent.sys.mjs b/devtools/server/connectors/js-window-actor/DevToolsFrameParent.sys.mjs
index 3c5af2a724..31750d58e4 100644
--- a/devtools/server/connectors/js-window-actor/DevToolsFrameParent.sys.mjs
+++ b/devtools/server/connectors/js-window-actor/DevToolsFrameParent.sys.mjs
@@ -7,11 +7,9 @@ import { EventEmitter } from "resource://gre/modules/EventEmitter.sys.mjs";
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.
- loadInDevToolsLoader: false,
- }
+ // WatcherRegistry needs to be a true singleton and loads ActorManagerParent
+ // which also has to be a true singleton.
+ { global: "shared" }
);
const lazy = {};
diff --git a/devtools/server/connectors/js-window-actor/DevToolsWorkerParent.sys.mjs b/devtools/server/connectors/js-window-actor/DevToolsWorkerParent.sys.mjs
index ebe3d10ad5..cb9bffc2ca 100644
--- a/devtools/server/connectors/js-window-actor/DevToolsWorkerParent.sys.mjs
+++ b/devtools/server/connectors/js-window-actor/DevToolsWorkerParent.sys.mjs
@@ -7,11 +7,9 @@ import { EventEmitter } from "resource://gre/modules/EventEmitter.sys.mjs";
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.
- loadInDevToolsLoader: false,
- }
+ // WatcherRegistry needs to be a true singleton and loads ActorManagerParent
+ // which also has to be a true singleton.
+ { global: "shared" }
);
const lazy = {};
@@ -175,11 +173,7 @@ export class DevToolsWorkerParent extends JSWindowActorParent {
watcher.notifyTargetAvailable(workerTargetForm);
}
- workerTargetDestroyed({
- watcherActorID,
- forwardingPrefix,
- workerTargetForm,
- }) {
+ workerTargetDestroyed({ watcherActorID, workerTargetForm }) {
const watcher = WatcherRegistry.getWatcher(watcherActorID);
if (!watcher) {
diff --git a/devtools/server/connectors/moz.build b/devtools/server/connectors/moz.build
index a8b6fa1fea..fd4baf81ff 100644
--- a/devtools/server/connectors/moz.build
+++ b/devtools/server/connectors/moz.build
@@ -5,6 +5,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DIRS += [
+ "js-process-actor",
"js-window-actor",
"process-actor",
]
diff --git a/devtools/server/connectors/process-actor/DevToolsServiceWorkerParent.sys.mjs b/devtools/server/connectors/process-actor/DevToolsServiceWorkerParent.sys.mjs
index 17fa89e7ac..2073f47e76 100644
--- a/devtools/server/connectors/process-actor/DevToolsServiceWorkerParent.sys.mjs
+++ b/devtools/server/connectors/process-actor/DevToolsServiceWorkerParent.sys.mjs
@@ -6,11 +6,9 @@ import { EventEmitter } from "resource://gre/modules/EventEmitter.sys.mjs";
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.
- loadInDevToolsLoader: false,
- }
+ // WatcherRegistry needs to be a true singleton and loads ActorManagerParent
+ // which also has to be a true singleton.
+ { global: "shared" }
);
const lazy = {};
@@ -185,11 +183,7 @@ export class DevToolsServiceWorkerParent extends JSProcessActorParent {
watcher.notifyTargetAvailable(serviceWorkerTargetForm);
}
- serviceWorkerTargetDestroyed({
- watcherActorID,
- forwardingPrefix,
- serviceWorkerTargetForm,
- }) {
+ serviceWorkerTargetDestroyed({ watcherActorID, serviceWorkerTargetForm }) {
const watcher = WatcherRegistry.getWatcher(watcherActorID);
if (!watcher) {
diff --git a/devtools/server/devtools-server-connection.js b/devtools/server/devtools-server-connection.js
index 53d977a8fe..4c5a1180b0 100644
--- a/devtools/server/devtools-server-connection.js
+++ b/devtools/server/devtools-server-connection.js
@@ -73,14 +73,6 @@ DevToolsServerConnection.prototype = {
return this._prefix;
},
- /**
- * For a DevToolsServerConnection used in content processes,
- * returns the prefix of the connection it originates from, from the parent process.
- */
- get parentPrefix() {
- this.prefix.replace(/child\d+\//, "");
- },
-
_transport: null,
get transport() {
return this._transport;
diff --git a/devtools/server/performance/memory.js b/devtools/server/performance/memory.js
index c983a742ec..96f20ed0b6 100644
--- a/devtools/server/performance/memory.js
+++ b/devtools/server/performance/memory.js
@@ -15,9 +15,13 @@ loader.lazyRequireGetter(
"resource://devtools/shared/event-emitter.js"
);
const lazy = {};
-ChromeUtils.defineESModuleGetters(lazy, {
- DeferredTask: "resource://gre/modules/DeferredTask.sys.mjs",
-});
+ChromeUtils.defineESModuleGetters(
+ lazy,
+ {
+ DeferredTask: "resource://gre/modules/DeferredTask.sys.mjs",
+ },
+ { global: "contextual" }
+);
loader.lazyRequireGetter(
this,
"StackFrameCache",
diff --git a/devtools/server/socket/websocket-server.js b/devtools/server/socket/websocket-server.js
index 4236ec2921..d49c6fbf1b 100644
--- a/devtools/server/socket/websocket-server.js
+++ b/devtools/server/socket/websocket-server.js
@@ -27,7 +27,7 @@ function readLine(input) {
let line = "";
const wait = () => {
input.asyncWait(
- stream => {
+ () => {
try {
const amountToRead = HEADER_MAX_LEN - line.length;
line += delimitedRead(input, "\n", amountToRead);
@@ -72,7 +72,7 @@ function writeString(output, data) {
}
output.asyncWait(
- stream => {
+ () => {
try {
const written = output.write(data, data.length);
data = data.slice(written);
diff --git a/devtools/server/startup/content-process-script.js b/devtools/server/startup/content-process-script.js
index ffd461c4e2..3449eb465a 100644
--- a/devtools/server/startup/content-process-script.js
+++ b/devtools/server/startup/content-process-script.js
@@ -37,7 +37,7 @@ class ContentProcessStartup {
this.maybeCreateExistingTargetActors();
}
- observe(subject, topic, data) {
+ observe(subject, topic) {
switch (topic) {
case "xpcom-shutdown": {
this.destroy();
@@ -143,7 +143,7 @@ class ContentProcessStartup {
/**
* Called when the content process just started.
- * This will start creating ContentProcessTarget actors, but only if DevTools code (WatcherActor / WatcherRegistry.jsm)
+ * This will start creating ContentProcessTarget actors, but only if DevTools code (WatcherActor / WatcherRegistry.sys.mjs)
* put some data in `sharedData` telling us to do so.
*/
maybeCreateExistingTargetActors() {
diff --git a/devtools/server/tests/browser/browser_accessibility_keyboard_audit.js b/devtools/server/tests/browser/browser_accessibility_keyboard_audit.js
index fee9814b6c..e59cac87a3 100644
--- a/devtools/server/tests/browser/browser_accessibility_keyboard_audit.js
+++ b/devtools/server/tests/browser/browser_accessibility_keyboard_audit.js
@@ -186,7 +186,11 @@ add_task(async function () {
null,
],
["Interactive grid that is not focusable.", "#grid-1", null],
- ["Focusable interactive grid.", "#grid-2", null],
+ [
+ "Focusable interactive grid.",
+ "#grid-2",
+ { score: "WARNING", issue: "FOCUSABLE_NO_SEMANTICS" },
+ ],
[
"Non interactive ARIA table does not need to be focusable.",
"#table-1",
diff --git a/devtools/server/tests/browser/browser_connectToFrame.js b/devtools/server/tests/browser/browser_connectToFrame.js
index 568eb1acc1..1047393470 100644
--- a/devtools/server/tests/browser/browser_connectToFrame.js
+++ b/devtools/server/tests/browser/browser_connectToFrame.js
@@ -39,7 +39,7 @@ add_task(async function () {
const { Actor } = require("resource://devtools/shared/protocol/Actor.js");
class ConnectToFrameTestActor extends Actor {
- constructor(conn, tab) {
+ constructor(conn) {
super(conn, { typeName: "connectToFrameTest", methods: [] });
dump("instantiate test actor\n");
this.requestTypes = {
diff --git a/devtools/server/tests/browser/browser_debugger_server.js b/devtools/server/tests/browser/browser_debugger_server.js
index 8b36076b34..11a96a66c9 100644
--- a/devtools/server/tests/browser/browser_debugger_server.js
+++ b/devtools/server/tests/browser/browser_debugger_server.js
@@ -181,7 +181,7 @@ async function assertDevToolsOpened(tab, expected, message) {
);
}
-async function setContentServerKeepAlive(tab, keepAlive, message) {
+async function setContentServerKeepAlive(tab, keepAlive) {
await SpecialPowers.spawn(
tab.linkedBrowser,
[keepAlive],
diff --git a/devtools/server/tests/browser/browser_storage_updates.js b/devtools/server/tests/browser/browser_storage_updates.js
index 50926538a5..c1b56f6706 100644
--- a/devtools/server/tests/browser/browser_storage_updates.js
+++ b/devtools/server/tests/browser/browser_storage_updates.js
@@ -17,7 +17,7 @@ const sessionString = l10n.formatValueSync("storage-expires-session");
const TESTS = [
// index 0
{
- async action(win) {
+ async action() {
await addCookie("c1", "foobar1");
await addCookie("c2", "foobar2");
await localStorageSetItem("l1", "foobar1");
diff --git a/devtools/server/tests/chrome/inactive-property-helper/align-content.mjs b/devtools/server/tests/chrome/inactive-property-helper/align-content.mjs
index a871081fad..49469baf22 100644
--- a/devtools/server/tests/chrome/inactive-property-helper/align-content.mjs
+++ b/devtools/server/tests/chrome/inactive-property-helper/align-content.mjs
@@ -6,10 +6,17 @@
export default [
{
- info: "align-content is inactive on block elements (until bug 1105571 is fixed)",
+ info: "align-content is active on block elements when layout.css.align-content.blocks.enabled is true",
property: "align-content",
tagName: "div",
rules: ["div { align-content: center; }"],
+ isActive: true,
+ },
+ {
+ info: "align-content is inactive on inline elements",
+ property: "align-content",
+ tagName: "span",
+ rules: ["div { align-content: center; }"],
isActive: false,
},
{
@@ -27,7 +34,7 @@ export default [
isActive: true,
},
{
- info: "align-content is inactive on flex items",
+ info: "align-content is active on flex items (as they have a computed display of block)",
property: "align-content",
createTestElement: rootNode => {
const container = document.createElement("div");
@@ -38,10 +45,10 @@ export default [
},
rules: ["div { display: flex; }", "span { align-content: center; }"],
ruleIndex: 1,
- isActive: false,
+ isActive: true,
},
{
- info: "align-content is inactive on grid items",
+ info: "align-content is active on grid items (as they have a computed display of block)",
property: "align-content",
createTestElement: rootNode => {
const container = document.createElement("div");
@@ -52,7 +59,7 @@ export default [
},
rules: ["div { display: grid; }", "span { align-content: center; }"],
ruleIndex: 1,
- isActive: false,
+ isActive: true,
},
{
info: "align-content:baseline is active on flex items",
@@ -89,4 +96,11 @@ export default [
rules: ["div { display: table-cell; align-content: baseline; }"],
isActive: true,
},
+ {
+ info: "align-content:end is inactive on table cells until Bug 1883357 is fixed",
+ property: "align-content",
+ tagName: "div",
+ rules: ["div { display: table-cell; align-content: end; }"],
+ isActive: false,
+ },
];
diff --git a/devtools/server/tests/chrome/memory-helpers.js b/devtools/server/tests/chrome/memory-helpers.js
index e4db689134..ac24568779 100644
--- a/devtools/server/tests/chrome/memory-helpers.js
+++ b/devtools/server/tests/chrome/memory-helpers.js
@@ -57,7 +57,7 @@ async function destroyServerAndFinish(target) {
}
function waitForTime(ms) {
- return new Promise((resolve, reject) => {
+ return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
diff --git a/devtools/server/tests/chrome/suspendTimeouts_content.js b/devtools/server/tests/chrome/suspendTimeouts_content.js
index cb41653cff..3114578877 100644
--- a/devtools/server/tests/chrome/suspendTimeouts_content.js
+++ b/devtools/server/tests/chrome/suspendTimeouts_content.js
@@ -62,7 +62,7 @@ function resume_timeouts() {
// The buggy code calls this handler from the resumeTimeouts call, before the
// main thread returns to the event loop. The correct code calls this only once
// the JavaScript invocation that called resumeTimeouts has run to completion.
-function handle_echo({ data }) {
+function handle_echo() {
ok(
resumeTimeouts_has_returned,
"worker message delivered from main event loop"
diff --git a/devtools/server/tests/chrome/test_inspector-hide.html b/devtools/server/tests/chrome/test_inspector-hide.html
index e699400ee0..ea6ed74560 100644
--- a/devtools/server/tests/chrome/test_inspector-hide.html
+++ b/devtools/server/tests/chrome/test_inspector-hide.html
@@ -40,7 +40,7 @@ addTest(function testRearrange() {
const computed = gInspectee.defaultView.getComputedStyle(listNode);
is(computed.visibility, "visible", "Node should be visible to start with");
return gWalker.hideNode(listFront);
- }).then(response => {
+ }).then(() => {
const computed = gInspectee.defaultView.getComputedStyle(listNode);
is(computed.visibility, "hidden", "Node should be hidden");
return gWalker.unhideNode(listFront);
diff --git a/devtools/server/tests/chrome/test_inspector-inactive-property-helper.html b/devtools/server/tests/chrome/test_inspector-inactive-property-helper.html
index 86c783c035..c3d9d26aee 100644
--- a/devtools/server/tests/chrome/test_inspector-inactive-property-helper.html
+++ b/devtools/server/tests/chrome/test_inspector-inactive-property-helper.html
@@ -16,15 +16,18 @@ SimpleTest.waitForExplicitFinish();
const INACTIVE_CSS_PREF = "devtools.inspector.inactive.css.enabled";
const CUSTOM_HIGHLIGHT_API = "dom.customHighlightAPI.enabled";
const TEXT_WRAP_BALANCE = "layout.css.text-wrap-balance.enabled";
+ const ALIGN_CONTENT_BLOCKS = "layout.css.align-content.blocks.enabled";
Services.prefs.setBoolPref(INACTIVE_CSS_PREF, true);
Services.prefs.setBoolPref(CUSTOM_HIGHLIGHT_API, true);
Services.prefs.setBoolPref(TEXT_WRAP_BALANCE, true);
+ Services.prefs.setBoolPref(ALIGN_CONTENT_BLOCKS, true);
SimpleTest.registerCleanupFunction(() => {
Services.prefs.clearUserPref(INACTIVE_CSS_PREF);
Services.prefs.clearUserPref(CUSTOM_HIGHLIGHT_API);
Services.prefs.clearUserPref(TEXT_WRAP_BALANCE);
+ Services.prefs.clearUserPref(ALIGN_CONTENT_BLOCKS);
});
const FOLDER = "./inactive-property-helper";
diff --git a/devtools/server/tests/chrome/test_inspector-pseudoclass-lock.html b/devtools/server/tests/chrome/test_inspector-pseudoclass-lock.html
index 949066255d..c40a94d469 100644
--- a/devtools/server/tests/chrome/test_inspector-pseudoclass-lock.html
+++ b/devtools/server/tests/chrome/test_inspector-pseudoclass-lock.html
@@ -23,7 +23,7 @@ window.onload = function() {
let gInspectee = null;
let gWalker = null;
-async function setup(callback) {
+async function setup() {
const url = document.getElementById("inspectorContent").href;
const { target, doc } = await attachURL(url);
gInspectee = doc;
diff --git a/devtools/server/tests/chrome/test_memory_allocations_04.html b/devtools/server/tests/chrome/test_memory_allocations_04.html
index 8bb64c591c..ae50a38291 100644
--- a/devtools/server/tests/chrome/test_memory_allocations_04.html
+++ b/devtools/server/tests/chrome/test_memory_allocations_04.html
@@ -29,7 +29,7 @@ window.onload = function() {
}
}
- const testProbability = async function(p, expected) {
+ const testProbability = async function(p) {
info("probability = " + p);
await memory.startRecordingAllocations({
probability: p,
diff --git a/devtools/server/tests/chrome/test_suspendTimeouts.js b/devtools/server/tests/chrome/test_suspendTimeouts.js
index 614ac60cdb..d8a993fa8d 100644
--- a/devtools/server/tests/chrome/test_suspendTimeouts.js
+++ b/devtools/server/tests/chrome/test_suspendTimeouts.js
@@ -131,7 +131,7 @@ window.onload = function () {
}, 1000);
}
- function finish(message) {
+ function finish() {
SimpleTest.info("suspendTimeouts_content.js", "called finish");
SimpleTest.finish();
}
diff --git a/devtools/server/tests/xpcshell/registertestactors-lazy.js b/devtools/server/tests/xpcshell/registertestactors-lazy.js
index ef04e7a8d2..3ab7ebb1a8 100644
--- a/devtools/server/tests/xpcshell/registertestactors-lazy.js
+++ b/devtools/server/tests/xpcshell/registertestactors-lazy.js
@@ -21,13 +21,13 @@ const lazySpec = generateActorSpec({
});
class LazyActor extends Actor {
- constructor(conn, id) {
+ constructor(conn) {
super(conn, lazySpec);
Services.obs.notifyObservers(null, "actor", "instantiated");
}
- hello(str) {
+ hello() {
return "world";
}
}
diff --git a/devtools/server/tests/xpcshell/test_blackboxing-01.js b/devtools/server/tests/xpcshell/test_blackboxing-01.js
index 6c549b908e..981692dd6c 100644
--- a/devtools/server/tests/xpcshell/test_blackboxing-01.js
+++ b/devtools/server/tests/xpcshell/test_blackboxing-01.js
@@ -114,7 +114,7 @@ function evalCode() {
Cu.evalInSandbox(
"" + function runTest() { // line 1
doStuff( // line 2 - Break here
- function (n) { // line 3 - Step through `doStuff` to here
+ function () { // line 3 - Step through `doStuff` to here
(() => {})(); // line 4
debugger; // line 5
} // line 6
diff --git a/devtools/server/tests/xpcshell/test_blackboxing-02.js b/devtools/server/tests/xpcshell/test_blackboxing-02.js
index 66efaee6c8..b713b33187 100644
--- a/devtools/server/tests/xpcshell/test_blackboxing-02.js
+++ b/devtools/server/tests/xpcshell/test_blackboxing-02.js
@@ -80,7 +80,7 @@ function evalCode(debuggee) {
Cu.evalInSandbox(
"" + function runTest() { // line 1
doStuff( // line 2
- function(n) { // line 3
+ function() { // line 3
debugger; // line 5
} // line 6
); // line 7
diff --git a/devtools/server/tests/xpcshell/test_blackboxing-05.js b/devtools/server/tests/xpcshell/test_blackboxing-05.js
index 388c87da88..0a9bbbbdae 100644
--- a/devtools/server/tests/xpcshell/test_blackboxing-05.js
+++ b/devtools/server/tests/xpcshell/test_blackboxing-05.js
@@ -81,7 +81,7 @@ function evalCode(debuggee) {
"" +
function runTest() { // line 1
doStuff( // line 2
- function(n) { // line 3
+ function() { // line 3
debugger; // line 4
} // line 5
); // line 6
diff --git a/devtools/server/tests/xpcshell/test_breakpoint-03.js b/devtools/server/tests/xpcshell/test_breakpoint-03.js
index f598660a98..9b0dbe3c0b 100644
--- a/devtools/server/tests/xpcshell/test_breakpoint-03.js
+++ b/devtools/server/tests/xpcshell/test_breakpoint-03.js
@@ -50,7 +50,7 @@ add_task(
Assert.equal(debuggee.b, undefined);
// Remove the breakpoint.
- bpClient.remove(function (response) {
+ bpClient.remove(function () {
threadFront.resume().then(resolve);
});
});
diff --git a/devtools/server/tests/xpcshell/test_breakpoint-05.js b/devtools/server/tests/xpcshell/test_breakpoint-05.js
index f678b285b1..288f2d3a6e 100644
--- a/devtools/server/tests/xpcshell/test_breakpoint-05.js
+++ b/devtools/server/tests/xpcshell/test_breakpoint-05.js
@@ -35,7 +35,7 @@ add_task(
Assert.equal(debuggee.b, undefined);
// Remove the breakpoint.
- bpClient.remove(function (response) {
+ bpClient.remove(function () {
threadFront.resume().then(resolve);
});
});
diff --git a/devtools/server/tests/xpcshell/test_breakpoint-06.js b/devtools/server/tests/xpcshell/test_breakpoint-06.js
index 79ddcdc3d4..a2cdcd1340 100644
--- a/devtools/server/tests/xpcshell/test_breakpoint-06.js
+++ b/devtools/server/tests/xpcshell/test_breakpoint-06.js
@@ -35,7 +35,7 @@ add_task(
Assert.equal(debuggee.b, undefined);
// Remove the breakpoint.
- bpClient.remove(function (response) {
+ bpClient.remove(function () {
threadFront.resume().then(resolve);
});
});
diff --git a/devtools/server/tests/xpcshell/test_breakpoint-07.js b/devtools/server/tests/xpcshell/test_breakpoint-07.js
index e6391747bb..a6a1576c5c 100644
--- a/devtools/server/tests/xpcshell/test_breakpoint-07.js
+++ b/devtools/server/tests/xpcshell/test_breakpoint-07.js
@@ -35,7 +35,7 @@ add_task(
Assert.equal(debuggee.b, undefined);
// Remove the breakpoint.
- bpClient.remove(function (response) {
+ bpClient.remove(function () {
threadFront.resume().then(resolve);
});
});
diff --git a/devtools/server/tests/xpcshell/test_breakpoint-08.js b/devtools/server/tests/xpcshell/test_breakpoint-08.js
index bff0cc3b52..44a7c11803 100644
--- a/devtools/server/tests/xpcshell/test_breakpoint-08.js
+++ b/devtools/server/tests/xpcshell/test_breakpoint-08.js
@@ -43,7 +43,7 @@ add_task(
Assert.equal(debuggee.b, undefined);
// Remove the breakpoint.
- response.bpClient.remove(function (response) {
+ response.bpClient.remove(function () {
threadFront.resume().then(resolve);
});
});
diff --git a/devtools/server/tests/xpcshell/test_breakpoint-09.js b/devtools/server/tests/xpcshell/test_breakpoint-09.js
index 90b334102d..5dbf8be62a 100644
--- a/devtools/server/tests/xpcshell/test_breakpoint-09.js
+++ b/devtools/server/tests/xpcshell/test_breakpoint-09.js
@@ -43,7 +43,7 @@ add_task(
await client.waitForRequestsToSettle();
done = true;
- threadFront.once("paused", function (packet) {
+ threadFront.once("paused", function () {
// The breakpoint should not be hit again.
threadFront.resume().then(function () {
Assert.ok(false);
diff --git a/devtools/server/tests/xpcshell/test_breakpoint-11.js b/devtools/server/tests/xpcshell/test_breakpoint-11.js
index a29cd2f768..341f983c39 100644
--- a/devtools/server/tests/xpcshell/test_breakpoint-11.js
+++ b/devtools/server/tests/xpcshell/test_breakpoint-11.js
@@ -9,7 +9,7 @@
*/
add_task(
- threadFrontTest(async ({ threadFront, client, debuggee }) => {
+ threadFrontTest(async ({ threadFront, debuggee }) => {
const packet = await executeOnNextTickAndWaitForPause(
() => evaluateTestCode(debuggee),
threadFront
diff --git a/devtools/server/tests/xpcshell/test_breakpoint-12.js b/devtools/server/tests/xpcshell/test_breakpoint-12.js
index 44b524f1cf..05de46bb6f 100644
--- a/devtools/server/tests/xpcshell/test_breakpoint-12.js
+++ b/devtools/server/tests/xpcshell/test_breakpoint-12.js
@@ -23,7 +23,7 @@ add_task(
);
const location = { line: debuggee.line0 + 3 };
- source.setBreakpoint(location).then(function ([response, bpClient]) {
+ source.setBreakpoint(location).then(function ([response]) {
// Check that the breakpoint has properly skipped forward one line.
Assert.equal(response.actualLocation.source.actor, source.actor);
Assert.equal(response.actualLocation.line, location.line + 1);
@@ -75,7 +75,7 @@ add_task(
Assert.equal(debuggee.a, 1);
Assert.equal(debuggee.b, undefined);
- threadFront.once("paused", function (packet) {
+ threadFront.once("paused", function () {
// We don't expect any more pauses after the breakpoint was hit once.
Assert.ok(false);
});
diff --git a/devtools/server/tests/xpcshell/test_breakpoint-14.js b/devtools/server/tests/xpcshell/test_breakpoint-14.js
index 835edb1385..aa4c92bdd4 100644
--- a/devtools/server/tests/xpcshell/test_breakpoint-14.js
+++ b/devtools/server/tests/xpcshell/test_breakpoint-14.js
@@ -10,7 +10,7 @@
*/
add_task(
- threadFrontTest(async ({ threadFront, client, debuggee }) => {
+ threadFrontTest(async ({ threadFront, debuggee }) => {
const packet = await executeOnNextTickAndWaitForPause(
() => evaluateTestCode(debuggee),
threadFront
diff --git a/devtools/server/tests/xpcshell/test_breakpoint-16.js b/devtools/server/tests/xpcshell/test_breakpoint-16.js
index a42306eee1..6ca098cef9 100644
--- a/devtools/server/tests/xpcshell/test_breakpoint-16.js
+++ b/devtools/server/tests/xpcshell/test_breakpoint-16.js
@@ -9,7 +9,7 @@
*/
add_task(
- threadFrontTest(async ({ threadFront, client, debuggee }) => {
+ threadFrontTest(async ({ threadFront, debuggee }) => {
const packet = await executeOnNextTickAndWaitForPause(
() => evaluateTestCode(debuggee),
threadFront
diff --git a/devtools/server/tests/xpcshell/test_client_request.js b/devtools/server/tests/xpcshell/test_client_request.js
index 837bee5047..5c19884824 100644
--- a/devtools/server/tests/xpcshell/test_client_request.js
+++ b/devtools/server/tests/xpcshell/test_client_request.js
@@ -203,7 +203,7 @@ function test_client_request_after_close() {
});
request.then(
- response => {
+ () => {
ok(false, "Request succeed even after client.close");
},
response => {
diff --git a/devtools/server/tests/xpcshell/test_conditional_breakpoint-04.js b/devtools/server/tests/xpcshell/test_conditional_breakpoint-04.js
index b270b92974..941b480347 100644
--- a/devtools/server/tests/xpcshell/test_conditional_breakpoint-04.js
+++ b/devtools/server/tests/xpcshell/test_conditional_breakpoint-04.js
@@ -9,7 +9,7 @@
*/
add_task(
- threadFrontTest(async ({ threadFront, debuggee, commands }) => {
+ threadFrontTest(async ({ threadFront, debuggee }) => {
await threadFront.setBreakpoint(
{ sourceUrl: "conditional_breakpoint-04.js", line: 3 },
{ condition: "throw new Error()" }
diff --git a/devtools/server/tests/xpcshell/test_dbgglobal.js b/devtools/server/tests/xpcshell/test_dbgglobal.js
index 407e270da4..6e6f40bb5d 100644
--- a/devtools/server/tests/xpcshell/test_dbgglobal.js
+++ b/devtools/server/tests/xpcshell/test_dbgglobal.js
@@ -69,14 +69,14 @@ function run_test() {
);
client2.close();
},
- onTransportClosed(result) {
+ onTransportClosed() {
client1.close();
},
};
client2.ready();
},
- onTransportClosed(result) {
+ onTransportClosed() {
do_test_finished();
},
};
diff --git a/devtools/server/tests/xpcshell/test_forwardingprefix.js b/devtools/server/tests/xpcshell/test_forwardingprefix.js
index e917350da5..fa9afbf2f0 100644
--- a/devtools/server/tests/xpcshell/test_forwardingprefix.js
+++ b/devtools/server/tests/xpcshell/test_forwardingprefix.js
@@ -51,7 +51,7 @@ function newConnection(prefix) {
function createMainConnection() {
({ conn: gMainConnection, transport: gMainTransport } = newConnection());
gClient = new DevToolsClient(gMainTransport);
- gClient.connect().then(([type, traits]) => run_next_test());
+ gClient.connect().then(() => run_next_test());
}
/*
@@ -152,7 +152,7 @@ function createSubconnection1() {
const { conn, transport } = newSubconnection("prefix1");
gSubconnection1 = conn;
transport.ready();
- gClient.expectReply("prefix1/root", reply => run_next_test());
+ gClient.expectReply("prefix1/root", () => run_next_test());
}
// Establish forwarding, but don't put any actors in that server.
@@ -165,7 +165,7 @@ function createSubconnection2() {
const { conn, transport } = newSubconnection("prefix2");
gSubconnection2 = conn;
transport.ready();
- gClient.expectReply("prefix2/root", reply => run_next_test());
+ gClient.expectReply("prefix2/root", () => run_next_test());
}
function TestForwardPrefix12OnlyRoot() {
diff --git a/devtools/server/tests/xpcshell/test_framearguments-01.js b/devtools/server/tests/xpcshell/test_framearguments-01.js
index 524d43f58c..9f6f433e70 100644
--- a/devtools/server/tests/xpcshell/test_framearguments-01.js
+++ b/devtools/server/tests/xpcshell/test_framearguments-01.js
@@ -33,6 +33,8 @@ function evalCode(debuggee) {
debuggee.eval(
"(" +
function () {
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(number, bool, string, null_, undef, object) {
debugger;
}
diff --git a/devtools/server/tests/xpcshell/test_framebindings-07.js b/devtools/server/tests/xpcshell/test_framebindings-07.js
index 77d43dfba8..ee655da07a 100644
--- a/devtools/server/tests/xpcshell/test_framebindings-07.js
+++ b/devtools/server/tests/xpcshell/test_framebindings-07.js
@@ -4,7 +4,7 @@
"use strict";
add_task(
- threadFrontTest(async ({ threadFront, debuggee, client }) => {
+ threadFrontTest(async ({ threadFront, debuggee }) => {
const packet = await executeOnNextTickAndWaitForPause(
() => evalCode(debuggee),
threadFront
diff --git a/devtools/server/tests/xpcshell/test_functiongrips-01.js b/devtools/server/tests/xpcshell/test_functiongrips-01.js
index 5abce26875..ccadeba11d 100644
--- a/devtools/server/tests/xpcshell/test_functiongrips-01.js
+++ b/devtools/server/tests/xpcshell/test_functiongrips-01.js
@@ -8,6 +8,8 @@ add_task(
// Test named function
function evalCode() {
debuggee.eval(
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arg1) {
debugger;
}.toString()
diff --git a/devtools/server/tests/xpcshell/test_getRuleText.js b/devtools/server/tests/xpcshell/test_getRuleText.js
index fe53dca158..bc89da974c 100644
--- a/devtools/server/tests/xpcshell/test_getRuleText.js
+++ b/devtools/server/tests/xpcshell/test_getRuleText.js
@@ -16,6 +16,25 @@ const TEST_DATA = [
throws: true,
},
{
+ desc: "Null input",
+ input: null,
+ line: 1,
+ column: 1,
+ throws: true,
+ },
+ {
+ desc: "Missing loc",
+ input: "#id{color:red;background:yellow;}",
+ throws: true,
+ },
+ {
+ desc: "No opening bracket",
+ input: "/* hey */",
+ line: 1,
+ column: 1,
+ throws: true,
+ },
+ {
desc: "Simplest test case",
input: "#id{color:red;background:yellow;}",
line: 1,
@@ -39,18 +58,6 @@ const TEST_DATA = [
expected: { offset: 4, text: "color:red;background:yellow;" },
},
{
- desc: "Null input",
- input: null,
- line: 1,
- column: 1,
- throws: true,
- },
- {
- desc: "Missing loc",
- input: "#id{color:red;background:yellow;}",
- throws: true,
- },
- {
desc: "Multi-lines CSS",
input: [
"/* this is a multi line css */",
@@ -61,7 +68,7 @@ const TEST_DATA = [
" /*something else here */",
"* {",
" color: purple;",
- "}",
+ "} ",
].join("\n"),
line: 7,
column: 1,
@@ -107,12 +114,60 @@ const TEST_DATA = [
},
},
{
+ desc: "Attribute selector containing a { character",
+ input: `div[data-x="{"]{color: gold}`,
+ line: 1,
+ column: 1,
+ expected: {
+ offset: 16,
+ text: "color: gold",
+ },
+ },
+ {
desc: "Rule contains no tokens",
input: "div{}",
line: 1,
column: 1,
expected: { offset: 4, text: "" },
},
+ {
+ desc: "Rule contains invalid declaration",
+ input: `#id{color;}`,
+ line: 1,
+ column: 1,
+ expected: { offset: 4, text: "color;" },
+ },
+ {
+ desc: "Rule contains invalid declaration",
+ input: `#id{-}`,
+ line: 1,
+ column: 1,
+ expected: { offset: 4, text: "-" },
+ },
+ {
+ desc: "Rule contains nested rule",
+ input: `#id{background: gold; .nested{color:blue;} color: tomato; }`,
+ line: 1,
+ column: 1,
+ expected: {
+ offset: 4,
+ text: "background: gold; .nested{color:blue;} color: tomato; ",
+ },
+ },
+ {
+ desc: "Rule contains nested rule with invalid declaration",
+ input: `#id{.nested{color;}}`,
+ line: 1,
+ column: 1,
+ expected: { offset: 4, text: ".nested{color;}" },
+ },
+ {
+ desc: "Rule contains unicode chars",
+ input: `#id /*🙃*/ {content: "☃️";}`,
+ line: 1,
+ column: 1,
+ expected: { offset: 12, text: `content: "☃️";` },
+ },
];
function run_test() {
diff --git a/devtools/server/tests/xpcshell/test_interrupt.js b/devtools/server/tests/xpcshell/test_interrupt.js
index 07593a7360..5431c8f508 100644
--- a/devtools/server/tests/xpcshell/test_interrupt.js
+++ b/devtools/server/tests/xpcshell/test_interrupt.js
@@ -4,7 +4,7 @@
"use strict";
add_task(
- threadFrontTest(async ({ threadFront, debuggee, client, targetFront }) => {
+ threadFrontTest(async ({ threadFront }) => {
const onPaused = waitForEvent(threadFront, "paused");
await threadFront.interrupt();
await onPaused;
diff --git a/devtools/server/tests/xpcshell/test_logpoint-01.js b/devtools/server/tests/xpcshell/test_logpoint-01.js
index a5cb4f2197..e2d3186d47 100644
--- a/devtools/server/tests/xpcshell/test_logpoint-01.js
+++ b/devtools/server/tests/xpcshell/test_logpoint-01.js
@@ -12,7 +12,7 @@ const Resources = require("resource://devtools/server/actors/resources/index.js"
add_task(
threadFrontTest(async ({ threadActor, threadFront, debuggee, client }) => {
let lastMessage, lastExpression;
- const targetActor = threadActor._parent;
+ const { targetActor } = threadActor;
// Only Workers are evaluating through the WebConsoleActor.
// Tabs will be evaluating directly via the frame object.
targetActor._consoleActor = {
diff --git a/devtools/server/tests/xpcshell/test_logpoint-02.js b/devtools/server/tests/xpcshell/test_logpoint-02.js
index d84d3fc324..183478ee32 100644
--- a/devtools/server/tests/xpcshell/test_logpoint-02.js
+++ b/devtools/server/tests/xpcshell/test_logpoint-02.js
@@ -12,7 +12,7 @@ const Resources = require("resource://devtools/server/actors/resources/index.js"
add_task(
threadFrontTest(async ({ threadActor, threadFront, debuggee, client }) => {
let lastMessage, lastExpression;
- const targetActor = threadActor._parent;
+ const { targetActor } = threadActor;
// Only Workers are evaluating through the WebConsoleActor.
// Tabs will be evaluating directly via the frame object.
targetActor._consoleActor = {
diff --git a/devtools/server/tests/xpcshell/test_logpoint-03.js b/devtools/server/tests/xpcshell/test_logpoint-03.js
index b5d4440889..0b6e37a9f6 100644
--- a/devtools/server/tests/xpcshell/test_logpoint-03.js
+++ b/devtools/server/tests/xpcshell/test_logpoint-03.js
@@ -10,9 +10,9 @@
const Resources = require("resource://devtools/server/actors/resources/index.js");
add_task(
- threadFrontTest(async ({ threadActor, threadFront, debuggee, client }) => {
+ threadFrontTest(async ({ threadActor, threadFront, debuggee }) => {
let lastMessage, lastExpression;
- const targetActor = threadActor._parent;
+ const { targetActor } = threadActor;
// Only Workers are evaluating through the WebConsoleActor.
// Tabs will be evaluating directly via the frame object.
targetActor._consoleActor = {
diff --git a/devtools/server/tests/xpcshell/test_longstringgrips-01.js b/devtools/server/tests/xpcshell/test_longstringgrips-01.js
index ac0b228c17..fc93a80495 100644
--- a/devtools/server/tests/xpcshell/test_longstringgrips-01.js
+++ b/devtools/server/tests/xpcshell/test_longstringgrips-01.js
@@ -66,6 +66,8 @@ function test_longstring_grip() {
});
gDebuggee.eval(
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arg1) {
debugger;
}.toString()
diff --git a/devtools/server/tests/xpcshell/test_objectgrips-02.js b/devtools/server/tests/xpcshell/test_objectgrips-02.js
index 810a5009c0..71bf7d2c27 100644
--- a/devtools/server/tests/xpcshell/test_objectgrips-02.js
+++ b/devtools/server/tests/xpcshell/test_objectgrips-02.js
@@ -29,6 +29,8 @@ add_task(
function evalCode(debuggee) {
debuggee.eval(
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arg1) {
debugger;
}.toString()
diff --git a/devtools/server/tests/xpcshell/test_objectgrips-03.js b/devtools/server/tests/xpcshell/test_objectgrips-03.js
index c8a51d41d3..b41b478f5d 100644
--- a/devtools/server/tests/xpcshell/test_objectgrips-03.js
+++ b/devtools/server/tests/xpcshell/test_objectgrips-03.js
@@ -44,6 +44,8 @@ add_task(
function evalCode(debuggee) {
debuggee.eval(
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arg1) {
debugger;
}.toString()
diff --git a/devtools/server/tests/xpcshell/test_objectgrips-04.js b/devtools/server/tests/xpcshell/test_objectgrips-04.js
index d08705db3c..eb3fb06103 100644
--- a/devtools/server/tests/xpcshell/test_objectgrips-04.js
+++ b/devtools/server/tests/xpcshell/test_objectgrips-04.js
@@ -46,6 +46,8 @@ add_task(
function evalCode(debuggee) {
debuggee.eval(
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arg1) {
debugger;
}.toString()
diff --git a/devtools/server/tests/xpcshell/test_objectgrips-05.js b/devtools/server/tests/xpcshell/test_objectgrips-05.js
index 4c6f0f107a..916c9db6df 100644
--- a/devtools/server/tests/xpcshell/test_objectgrips-05.js
+++ b/devtools/server/tests/xpcshell/test_objectgrips-05.js
@@ -38,6 +38,8 @@ add_task(
function evalCode(debuggee) {
debuggee.eval(
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arg1) {
debugger;
}.toString()
diff --git a/devtools/server/tests/xpcshell/test_objectgrips-06.js b/devtools/server/tests/xpcshell/test_objectgrips-06.js
index ef3d2b5b66..e6c0f835a2 100644
--- a/devtools/server/tests/xpcshell/test_objectgrips-06.js
+++ b/devtools/server/tests/xpcshell/test_objectgrips-06.js
@@ -38,6 +38,8 @@ add_task(
function evalCode(debuggee) {
debuggee.eval(
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arg1) {
debugger;
}.toString()
diff --git a/devtools/server/tests/xpcshell/test_objectgrips-07.js b/devtools/server/tests/xpcshell/test_objectgrips-07.js
index 2a3a0bf00e..4ccd711fc7 100644
--- a/devtools/server/tests/xpcshell/test_objectgrips-07.js
+++ b/devtools/server/tests/xpcshell/test_objectgrips-07.js
@@ -43,6 +43,8 @@ add_task(
function evalCode(debuggee) {
debuggee.eval(
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arg1) {
debugger;
}.toString()
diff --git a/devtools/server/tests/xpcshell/test_objectgrips-08.js b/devtools/server/tests/xpcshell/test_objectgrips-08.js
index 1a37f19fb8..892368d7fe 100644
--- a/devtools/server/tests/xpcshell/test_objectgrips-08.js
+++ b/devtools/server/tests/xpcshell/test_objectgrips-08.js
@@ -36,6 +36,8 @@ add_task(
function evalCode(debuggee) {
debuggee.eval(
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arg1) {
debugger;
}.toString()
diff --git a/devtools/server/tests/xpcshell/test_objectgrips-16.js b/devtools/server/tests/xpcshell/test_objectgrips-16.js
index 785c3bc36d..5e66aa700b 100644
--- a/devtools/server/tests/xpcshell/test_objectgrips-16.js
+++ b/devtools/server/tests/xpcshell/test_objectgrips-16.js
@@ -29,6 +29,8 @@ add_task(
function eval_code() {
debuggee.eval(
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arg1) {
debugger;
}.toString()
diff --git a/devtools/server/tests/xpcshell/test_objectgrips-17.js b/devtools/server/tests/xpcshell/test_objectgrips-17.js
index edaea88eaa..ed90cc7ae6 100644
--- a/devtools/server/tests/xpcshell/test_objectgrips-17.js
+++ b/devtools/server/tests/xpcshell/test_objectgrips-17.js
@@ -288,6 +288,8 @@ async function run_tests_in_principal(
) {
const { debuggee } = options;
debuggee.eval(
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arg1, arg2) {
debugger;
}.toString()
diff --git a/devtools/server/tests/xpcshell/test_objectgrips-18.js b/devtools/server/tests/xpcshell/test_objectgrips-18.js
index 90c38d99a9..9e169b596c 100644
--- a/devtools/server/tests/xpcshell/test_objectgrips-18.js
+++ b/devtools/server/tests/xpcshell/test_objectgrips-18.js
@@ -31,6 +31,8 @@ add_task(
function eval_code() {
debuggee.eval(
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arg1) {
debugger;
}.toString()
diff --git a/devtools/server/tests/xpcshell/test_objectgrips-19.js b/devtools/server/tests/xpcshell/test_objectgrips-19.js
index 655c7d0f43..9ee92638bc 100644
--- a/devtools/server/tests/xpcshell/test_objectgrips-19.js
+++ b/devtools/server/tests/xpcshell/test_objectgrips-19.js
@@ -9,8 +9,10 @@ registerCleanupFunction(() => {
});
add_task(
- threadFrontTest(async ({ threadFront, debuggee, client }) => {
+ threadFrontTest(async ({ threadFront, debuggee }) => {
debuggee.eval(
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arg1) {
debugger;
}.toString()
diff --git a/devtools/server/tests/xpcshell/test_objectgrips-20.js b/devtools/server/tests/xpcshell/test_objectgrips-20.js
index 5027ca31a7..f36ea20f7e 100644
--- a/devtools/server/tests/xpcshell/test_objectgrips-20.js
+++ b/devtools/server/tests/xpcshell/test_objectgrips-20.js
@@ -17,6 +17,8 @@ registerCleanupFunction(() => {
add_task(
threadFrontTest(async ({ threadFront, debuggee, client }) => {
debuggee.eval(
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arg1) {
debugger;
}.toString()
diff --git a/devtools/server/tests/xpcshell/test_objectgrips-21.js b/devtools/server/tests/xpcshell/test_objectgrips-21.js
index 88296f7786..daed1808c7 100644
--- a/devtools/server/tests/xpcshell/test_objectgrips-21.js
+++ b/devtools/server/tests/xpcshell/test_objectgrips-21.js
@@ -213,6 +213,8 @@ async function test_unsafe_grips(
tests
) {
debuggee.eval(
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arg1, arg2) {
debugger;
}.toString()
diff --git a/devtools/server/tests/xpcshell/test_objectgrips-22.js b/devtools/server/tests/xpcshell/test_objectgrips-22.js
index 34264f5534..fd33030fef 100644
--- a/devtools/server/tests/xpcshell/test_objectgrips-22.js
+++ b/devtools/server/tests/xpcshell/test_objectgrips-22.js
@@ -40,6 +40,8 @@ add_task(
function evalCode(debuggee) {
debuggee.eval(
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arg1) {
debugger;
}.toString()
diff --git a/devtools/server/tests/xpcshell/test_objectgrips-25.js b/devtools/server/tests/xpcshell/test_objectgrips-25.js
index f80572bb19..4fdce90805 100644
--- a/devtools/server/tests/xpcshell/test_objectgrips-25.js
+++ b/devtools/server/tests/xpcshell/test_objectgrips-25.js
@@ -12,6 +12,8 @@ registerCleanupFunction(() => {
function evalCode(debuggee) {
debuggee.eval(
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(obj) {
debugger;
}.toString()
diff --git a/devtools/server/tests/xpcshell/test_objectgrips-fn-apply-01.js b/devtools/server/tests/xpcshell/test_objectgrips-fn-apply-01.js
index f576f16a5e..a67b08b0d2 100644
--- a/devtools/server/tests/xpcshell/test_objectgrips-fn-apply-01.js
+++ b/devtools/server/tests/xpcshell/test_objectgrips-fn-apply-01.js
@@ -63,6 +63,8 @@ add_task(
function evalCode(debuggee) {
debuggee.eval(
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arg1) {
debugger;
}.toString()
diff --git a/devtools/server/tests/xpcshell/test_objectgrips-fn-apply-02.js b/devtools/server/tests/xpcshell/test_objectgrips-fn-apply-02.js
index 743286281c..fda27f6874 100644
--- a/devtools/server/tests/xpcshell/test_objectgrips-fn-apply-02.js
+++ b/devtools/server/tests/xpcshell/test_objectgrips-fn-apply-02.js
@@ -41,6 +41,8 @@ add_task(
function evalCode(debuggee) {
debuggee.eval(
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arg1) {
debugger;
}.toString()
diff --git a/devtools/server/tests/xpcshell/test_objectgrips-fn-apply-03.js b/devtools/server/tests/xpcshell/test_objectgrips-fn-apply-03.js
index 6a3e919661..69a317b5e0 100644
--- a/devtools/server/tests/xpcshell/test_objectgrips-fn-apply-03.js
+++ b/devtools/server/tests/xpcshell/test_objectgrips-fn-apply-03.js
@@ -38,6 +38,8 @@ add_task(
function evalCode(debuggee) {
debuggee.eval(
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arg1) {
debugger;
}.toString()
diff --git a/devtools/server/tests/xpcshell/test_objectgrips-nested-promise.js b/devtools/server/tests/xpcshell/test_objectgrips-nested-promise.js
index b60b7328c2..e0de94d24f 100644
--- a/devtools/server/tests/xpcshell/test_objectgrips-nested-promise.js
+++ b/devtools/server/tests/xpcshell/test_objectgrips-nested-promise.js
@@ -38,6 +38,8 @@ add_task(
function evalCode(debuggee) {
debuggee.eval(
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arg) {
debugger;
}.toString()
diff --git a/devtools/server/tests/xpcshell/test_objectgrips-nested-proxy.js b/devtools/server/tests/xpcshell/test_objectgrips-nested-proxy.js
index 5b0667c055..a5760f6144 100644
--- a/devtools/server/tests/xpcshell/test_objectgrips-nested-proxy.js
+++ b/devtools/server/tests/xpcshell/test_objectgrips-nested-proxy.js
@@ -37,6 +37,8 @@ add_task(
function evalCode(debuggee) {
debuggee.eval(
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arg) {
debugger;
}.toString()
diff --git a/devtools/server/tests/xpcshell/test_objectgrips-property-value-01.js b/devtools/server/tests/xpcshell/test_objectgrips-property-value-01.js
index 69da96a741..c7588578f4 100644
--- a/devtools/server/tests/xpcshell/test_objectgrips-property-value-01.js
+++ b/devtools/server/tests/xpcshell/test_objectgrips-property-value-01.js
@@ -89,6 +89,8 @@ add_task(
function evalCode(debuggee) {
debuggee.eval(
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arg1) {
debugger;
}.toString()
diff --git a/devtools/server/tests/xpcshell/test_objectgrips-property-value-02.js b/devtools/server/tests/xpcshell/test_objectgrips-property-value-02.js
index bc7337128c..6d00a6e04e 100644
--- a/devtools/server/tests/xpcshell/test_objectgrips-property-value-02.js
+++ b/devtools/server/tests/xpcshell/test_objectgrips-property-value-02.js
@@ -38,6 +38,8 @@ add_task(
function evalCode(debuggee) {
debuggee.eval(
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arg1) {
debugger;
}.toString()
diff --git a/devtools/server/tests/xpcshell/test_objectgrips-sparse-array.js b/devtools/server/tests/xpcshell/test_objectgrips-sparse-array.js
index 76a6b32f4b..3448987420 100644
--- a/devtools/server/tests/xpcshell/test_objectgrips-sparse-array.js
+++ b/devtools/server/tests/xpcshell/test_objectgrips-sparse-array.js
@@ -32,6 +32,8 @@ add_task(
function evalCode(debuggee) {
debuggee.eval(
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arr) {
debugger;
}.toString()
diff --git a/devtools/server/tests/xpcshell/test_pause_exceptions-04.js b/devtools/server/tests/xpcshell/test_pause_exceptions-04.js
index 6246b112e0..aa30002af8 100644
--- a/devtools/server/tests/xpcshell/test_pause_exceptions-04.js
+++ b/devtools/server/tests/xpcshell/test_pause_exceptions-04.js
@@ -12,7 +12,7 @@ const { waitForTick } = require("resource://devtools/shared/DevToolsUtils.js");
add_task(
threadFrontTest(
- async ({ threadFront, client, debuggee, commands }) => {
+ async ({ threadFront, debuggee, commands }) => {
let onResume = null;
let packet = null;
diff --git a/devtools/server/tests/xpcshell/test_pauselifetime-02.js b/devtools/server/tests/xpcshell/test_pauselifetime-02.js
index e936df6177..08495229e6 100644
--- a/devtools/server/tests/xpcshell/test_pauselifetime-02.js
+++ b/devtools/server/tests/xpcshell/test_pauselifetime-02.js
@@ -47,6 +47,8 @@ function evaluateTestCode(debuggee) {
debuggee.eval(
"(" +
function () {
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(obj) {
debugger;
}
diff --git a/devtools/server/tests/xpcshell/test_pauselifetime-03.js b/devtools/server/tests/xpcshell/test_pauselifetime-03.js
index 558ac8b910..f79ade2e7e 100644
--- a/devtools/server/tests/xpcshell/test_pauselifetime-03.js
+++ b/devtools/server/tests/xpcshell/test_pauselifetime-03.js
@@ -54,6 +54,8 @@ function evaluateTestCode(debuggee) {
debuggee.eval(
"(" +
function () {
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(obj) {
debugger;
}
diff --git a/devtools/server/tests/xpcshell/test_pauselifetime-04.js b/devtools/server/tests/xpcshell/test_pauselifetime-04.js
index 7d226260f0..a699a96709 100644
--- a/devtools/server/tests/xpcshell/test_pauselifetime-04.js
+++ b/devtools/server/tests/xpcshell/test_pauselifetime-04.js
@@ -30,6 +30,8 @@ function evaluateTestCode(debuggee) {
debuggee.eval(
"(" +
function () {
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(obj) {
debugger;
}
diff --git a/devtools/server/tests/xpcshell/test_promises_run_to_completion.js b/devtools/server/tests/xpcshell/test_promises_run_to_completion.js
index 4d1e8745fe..7147d77e0d 100644
--- a/devtools/server/tests/xpcshell/test_promises_run_to_completion.js
+++ b/devtools/server/tests/xpcshell/test_promises_run_to_completion.js
@@ -44,7 +44,7 @@ function test_promises_run_to_completion() {
const log = [""];
g.log = log;
- dbg.onDebuggerStatement = function handleDebuggerStatement(frame) {
+ dbg.onDebuggerStatement = function handleDebuggerStatement() {
dbg.onDebuggerStatement = undefined;
// Exercise the promise machinery: resolve a promise and perform a microtask
@@ -63,7 +63,7 @@ function test_promises_run_to_completion() {
force_microtask_checkpoint();
log[0] += ")";
- Promise.resolve(42).then(v => {
+ Promise.resolve(42).then(() => {
// The microtask running this callback should be handled as we leave the
// onDebuggerStatement Debugger callback, and should not be interleaved
// with debuggee microtasks.
diff --git a/devtools/server/tests/xpcshell/test_restartFrame-01.js b/devtools/server/tests/xpcshell/test_restartFrame-01.js
index cb13ae2d7e..51fbe75510 100644
--- a/devtools/server/tests/xpcshell/test_restartFrame-01.js
+++ b/devtools/server/tests/xpcshell/test_restartFrame-01.js
@@ -8,7 +8,7 @@
* restarted frame.
*/
-async function testFinish({ threadFront, devToolsClient }) {
+async function testFinish({ devToolsClient }) {
await close(devToolsClient);
do_test_finished();
diff --git a/devtools/server/tests/xpcshell/test_source-01.js b/devtools/server/tests/xpcshell/test_source-01.js
index 5cb7a6da52..cb57ad43e9 100644
--- a/devtools/server/tests/xpcshell/test_source-01.js
+++ b/devtools/server/tests/xpcshell/test_source-01.js
@@ -46,6 +46,8 @@ add_task(
function evaluateTestCode(debuggee) {
Cu.evalInSandbox(
"" +
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arg1) {
debugger;
},
diff --git a/devtools/server/tests/xpcshell/test_source-02.js b/devtools/server/tests/xpcshell/test_source-02.js
index 9cb88cb0e4..88e91ee6e3 100644
--- a/devtools/server/tests/xpcshell/test_source-02.js
+++ b/devtools/server/tests/xpcshell/test_source-02.js
@@ -52,6 +52,8 @@ add_task(
function evaluateTestCode(debuggee) {
Cu.evalInSandbox(
"" +
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arg1) {
debugger;
},
diff --git a/devtools/server/tests/xpcshell/test_source-03.js b/devtools/server/tests/xpcshell/test_source-03.js
index d0cd4839a0..bbee9d2949 100644
--- a/devtools/server/tests/xpcshell/test_source-03.js
+++ b/devtools/server/tests/xpcshell/test_source-03.js
@@ -51,7 +51,7 @@ add_task(
// in the first global.
let pausedOne = false;
let onResumed = null;
- threadFront.once("paused", function (packet) {
+ threadFront.once("paused", function () {
pausedOne = true;
onResumed = resume(threadFront);
});
@@ -62,7 +62,7 @@ add_task(
// Ensure that the breakpoint was properly applied to the JSScipt loaded
// in the second global.
let pausedTwo = false;
- threadFront.once("paused", function (packet) {
+ threadFront.once("paused", function () {
pausedTwo = true;
onResumed = resume(threadFront);
});
diff --git a/devtools/server/tests/xpcshell/test_source-04.js b/devtools/server/tests/xpcshell/test_source-04.js
index a3e3bef25f..fd3e8c339c 100644
--- a/devtools/server/tests/xpcshell/test_source-04.js
+++ b/devtools/server/tests/xpcshell/test_source-04.js
@@ -39,7 +39,7 @@ add_task(
// in the first global.
let pausedOne = false;
let onResumed = null;
- threadFront.once("paused", function (packet) {
+ threadFront.once("paused", function () {
pausedOne = true;
onResumed = resume(threadFront);
});
@@ -61,7 +61,7 @@ add_task(
// Ensure that the breakpoint was properly applied to the JSScipt loaded
// in the second global.
let pausedTwo = false;
- threadFront.once("paused", function (packet) {
+ threadFront.once("paused", function () {
pausedTwo = true;
onResumed = resume(threadFront);
});
diff --git a/devtools/server/tests/xpcshell/test_stepping-01.js b/devtools/server/tests/xpcshell/test_stepping-01.js
index 0c66404510..6231ed746e 100644
--- a/devtools/server/tests/xpcshell/test_stepping-01.js
+++ b/devtools/server/tests/xpcshell/test_stepping-01.js
@@ -8,7 +8,7 @@
* going to the function b's call-site.
*/
-async function testFinish({ threadFront, devToolsClient }) {
+async function testFinish({ devToolsClient }) {
await close(devToolsClient);
do_test_finished();
diff --git a/devtools/server/tests/xpcshell/test_stepping-18.js b/devtools/server/tests/xpcshell/test_stepping-18.js
index e8581835d3..4961173074 100644
--- a/devtools/server/tests/xpcshell/test_stepping-18.js
+++ b/devtools/server/tests/xpcshell/test_stepping-18.js
@@ -8,7 +8,7 @@
* going to the function b's call-site.
*/
-async function testFinish({ threadFront, devToolsClient }) {
+async function testFinish({ devToolsClient }) {
await close(devToolsClient);
do_test_finished();
diff --git a/devtools/server/tests/xpcshell/test_stepping-19.js b/devtools/server/tests/xpcshell/test_stepping-19.js
index 7ab21c7b66..c344816615 100644
--- a/devtools/server/tests/xpcshell/test_stepping-19.js
+++ b/devtools/server/tests/xpcshell/test_stepping-19.js
@@ -7,7 +7,7 @@
* Check that step out stops at the async parent's frame.
*/
-async function testFinish({ threadFront, devToolsClient }) {
+async function testFinish({ devToolsClient }) {
await close(devToolsClient);
do_test_finished();
diff --git a/devtools/server/tests/xpcshell/test_threadlifetime-01.js b/devtools/server/tests/xpcshell/test_threadlifetime-01.js
index d2e8234fb9..d8b2e3c53b 100644
--- a/devtools/server/tests/xpcshell/test_threadlifetime-01.js
+++ b/devtools/server/tests/xpcshell/test_threadlifetime-01.js
@@ -45,6 +45,8 @@ function evaluateTestCode(debuggee) {
debuggee.eval(
"(" +
function () {
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arg1) {
debugger;
debugger;
diff --git a/devtools/server/tests/xpcshell/test_threadlifetime-02.js b/devtools/server/tests/xpcshell/test_threadlifetime-02.js
index c35350a48c..310ffe6b9f 100644
--- a/devtools/server/tests/xpcshell/test_threadlifetime-02.js
+++ b/devtools/server/tests/xpcshell/test_threadlifetime-02.js
@@ -62,6 +62,8 @@ function evaluateTestCode(debuggee) {
debuggee.eval(
"(" +
function () {
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arg1) {
debugger;
debugger;
diff --git a/devtools/server/tests/xpcshell/test_threadlifetime-04.js b/devtools/server/tests/xpcshell/test_threadlifetime-04.js
index 6b815c7933..a78b70cc66 100644
--- a/devtools/server/tests/xpcshell/test_threadlifetime-04.js
+++ b/devtools/server/tests/xpcshell/test_threadlifetime-04.js
@@ -48,6 +48,8 @@ function test_thread_lifetime() {
gDebuggee.eval(
"(" +
function () {
+ // These arguments are tested.
+ // eslint-disable-next-line no-unused-vars
function stopMe(arg1) {
debugger;
}
diff --git a/devtools/server/tests/xpcshell/test_wasm_source-01.js b/devtools/server/tests/xpcshell/test_wasm_source-01.js
index fe8e43e236..4f6561d88d 100644
--- a/devtools/server/tests/xpcshell/test_wasm_source-01.js
+++ b/devtools/server/tests/xpcshell/test_wasm_source-01.js
@@ -13,7 +13,7 @@ var gThreadFront;
add_task(
threadFrontTest(
- async ({ threadFront, debuggee, client }) => {
+ async ({ threadFront, debuggee }) => {
gThreadFront = threadFront;
gDebuggee = debuggee;
@@ -90,7 +90,7 @@ const EXPECTED_CONTENT = String.fromCharCode(
);
function test_source() {
- gThreadFront.once("paused", function (packet) {
+ gThreadFront.once("paused", function () {
gThreadFront.getSources().then(function (response) {
Assert.ok(!!response);
Assert.ok(!!response.sources);
diff --git a/devtools/server/tests/xpcshell/testactors.js b/devtools/server/tests/xpcshell/testactors.js
index af208fe93e..bbcd8abe6e 100644
--- a/devtools/server/tests/xpcshell/testactors.js
+++ b/devtools/server/tests/xpcshell/testactors.js
@@ -166,7 +166,7 @@ class TestTargetActor extends protocol.Actor {
this._extraActors.threadActor = this.threadActor;
this.makeDebugger = makeDebugger.bind(null, {
findDebuggees: () => [this._global],
- shouldAddNewGlobalAsDebuggee: g => gAllowNewThreadGlobals,
+ shouldAddNewGlobalAsDebuggee: () => gAllowNewThreadGlobals,
});
this.dbg = this.makeDebugger();
this.notifyResources = this.notifyResources.bind(this);
@@ -216,12 +216,12 @@ class TestTargetActor extends protocol.Actor {
return { ...response, ...actors };
}
- detach(request) {
+ detach() {
this.threadActor.destroy();
return { type: "detached" };
}
- reload(request) {
+ reload() {
this.sourcesManager.reset();
this.threadActor.clearDebuggees();
this.threadActor.dbg.addDebuggees();
diff --git a/devtools/server/tracer/tests/browser/browser_document_tracer.js b/devtools/server/tracer/tests/browser/browser_document_tracer.js
index 694842fa8b..dcf2c9eb4d 100644
--- a/devtools/server/tracer/tests/browser/browser_document_tracer.js
+++ b/devtools/server/tracer/tests/browser/browser_document_tracer.js
@@ -52,7 +52,7 @@ add_task(async function testTracingWorker() {
const firstFrame = frames[0];
is(firstFrame.formatedDisplayName, "λ foo");
- is(firstFrame.currentDOMEvent, "DOM(click)");
+ is(firstFrame.currentDOMEvent, "DOM | click");
const lastFrame = frames.at(-1);
is(lastFrame.formatedDisplayName, "λ bar");
diff --git a/devtools/server/tracer/tests/browser/browser_worker_tracer.js b/devtools/server/tracer/tests/browser/browser_worker_tracer.js
index 815da85853..f555bd06b0 100644
--- a/devtools/server/tracer/tests/browser/browser_worker_tracer.js
+++ b/devtools/server/tracer/tests/browser/browser_worker_tracer.js
@@ -52,7 +52,7 @@ add_task(async function testTracingWorker() {
ok(lastFrame.frame);
});
-function waitForWorkerDebugger(url, dbgUrl) {
+function waitForWorkerDebugger(url) {
return new Promise(function (resolve) {
wdm.addListener({
onRegister(dbg) {
diff --git a/devtools/server/tracer/tests/xpcshell/test_tracer.js b/devtools/server/tracer/tests/xpcshell/test_tracer.js
index fe9a984aa8..0f38052ba5 100644
--- a/devtools/server/tracer/tests/xpcshell/test_tracer.js
+++ b/devtools/server/tracer/tests/xpcshell/test_tracer.js
@@ -238,3 +238,267 @@ add_task(async function testTracingFunctionReturnAndValues() {
info("Stop tracing");
stopTracing();
});
+
+add_task(async function testTracingStep() {
+ // Test the `traceStep` flag
+ const sandbox = Cu.Sandbox("https://example.com");
+ const source = `
+function foo() {
+ bar(); /* line 3 */
+ second(); /* line 4 */
+}
+function bar() {
+ let res; /* line 7 */
+ if (1 === 1) { /* line 8 */
+ res = "string"; /* line 9 */
+ } else {
+ res = "nope"
+ }
+ return res; /* line 13 */
+};
+function second() {
+ let x = 0; /* line 16 */
+ for (let i = 0; i < 2; i++) { /* line 17 */
+ x++; /* line 18 */
+ }
+ return null; /* line 20 */
+};
+foo();`;
+ Cu.evalInSandbox(source, sandbox, null, "file.js", 1);
+
+ // Pass an override method to catch all strings tentatively logged to stdout
+ const logs = [];
+ function loggingMethod(str) {
+ logs.push(str);
+ }
+
+ info("Start tracing");
+ startTracing({
+ global: sandbox,
+ traceSteps: true,
+ loggingMethod,
+ });
+
+ info("Call some code");
+ sandbox.foo();
+
+ Assert.equal(logs.length, 19);
+ Assert.equal(logs[0], "Start tracing JavaScript\n");
+ Assert.stringContains(logs[1], "λ foo");
+ Assert.stringContains(logs[1], "file.js:3:3");
+
+ // Each "step" only prints the location and nothing more
+ Assert.stringContains(logs[2], "file.js:3:3");
+
+ Assert.stringContains(logs[3], "λ bar");
+ Assert.stringContains(logs[3], "file.js:6:16");
+
+ Assert.stringContains(logs[4], "file.js:8:7");
+
+ Assert.stringContains(logs[5], "file.js:9:5");
+
+ Assert.stringContains(logs[6], "file.js:13:3");
+
+ Assert.stringContains(logs[7], "file.js:4:3");
+
+ Assert.stringContains(logs[8], "λ second");
+ Assert.stringContains(logs[8], "file.js:15:19");
+
+ Assert.stringContains(logs[9], "file.js:16:11");
+
+ // For loop
+ Assert.stringContains(logs[10], "file.js:17:16");
+
+ Assert.stringContains(logs[11], "file.js:17:19");
+
+ Assert.stringContains(logs[12], "file.js:18:5");
+
+ Assert.stringContains(logs[13], "file.js:17:26");
+
+ Assert.stringContains(logs[14], "file.js:17:19");
+
+ Assert.stringContains(logs[15], "file.js:18:5");
+
+ Assert.stringContains(logs[16], "file.js:17:26");
+
+ Assert.stringContains(logs[17], "file.js:17:19");
+ // End of for loop
+
+ Assert.stringContains(logs[18], "file.js:20:3");
+
+ info("Stop tracing");
+ stopTracing();
+});
+
+add_task(async function testTracingPauseOnStep() {
+ // Test the `pauseOnStep` flag
+ const sandbox = Cu.Sandbox("https://example.com");
+ sandbox.dump = dump;
+ const source = `var counter = 0; function incrementCounter() { let x = 0; dump("++\\n"); counter++; };`;
+ Cu.evalInSandbox(source, sandbox);
+
+ // Pass an override method to catch all strings tentatively logged to stdout
+ const logs = [];
+ let loggingMethodResolve;
+ function loggingMethod(str) {
+ logs.push(str);
+ if (loggingMethodResolve) {
+ loggingMethodResolve();
+ }
+ }
+
+ info("Start tracing without pause");
+ startTracing({
+ global: sandbox,
+ loggingMethod,
+ });
+
+ info("Call some code");
+ sandbox.incrementCounter();
+
+ Assert.equal(logs.length, 2);
+ Assert.equal(logs[0], "Start tracing JavaScript\n");
+ Assert.stringContains(logs[1], "λ incrementCounter");
+
+ info(
+ "When pauseOnStep isn't used, the traced code runs synchronously to completion"
+ );
+ Assert.equal(sandbox.counter, 1);
+
+ info("Stop tracing");
+ stopTracing();
+
+ logs.length = 0;
+ sandbox.counter = 0;
+
+ info("Start tracing with 0ms pause");
+ startTracing({
+ global: sandbox,
+ pauseOnStep: 0,
+ loggingMethod,
+ });
+
+ let onTraces = Promise.withResolvers();
+ let onResumed = Promise.withResolvers();
+ // This is used when receiving new traces in `loggingMethod()`
+ loggingMethodResolve = onTraces.resolve;
+
+ info(
+ "Run the to-be-traced code in a distinct event loop as it would be paused synchronously and would prevent further test script execution"
+ );
+ Services.tm.dispatchToMainThread(() => {
+ sandbox.incrementCounter();
+ onResumed.resolve();
+ });
+
+ info("Wait for tracer to call the listener");
+ await onTraces.promise;
+
+ Assert.equal(logs.length, 2);
+ Assert.equal(logs[0], "Start tracing JavaScript\n");
+ Assert.stringContains(logs[1], "λ incrementCounter");
+
+ info(
+ "When pauseInStep is used, the tracer listener is called, but the traced function is paused and doesn't run synchronously to completion"
+ );
+ Assert.equal(
+ sandbox.counter,
+ 0,
+ "The increment method was called but its execution flow was blocked and couldn't increment"
+ );
+
+ info("Wait for traced code to be resumed");
+ await onResumed.promise;
+ info(
+ "If we release the event loop, we can see the traced function completion"
+ );
+ Assert.equal(sandbox.counter, 1);
+
+ info("Stop tracing");
+ stopTracing();
+
+ logs.length = 0;
+ sandbox.counter = 0;
+
+ info("Start tracing with 250ms pause");
+ startTracing({
+ global: sandbox,
+ pauseOnStep: 250,
+ loggingMethod,
+ });
+
+ onTraces = Promise.withResolvers();
+ onResumed = Promise.withResolvers();
+ // This is used when receiving new traces in `loggingMethod()`
+ loggingMethodResolve = onTraces.resolve;
+
+ info(
+ "Run the to-be-traced code in a distinct event loop as it would be paused synchronously and would prevent further test script execution"
+ );
+ const startTimestamp = Cu.now();
+ Services.tm.dispatchToMainThread(() => {
+ sandbox.incrementCounter();
+ onResumed.resolve();
+ });
+
+ info("Wait for tracer to call the listener");
+ await onTraces.promise;
+
+ Assert.equal(logs.length, 2);
+ Assert.equal(logs[0], "Start tracing JavaScript\n");
+ Assert.stringContains(logs[1], "λ incrementCounter");
+
+ info(
+ "When pauseInStep is used, the tracer lsitener is called, but the traced function is paused and doesn't run synchronously to completion"
+ );
+ Assert.equal(sandbox.counter, 0);
+
+ info("Wait for traced code to be resumed");
+ await onResumed.promise;
+ info(
+ "If we release the event loop, we can see the traced function completion"
+ );
+ Assert.equal(sandbox.counter, 1);
+ info("The thread should have paused at least the pauseOnStep's duration");
+ Assert.greater(Cu.now() - startTimestamp, 250);
+
+ info("Stop tracing");
+ stopTracing();
+});
+
+add_task(async function testTracingFilterSourceUrl() {
+ // Test the `filterFrameSourceUrl` flag
+ const sandbox = Cu.Sandbox("https://example.com");
+
+ // Use a unique global (sandbox), but with two distinct scripts (first.js and second.js)
+ const source1 = `function foo() { bar(); }`;
+ Cu.evalInSandbox(source1, sandbox, null, "first.js", 1);
+
+ // Only code running in that second source should be traced.
+ const source2 = `function bar() { }`;
+ Cu.evalInSandbox(source2, sandbox, null, "second.js", 1);
+
+ // Pass an override method to catch all strings tentatively logged to stdout
+ const logs = [];
+ function loggingMethod(str) {
+ logs.push(str);
+ }
+
+ info("Start tracing");
+ startTracing({
+ global: sandbox,
+ filterFrameSourceUrl: "second",
+ loggingMethod,
+ });
+
+ info("Call some code");
+ sandbox.foo();
+
+ Assert.equal(logs.length, 2);
+ Assert.equal(logs[0], "Start tracing JavaScript\n");
+ Assert.stringContains(logs[1], "λ bar");
+ Assert.stringContains(logs[1], "second.js:1:18");
+
+ info("Stop tracing");
+ stopTracing();
+});
diff --git a/devtools/server/tracer/tracer.jsm b/devtools/server/tracer/tracer.jsm
index 82c746bb57..955b25fe3a 100644
--- a/devtools/server/tracer/tracer.jsm
+++ b/devtools/server/tracer/tracer.jsm
@@ -25,6 +25,7 @@ const EXPORTED_SYMBOLS = [
"addTracingListener",
"removeTracingListener",
"NEXT_INTERACTION_MESSAGE",
+ "DOM_MUTATIONS",
];
const NEXT_INTERACTION_MESSAGE =
@@ -43,8 +44,23 @@ const FRAME_EXIT_REASONS = {
THROW: "throw",
};
+const DOM_MUTATIONS = {
+ // Track all DOM Node being added
+ ADD: "add",
+ // Track all attributes being modified
+ ATTRIBUTES: "attributes",
+ // Track all DOM Node being removed
+ REMOVE: "remove",
+};
+
const listeners = new Set();
+// Detecting worker is different if this file is loaded via Common JS loader (isWorker global)
+// or as a JSM (constructor name)
+const isWorker =
+ globalThis.isWorker ||
+ globalThis.constructor.name == "WorkerDebuggerGlobalScope";
+
// This module can be loaded from the worker thread, where we can't use ChromeUtils.
// So implement custom lazy getters (without XPCOMUtils ESM) from here.
// Worker codepath in DevTools will pass a custom Debugger instance.
@@ -60,7 +76,7 @@ const customLazy = {
// (ex: from tracer actor module),
// this module no longer has WorkerDebuggerGlobalScope as global,
// but has to use require() to pull Debugger.
- if (typeof isWorker == "boolean") {
+ if (isWorker) {
return require("Debugger");
}
const { addDebuggerToGlobal } = ChromeUtils.importESModule(
@@ -113,25 +129,44 @@ const customLazy = {
* @param {Boolean} options.traceDOMEvents
* Optional setting to enable tracing all the DOM events being going through
* dom/events/EventListenerManager.cpp's `EventListenerManager`.
+ * @param {Array<string>} options.traceDOMMutations
+ * Optional setting to enable tracing all the DOM mutations.
+ * This array may contains three strings:
+ * - "add": trace all new DOM Node being added,
+ * - "attributes": trace all DOM attribute modifications,
+ * - "delete": trace all DOM Node being removed.
* @param {Boolean} options.traceValues
* Optional setting to enable tracing all function call values as well,
* as returned values (when we do log returned frames).
* @param {Boolean} options.traceOnNextInteraction
* Optional setting to enable when the tracing should only start when the
* use starts interacting with the page. i.e. on next keydown or mousedown.
+ * @param {Boolean} options.traceSteps
+ * Optional setting to enable tracing each frame within a function execution.
+ * (i.e. not only function call and function returns [when traceFunctionReturn is true])
* @param {Boolean} options.traceFunctionReturn
* Optional setting to enable when the tracing should notify about frame exit.
* i.e. when a function call returns or throws.
+ * @param {String} options.filterFrameSourceUrl
+ * Optional setting to restrict all traces to only a given source URL.
+ * This is a loose check, so any source whose URL includes the passed string will be traced.
* @param {Number} options.maxDepth
* Optional setting to ignore frames when depth is greater than the passed number.
* @param {Number} options.maxRecords
* Optional setting to stop the tracer after having recorded at least
* the passed number of top level frames.
+ * @param {Number} options.pauseOnStep
+ * Optional setting to delay each frame execution for a given amount of time in ms.
*/
class JavaScriptTracer {
constructor(options) {
this.onEnterFrame = this.onEnterFrame.bind(this);
+ // DevTools CommonJS Workers modules don't have access to AbortController
+ if (!isWorker) {
+ this.abortController = new AbortController();
+ }
+
// By default, we would trace only JavaScript related to caller's global.
// As there is no way to compute the caller's global default to the global of the
// mandatory options argument.
@@ -152,56 +187,48 @@ class JavaScriptTracer {
if (!this.loggingMethod) {
// On workers, `dump` can't be called with JavaScript on another object,
// so bind it.
- // Detecting worker is different if this file is loaded via Common JS loader (isWorker)
- // or as a JSM (constructor name)
- this.loggingMethod =
- typeof isWorker == "boolean" ||
- globalThis.constructor.name == "WorkerDebuggerGlobalScope"
- ? dump.bind(null)
- : dump;
+ this.loggingMethod = isWorker ? dump.bind(null) : dump;
}
this.traceDOMEvents = !!options.traceDOMEvents;
+
+ if (options.traceDOMMutations) {
+ if (!Array.isArray(options.traceDOMMutations)) {
+ throw new Error("'traceDOMMutations' attribute should be an array");
+ }
+ const acceptedValues = Object.values(DOM_MUTATIONS);
+ if (!options.traceDOMMutations.every(e => acceptedValues.includes(e))) {
+ throw new Error(
+ `'traceDOMMutations' only accept array of strings whose values can be: ${acceptedValues}`
+ );
+ }
+ this.traceDOMMutations = options.traceDOMMutations;
+ }
+ this.traceSteps = !!options.traceSteps;
this.traceValues = !!options.traceValues;
this.traceFunctionReturn = !!options.traceFunctionReturn;
this.maxDepth = options.maxDepth;
this.maxRecords = options.maxRecords;
this.records = 0;
+ if ("pauseOnStep" in options) {
+ if (typeof options.pauseOnStep != "number") {
+ throw new Error("'pauseOnStep' attribute should be a number");
+ }
+ this.pauseOnStep = options.pauseOnStep;
+ }
+ if ("filterFrameSourceUrl" in options) {
+ if (typeof options.filterFrameSourceUrl != "string") {
+ throw new Error("'filterFrameSourceUrl' attribute should be a string");
+ }
+ this.filterFrameSourceUrl = options.filterFrameSourceUrl;
+ }
// An increment used to identify function calls and their returned/exit frames
this.frameId = 0;
// This feature isn't supported on Workers as they aren't involving user events
- if (options.traceOnNextInteraction && typeof isWorker !== "boolean") {
- this.abortController = new AbortController();
- const listener = () => {
- this.abortController.abort();
- // Avoid tracing if the users asked to stop tracing.
- if (this.dbg) {
- this.#startTracing();
- }
- };
- const eventOptions = {
- signal: this.abortController.signal,
- capture: true,
- };
- // Register the event listener on the Chrome Event Handler in order to receive the event first.
- // When used for the parent process target, `tracedGlobal` is browser.xhtml's window, which doesn't have a chromeEventHandler.
- const eventHandler =
- this.tracedGlobal.docShell.chromeEventHandler || this.tracedGlobal;
- eventHandler.addEventListener("mousedown", listener, eventOptions);
- eventHandler.addEventListener("keydown", listener, eventOptions);
-
- // Significate to the user that the tracer is registered, but not tracing just yet.
- let shouldLogToStdout = listeners.size == 0;
- for (const l of listeners) {
- if (typeof l.onTracingPending == "function") {
- shouldLogToStdout |= l.onTracingPending();
- }
- }
- if (shouldLogToStdout) {
- this.loggingMethod(this.prefix + NEXT_INTERACTION_MESSAGE + "\n");
- }
+ if (options.traceOnNextInteraction && !isWorker) {
+ this.#waitForNextInteraction();
} else {
this.#startTracing();
}
@@ -212,6 +239,44 @@ class JavaScriptTracer {
isTracing = false;
/**
+ * In case `traceOnNextInteraction` option is used, delay the actual start of tracing until a first user interaction.
+ */
+ #waitForNextInteraction() {
+ // Use a dedicated Abort Controller as we are going to stop it as soon as we get the first user interaction,
+ // whereas other listeners would typically wait for tracer stop.
+ this.nextInteractionAbortController = new AbortController();
+
+ const listener = () => {
+ this.nextInteractionAbortController.abort();
+ // Avoid tracing if the users asked to stop tracing while we were waiting for the user interaction.
+ if (this.dbg) {
+ this.#startTracing();
+ }
+ };
+ const eventOptions = {
+ signal: this.nextInteractionAbortController.signal,
+ capture: true,
+ };
+ // Register the event listener on the Chrome Event Handler in order to receive the event first.
+ // When used for the parent process target, `tracedGlobal` is browser.xhtml's window, which doesn't have a chromeEventHandler.
+ const eventHandler =
+ this.tracedGlobal.docShell.chromeEventHandler || this.tracedGlobal;
+ eventHandler.addEventListener("mousedown", listener, eventOptions);
+ eventHandler.addEventListener("keydown", listener, eventOptions);
+
+ // Significate to the user that the tracer is registered, but not tracing just yet.
+ let shouldLogToStdout = listeners.size == 0;
+ for (const l of listeners) {
+ if (typeof l.onTracingPending == "function") {
+ shouldLogToStdout |= l.onTracingPending();
+ }
+ }
+ if (shouldLogToStdout) {
+ this.loggingMethod(this.prefix + NEXT_INTERACTION_MESSAGE + "\n");
+ }
+ }
+
+ /**
* Actually really start watching for executions.
*
* This may be delayed when traceOnNextInteraction options is used.
@@ -225,6 +290,10 @@ class JavaScriptTracer {
if (this.traceDOMEvents) {
this.startTracingDOMEvents();
}
+ // This feature isn't supported on Workers as they aren't interacting with the DOM Tree
+ if (this.traceDOMMutations?.length > 0 && !isWorker) {
+ this.startTracingDOMMutations();
+ }
// In any case, we consider the tracing as started
this.notifyToggle(true);
@@ -248,6 +317,97 @@ class JavaScriptTracer {
this.currentDOMEvent = null;
}
+ startTracingDOMMutations() {
+ this.tracedGlobal.document.devToolsWatchingDOMMutations = true;
+
+ const eventOptions = {
+ signal: this.abortController.signal,
+ capture: true,
+ };
+ if (this.traceDOMMutations.includes(DOM_MUTATIONS.ADD)) {
+ this.tracedGlobal.docShell.chromeEventHandler.addEventListener(
+ "devtoolschildinserted",
+ this.#onDOMMutation,
+ eventOptions
+ );
+ }
+ if (this.traceDOMMutations.includes(DOM_MUTATIONS.ATTRIBUTES)) {
+ this.tracedGlobal.docShell.chromeEventHandler.addEventListener(
+ "devtoolsattrmodified",
+ this.#onDOMMutation,
+ eventOptions
+ );
+ }
+ if (this.traceDOMMutations.includes(DOM_MUTATIONS.REMOVE)) {
+ this.tracedGlobal.docShell.chromeEventHandler.addEventListener(
+ "devtoolschildremoved",
+ this.#onDOMMutation,
+ eventOptions
+ );
+ }
+ }
+
+ stopTracingDOMMutations() {
+ this.tracedGlobal.document.devToolsWatchingDOMMutations = false;
+ // Note that the event listeners are all going to be unregistered via the AbortController.
+ }
+
+ /**
+ * Called for any DOM Mutation done in the traced document.
+ *
+ * @param {DOM Event} event
+ */
+ #onDOMMutation = event => {
+ // Ignore elements inserted by DevTools, like the inspector's highlighters
+ if (event.target.isNativeAnonymous) {
+ return;
+ }
+
+ let type = "";
+ switch (event.type) {
+ case "devtoolschildinserted":
+ type = DOM_MUTATIONS.ADD;
+ break;
+ case "devtoolsattrmodified":
+ type = DOM_MUTATIONS.ATTRIBUTES;
+ break;
+ case "devtoolschildremoved":
+ type = DOM_MUTATIONS.REMOVE;
+ break;
+ default:
+ throw new Error("Unexpected DOM Mutation event type: " + event.type);
+ }
+
+ let shouldLogToStdout = true;
+ if (listeners.size > 0) {
+ shouldLogToStdout = false;
+ for (const listener of listeners) {
+ // If any listener return true, also log to stdout
+ if (typeof listener.onTracingDOMMutation == "function") {
+ shouldLogToStdout |= listener.onTracingDOMMutation({
+ depth: this.depth,
+ prefix: this.prefix,
+
+ type,
+ element: event.target,
+ caller: Components.stack.caller,
+ });
+ }
+ }
+ }
+
+ if (shouldLogToStdout) {
+ const padding = "—".repeat(this.depth + 1);
+ this.loggingMethod(
+ this.prefix +
+ padding +
+ `[DOM Mutation | ${type}] ` +
+ objectToString(event.target) +
+ "\n"
+ );
+ }
+ };
+
/**
* Called by DebuggerNotificationObserver interface when a DOM event start being notified
* and after it has been notified.
@@ -277,7 +437,7 @@ class JavaScriptTracer {
.makeDebuggeeValue(notification.event)
.getProperty("type").return;
}
- this.currentDOMEvent = `DOM(${type})`;
+ this.currentDOMEvent = `DOM | ${type}`;
} else {
this.currentDOMEvent = notification.type;
}
@@ -306,14 +466,22 @@ class JavaScriptTracer {
this.depth = 0;
// Cancel the traceOnNextInteraction event listeners.
- if (this.abortController) {
- this.abortController.abort();
- this.abortController = null;
+ if (this.nextInteractionAbortController) {
+ this.nextInteractionAbortController.abort();
+ this.nextInteractionAbortController = null;
}
if (this.traceDOMEvents) {
this.stopTracingDOMEvents();
}
+ if (this.traceDOMMutations?.length > 0 && !isWorker) {
+ this.stopTracingDOMMutations();
+ }
+
+ // Unregister all event listeners
+ if (this.abortController) {
+ this.abortController.abort();
+ }
this.tracedGlobal = null;
this.isTracing = false;
@@ -406,10 +574,21 @@ class JavaScriptTracer {
return;
}
try {
+ // If an optional filter is passed, ignore frames which aren't matching the filter string
+ if (
+ this.filterFrameSourceUrl &&
+ !frame.script.source.url?.includes(this.filterFrameSourceUrl)
+ ) {
+ return;
+ }
+
// Because of async frame which are popped and entered again on completion of the awaited async task,
// we have to compute the depth from the frame. (and can't use a simple increment on enter/decrement on pop).
const depth = getFrameDepth(frame);
+ // Save the current depth for the DOM Mutation handler
+ this.depth = depth;
+
// Ignore the frame if we reached the depth limit (if one is provided)
if (this.maxDepth && depth >= this.maxDepth) {
return;
@@ -467,6 +646,39 @@ class JavaScriptTracer {
this.logFrameEnteredToStdout(frame, depth);
}
+ if (this.traceSteps) {
+ frame.onStep = () => {
+ // Spidermonkey steps on many intermediate positions which don't make sense to the user.
+ // `isStepStart` is close to each statement start, which is meaningful to the user.
+ const { isStepStart } = frame.script.getOffsetMetadata(frame.offset);
+ if (!isStepStart) {
+ return;
+ }
+
+ shouldLogToStdout = true;
+ if (listeners.size > 0) {
+ shouldLogToStdout = false;
+ for (const listener of listeners) {
+ // If any listener return true, also log to stdout
+ if (typeof listener.onTracingFrameStep == "function") {
+ shouldLogToStdout |= listener.onTracingFrameStep({
+ frame,
+ depth,
+ prefix: this.prefix,
+ });
+ }
+ }
+ }
+ if (shouldLogToStdout) {
+ this.logFrameStepToStdout(frame, depth);
+ }
+ // Optionaly pause the frame execution by letting the other event loop to run in between.
+ if (typeof this.pauseOnStep == "number") {
+ syncPause(this.pauseOnStep);
+ }
+ };
+ }
+
frame.onPop = completion => {
// Special case async frames. We are exiting the current frame because of waiting for an async task.
// (this is typically a `await foo()` from an async function)
@@ -520,6 +732,11 @@ class JavaScriptTracer {
this.logFrameExitedToStdout(frame, depth, why, rv);
}
};
+
+ // Optionaly pause the frame execution by letting the other event loop to run in between.
+ if (typeof this.pauseOnStep == "number") {
+ syncPause(this.pauseOnStep);
+ }
} catch (e) {
console.error("Exception while tracing javascript", e);
}
@@ -576,6 +793,20 @@ class JavaScriptTracer {
}
/**
+ * Display to stdout one given frame execution, which represents a step within a function execution.
+ *
+ * @param {Debugger.Frame} frame
+ * @param {Number} depth
+ */
+ logFrameStepToStdout(frame, depth) {
+ const padding = "—".repeat(depth + 1);
+
+ const message = `${padding}— ${getTerminalHyperLink(frame)}`;
+
+ this.loggingMethod(this.prefix + message + "\n");
+ }
+
+ /**
* Display to stdout the exit of a given frame execution, which represents a function return.
*
* @param {Debugger.Frame} frame
@@ -787,6 +1018,27 @@ function getTerminalHyperLink(frame) {
return `\x1B]8;;${href}\x1B\\${href}\x1B]8;;\x1B\\`;
}
+/**
+ * Helper function to synchronously pause the current frame execution
+ * for a given duration in ms.
+ *
+ * @param {Number} duration
+ */
+function syncPause(duration) {
+ let freeze = true;
+ const timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ timer.initWithCallback(
+ () => {
+ freeze = false;
+ },
+ duration,
+ Ci.nsITimer.TYPE_ONE_SHOT
+ );
+ Services.tm.spinEventLoopUntil("debugger-slow-motion", function () {
+ return !freeze;
+ });
+}
+
// This JSM may be execute as CommonJS when loaded in the worker thread
if (typeof module == "object") {
module.exports = {