diff --git a/devtools/client/framework/browser-toolbox/test/browser_browser_toolbox_fission_inspector.js b/devtools/client/framework/browser-toolbox/test/browser_browser_toolbox_fission_inspector.js
index d641de027473..de29d62e4534 100644
--- a/devtools/client/framework/browser-toolbox/test/browser_browser_toolbox_fission_inspector.js
+++ b/devtools/client/framework/browser-toolbox/test/browser_browser_toolbox_fission_inspector.js
@@ -18,16 +18,6 @@ add_task(async function() {
// Forces the Browser Toolbox to open on the inspector by default
await pushPref("devtools.browsertoolbox.panel", "inspector");
- // Open the tab *before* opening the Browser Toolbox in order to already have the document
- // loaded before it starts iterating over additional frame targets.
- // Bug 1593937 should make it optional and be able to care about dynamically added targets.
- const tab = await addTab(
- `data:text/html,
Foo
Foo
`
- );
-
- // Set a custom attribute on the tab's browser, in order to easily select it in the markup view
- tab.linkedBrowser.setAttribute("test-tab", "true");
-
const ToolboxTask = await initBrowserToolboxTask({
enableBrowserToolboxFission: true,
});
@@ -35,6 +25,14 @@ add_task(async function() {
selectNodeFront,
});
+ // Open the tab *after* opening the Browser Toolbox in order to force creating the remote frames
+ // late and exercise frame target watching code.
+ const tab = await addTab(
+ `data:text/html,Foo
Foo
`
+ );
+ // Set a custom attribute on the tab's browser, in order to easily select it in the markup view
+ tab.linkedBrowser.setAttribute("test-tab", "true");
+
const color = await ToolboxTask.spawn(null, async () => {
/* global gToolbox */
const inspector = gToolbox.getPanel("inspector");
diff --git a/devtools/client/fronts/descriptors/frame.js b/devtools/client/fronts/descriptors/frame.js
index bde01a9668bb..eca425118c4b 100644
--- a/devtools/client/fronts/descriptors/frame.js
+++ b/devtools/client/fronts/descriptors/frame.js
@@ -93,6 +93,15 @@ class FrameDescriptorFront extends FrontClassWithSpec(frameDescriptorSpec) {
return parentDescriptor.getTarget();
}
+ getCachedWatcher() {
+ for (const child of this.poolChildren()) {
+ if (child.typeName == "watcher") {
+ return child;
+ }
+ }
+ return null;
+ }
+
destroy() {
this._frameTargetFront = null;
this._targetFrontPromise = null;
diff --git a/devtools/client/fronts/descriptors/process.js b/devtools/client/fronts/descriptors/process.js
index 19f888dd468f..58bcda1edfc0 100644
--- a/devtools/client/fronts/descriptors/process.js
+++ b/devtools/client/fronts/descriptors/process.js
@@ -29,6 +29,7 @@ class ProcessDescriptorFront extends FrontClassWithSpec(processDescriptorSpec) {
form(json) {
this.id = json.id;
this.isParent = json.isParent;
+ this.traits = json.traits || {};
}
async _createProcessTargetFront(form) {
@@ -92,6 +93,15 @@ class ProcessDescriptorFront extends FrontClassWithSpec(processDescriptorSpec) {
return this._targetFrontPromise;
}
+ getCachedWatcher() {
+ for (const child of this.poolChildren()) {
+ if (child.typeName == "watcher") {
+ return child;
+ }
+ }
+ return null;
+ }
+
destroy() {
if (this._processTargetFront) {
this._processTargetFront.destroy();
diff --git a/devtools/client/fronts/descriptors/tab.js b/devtools/client/fronts/descriptors/tab.js
index f2f02f053242..42991ce82c14 100644
--- a/devtools/client/fronts/descriptors/tab.js
+++ b/devtools/client/fronts/descriptors/tab.js
@@ -143,6 +143,15 @@ class TabDescriptorFront extends FrontClassWithSpec(tabDescriptorSpec) {
})();
return this._targetFrontPromise;
}
+
+ getCachedWatcher() {
+ for (const child of this.poolChildren()) {
+ if (child.typeName == "watcher") {
+ return child;
+ }
+ }
+ return null;
+ }
}
exports.TabDescriptorFront = TabDescriptorFront;
diff --git a/devtools/client/fronts/moz.build b/devtools/client/fronts/moz.build
index 0fcdd630550a..879c125d706a 100644
--- a/devtools/client/fronts/moz.build
+++ b/devtools/client/fronts/moz.build
@@ -47,6 +47,7 @@ DevToolsModules(
'symbol-iterator.js',
'thread.js',
'walker.js',
+ 'watcher.js',
'webconsole.js',
'websocket.js'
)
diff --git a/devtools/client/fronts/watcher.js b/devtools/client/fronts/watcher.js
new file mode 100644
index 000000000000..6d08213f9282
--- /dev/null
+++ b/devtools/client/fronts/watcher.js
@@ -0,0 +1,49 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const { watcherSpec } = require("devtools/shared/specs/watcher");
+const {
+ FrontClassWithSpec,
+ registerFront,
+} = require("devtools/shared/protocol");
+
+loader.lazyRequireGetter(
+ this,
+ "BrowsingContextTargetFront",
+ "devtools/client/fronts/targets/browsing-context",
+ true
+);
+
+class WatcherFront extends FrontClassWithSpec(watcherSpec) {
+ constructor(client, targetFront, parentFront) {
+ super(client, targetFront, parentFront);
+
+ this._onTargetAvailable = this._onTargetAvailable.bind(this);
+ this._onTargetDestroyed = this._onTargetDestroyed.bind(this);
+
+ // Convert form, which is just JSON object to Fronts for these two events
+ this.on("target-available-form", this._onTargetAvailable);
+ this.on("target-destroyed-form", this._onTargetDestroyed);
+ }
+
+ form(json) {
+ this.actorID = json.actor;
+ this.traits = json.traits;
+ }
+
+ _onTargetAvailable(form) {
+ const front = new BrowsingContextTargetFront(this.conn, null, this);
+ front.actorID = form.actor;
+ front.form(form);
+ this.manage(front);
+ this.emit("target-available", front);
+ }
+
+ _onTargetDestroyed(form) {
+ const front = this.actor(form.actor);
+ this.emit("target-destroyed", front);
+ }
+}
+registerFront(WatcherFront);
diff --git a/devtools/server/actors/descriptors/frame.js b/devtools/server/actors/descriptors/frame.js
index 9bcdbfa2a640..873ca70f0b78 100644
--- a/devtools/server/actors/descriptors/frame.js
+++ b/devtools/server/actors/descriptors/frame.js
@@ -25,7 +25,7 @@ loader.lazyRequireGetter(
const FrameDescriptorActor = ActorClassWithSpec(frameDescriptorSpec, {
initialize(connection, browsingContext) {
if (typeof browsingContext.id != "number") {
- throw Error("Frame Descriptor Connect requires a valid browsingContext.");
+ throw Error("Frame Descriptor requires a valid BrowsingContext.");
}
Actor.prototype.initialize.call(this, connection);
this.destroy = this.destroy.bind(this);
diff --git a/devtools/server/actors/descriptors/moz.build b/devtools/server/actors/descriptors/moz.build
index 83ac853a6135..4dc26c0ac636 100644
--- a/devtools/server/actors/descriptors/moz.build
+++ b/devtools/server/actors/descriptors/moz.build
@@ -4,6 +4,10 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+DIRS += [
+ 'watcher',
+]
+
DevToolsModules(
'frame.js',
'process.js',
diff --git a/devtools/server/actors/descriptors/process.js b/devtools/server/actors/descriptors/process.js
index b031d61ad916..0565d0498328 100644
--- a/devtools/server/actors/descriptors/process.js
+++ b/devtools/server/actors/descriptors/process.js
@@ -31,6 +31,12 @@ loader.lazyRequireGetter(
"devtools/server/connectors/content-process-connector",
true
);
+loader.lazyRequireGetter(
+ this,
+ "WatcherActor",
+ "devtools/server/actors/descriptors/watcher/watcher",
+ true
+);
const ProcessDescriptorActor = ActorClassWithSpec(processDescriptorSpec, {
initialize(connection, options = {}) {
@@ -129,11 +135,28 @@ const ProcessDescriptorActor = ActorClassWithSpec(processDescriptorSpec, {
return this._childProcessConnect();
},
+ /**
+ * Return a Watcher actor, allowing to keep track of targets which
+ * already exists or will be created. It also helps knowing when they
+ * are destroyed.
+ */
+ getWatcher() {
+ if (!this.watcher) {
+ this.watcher = new WatcherActor(this.conn);
+ this.manage(this.watcher);
+ }
+ return this.watcher;
+ },
+
form() {
return {
actor: this.actorID,
id: this.id,
isParent: this.isParent,
+ traits: {
+ // FF77+ supports the Watcher actor
+ watcher: true,
+ },
};
},
diff --git a/devtools/server/actors/descriptors/tab.js b/devtools/server/actors/descriptors/tab.js
index a6d40d3f424d..53319f9510a1 100644
--- a/devtools/server/actors/descriptors/tab.js
+++ b/devtools/server/actors/descriptors/tab.js
@@ -25,6 +25,13 @@ const { ActorClassWithSpec, Actor } = require("devtools/shared/protocol");
const { tabDescriptorSpec } = require("devtools/shared/specs/descriptors/tab");
const { AppConstants } = require("resource://gre/modules/AppConstants.jsm");
+loader.lazyRequireGetter(
+ this,
+ "WatcherActor",
+ "devtools/server/actors/descriptors/watcher/watcher",
+ true
+);
+
/**
* Creates a target actor proxy for handling requests to a single browser frame.
* Both and