Bug 1699200 - [devtools] Create all commands via a CommandsFactory module. r=nchevobbe,jdescottes

Differential Revision: https://phabricator.services.mozilla.com/D109494
This commit is contained in:
Alexandre Poirot 2021-03-25 16:02:32 +00:00
parent 6c8d0cbf17
commit 743c2a228e
38 changed files with 478 additions and 405 deletions

View File

@ -26,15 +26,6 @@ add_task(async function() {
);
ok(tabDescriptor, "Should have a descriptor actor for the tab");
const firstCommands = await tabDescriptor.getCommands();
ok(firstCommands, "Got commands");
const secondCommands = await tabDescriptor.getCommands();
is(
firstCommands,
secondCommands,
"Multiple calls to getCommands return the same commands object"
);
is(
target.descriptorFront,
tabDescriptor,

View File

@ -39,6 +39,7 @@ var Startup = Cc["@mozilla.org/devtools/startup-clh;1"].getService(
const {
ResourceWatcher,
} = require("devtools/shared/resources/resource-watcher");
const { createCommandsDictionary } = require("devtools/shared/commands/index");
const { BrowserLoader } = ChromeUtils.import(
"resource://devtools/client/shared/browser-loader.js"
@ -778,7 +779,8 @@ Toolbox.prototype = {
// It exposes commands modules listed in devtools/shared/commands/index.js
// which are an abstraction on top of RDP methods.
// See devtools/shared/commands/README.md
this.commands = await this.descriptorFront.getCommands();
// Bug 1700909 will make the commands be instantiated by gDevTools instead of the Toolbox.
this.commands = await createCommandsDictionary(this.descriptorFront);
//TODO: complete the renaming of targetList everywhere
// But for now, still expose this name on Toolbox

View File

@ -4,8 +4,6 @@
"use strict";
const { createCommandsDictionary } = require("devtools/shared/commands/index");
/**
* A Descriptor represents a debuggable context. It can be a browser tab, a tab on
* a remote device, like a tab on Firefox for Android. But it can also be an add-on,
@ -33,13 +31,6 @@ function DescriptorMixin(parentClass) {
get client() {
return this._client;
}
async getCommands() {
if (!this._commands) {
this._commands = createCommandsDictionary(this);
}
return this._commands;
}
}
return Descriptor;
}

View File

@ -626,7 +626,7 @@ function addDeviceForTest(device) {
async function waitForClientClose(ui) {
info("Waiting for RDM devtools client to close");
await ui.client.once("closed");
await ui.commands.client.once("closed");
info("RDM's devtools client is now closed");
}

View File

@ -14,19 +14,10 @@ const Constants = require("devtools/client/responsive/constants");
const {
ResourceWatcher,
} = require("devtools/shared/resources/resource-watcher");
const {
CommandsFactory,
} = require("devtools/shared/commands/commands-factory");
loader.lazyRequireGetter(
this,
"DevToolsClient",
"devtools/client/devtools-client",
true
);
loader.lazyRequireGetter(
this,
"DevToolsServer",
"devtools/server/devtools-server",
true
);
loader.lazyRequireGetter(
this,
"throttlingProfiles",
@ -364,34 +355,22 @@ class ResponsiveUI {
this.resizeHandleY = null;
this.resizeToolbarObserver = null;
// Close the devtools client used to speak with responsive emulation actor.
// Destroying the commands will close the devtools client used to speak with responsive emulation actor.
// The actor handles clearing any overrides itself, so it's not necessary to clear
// anything on shutdown client side.
const clientClosed = this.client.close();
const commandsDestroyed = this.commands.destroy();
if (!isTabContentDestroying) {
await clientClosed;
await commandsDestroyed;
}
this.client = this.responsiveFront = null;
this.commands = this.responsiveFront = null;
this.destroyed = true;
return true;
}
async connectToServer() {
// The client being instantiated here is separate from the toolbox. It is being used
// separately and has a life cycle that doesn't correspond to the toolbox.
DevToolsServer.init();
DevToolsServer.registerAllActors();
this.client = new DevToolsClient(DevToolsServer.connectPipe());
await this.client.connect();
// Pass a proper `tab` filter option to getTab in order to create a
// "local" TabDescriptor, which handles target switching autonomously with
// its corresponding target-list.
const descriptor = await this.client.mainRoot.getTab({ tab: this.tab });
const commands = await descriptor.getCommands();
this.targetList = commands.targetCommand;
this.commands = await CommandsFactory.forTab(this.tab);
this.targetList = this.commands.targetCommand;
this.resourceWatcher = new ResourceWatcher(this.targetList);
await this.targetList.startListening();

View File

@ -51,6 +51,9 @@ const { gDevTools } = require("devtools/client/framework/devtools");
const {
TabDescriptorFactory,
} = require("devtools/client/framework/tab-descriptor-factory");
const {
CommandsFactory,
} = require("devtools/shared/commands/commands-factory");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
// This is overridden in files that load shared-head via loadSubScript.
@ -534,8 +537,13 @@ async function navigateTo(uri, { isErrorPage = false } = {}) {
async function createAndAttachTargetForTab(tab) {
info("Creating and attaching to a local tab target");
const descriptor = await TabDescriptorFactory.createDescriptorForTab(tab);
const target = await descriptor.getTarget();
const commands = await CommandsFactory.forTab(tab);
// Initialize the TargetCommands which require some async stuff to be done
// before being fully ready. This will define the `targetCommand.targetFront` attribute.
await commands.targetCommand.startListening();
const target = commands.targetCommand.targetFront;
await target.attach();
return target;
}

View File

@ -5,18 +5,11 @@
"use strict";
var Services = require("Services");
const ChromeUtils = require("ChromeUtils");
const { DevToolsLoader } = ChromeUtils.import(
"resource://devtools/shared/Loader.jsm"
);
const {
CommandsFactory,
} = require("devtools/shared/commands/commands-factory");
loader.lazyRequireGetter(this, "Tools", "devtools/client/definitions", true);
loader.lazyRequireGetter(
this,
"DevToolsClient",
"devtools/client/devtools-client",
true
);
loader.lazyRequireGetter(this, "l10n", "devtools/client/webconsole/utils/l10n");
loader.lazyRequireGetter(
this,
@ -38,7 +31,6 @@ class BrowserConsoleManager {
this._browserConsole = null;
this._browserConsoleInitializing = null;
this._browerConsoleSessionState = false;
this._devToolsClient = null;
}
storeBrowserConsoleSessionState() {
@ -50,17 +42,15 @@ class BrowserConsoleManager {
}
/**
* Open a Browser Console for the given commands context.
* Open a Browser Console for the current commands context.
*
* @param object commands
* The commands object with all interfaces defined from devtools/shared/commands/
* @param nsIDOMWindow iframeWindow
* The window where the browser console UI is already loaded.
* @return object
* A promise object for the opening of the new BrowserConsole instance.
*/
async openBrowserConsole(commands, win) {
const hud = new BrowserConsole(commands, win, win);
async openBrowserConsole(win) {
const hud = new BrowserConsole(this.commands, win, win);
this._browserConsole = hud;
await hud.init();
return hud;
@ -77,8 +67,8 @@ class BrowserConsoleManager {
await this._browserConsole.destroy();
this._browserConsole = null;
await this._devToolsClient.close();
this._devToolsClient = null;
await this.commands.destroy();
this.commands = null;
}
/**
@ -96,9 +86,9 @@ class BrowserConsoleManager {
// Temporarily cache the async startup sequence so that if toggleBrowserConsole
// gets called again we can return this console instead of opening another one.
this._browserConsoleInitializing = (async () => {
const commands = await this.connect();
this.commands = await CommandsFactory.forBrowserConsole();
const win = await this.openWindow();
const browserConsole = await this.openBrowserConsole(commands, win);
const browserConsole = await this.openBrowserConsole(win);
return browserConsole;
})();
@ -107,51 +97,6 @@ class BrowserConsoleManager {
return browserConsole;
}
/**
* One method to handle the whole setup sequence to connect to RDP backend.
*
* This will instantiate a special DevTools module loader for the DevToolsServer.
* Then spawn a DevToolsClient to connect to it.
* Get a Main Process Descriptor from it.
* Finally spawn a commands object for this descriptor.
*/
async connect() {
// The Browser console ends up using the debugger in autocomplete.
// Because the debugger can't be running in the same compartment than its debuggee,
// we have to load the server in a dedicated Loader, flagged with
// `freshCompartment`, which will force it to be loaded in another compartment.
// We aren't using `invisibleToDebugger` in order to allow the Browser toolbox to
// debug the Browser console. This is fine as they will spawn distinct Loaders and
// so distinct `DevToolsServer` and actor modules.
const loader = new DevToolsLoader({
freshCompartment: true,
});
const { DevToolsServer } = loader.require(
"devtools/server/devtools-server"
);
DevToolsServer.init();
// We want all the actors (root, browser and target-scoped) to be registered on the
// DevToolsServer. This is needed so the Browser Console can retrieve:
// - the console actors, which are target-scoped (See Bug 1416105)
// - the screenshotActor, which is browser-scoped (for the `:screenshot` command)
DevToolsServer.registerAllActors();
DevToolsServer.allowChromeProcess = true;
this._devToolsClient = new DevToolsClient(DevToolsServer.connectPipe());
await this._devToolsClient.connect();
const descriptor = await this._devToolsClient.mainRoot.getMainProcess();
// Hack something in order to help TargetMixinFront to distinguish the BrowserConsole
descriptor.createdForBrowserConsole = true;
const commands = await descriptor.getCommands();
return commands;
}
async openWindow() {
const win = Services.ww.openWindow(
null,

View File

@ -5,7 +5,8 @@
const {
STUBS_UPDATE_ENV,
createResourceWatcherForTab,
createCommandsForTab,
createResourceWatcherForCommands,
getStubFile,
getCleanedPacket,
getSerializedPacket,
@ -55,15 +56,14 @@ add_task(async function() {
} else {
ok(true, "Stubs are up to date");
}
await closeTabAndToolbox().catch(() => {});
});
async function generateConsoleApiStubs() {
const stubs = new Map();
const tab = await addTab(TEST_URI);
const resourceWatcher = await createResourceWatcherForTab(tab);
const commands = await createCommandsForTab(tab);
const resourceWatcher = await createResourceWatcherForCommands(commands);
// The resource-watcher only supports a single call to watch/unwatch per
// instance, so we attach a unique watch callback, which will forward the
@ -115,6 +115,8 @@ async function generateConsoleApiStubs() {
onAvailable: onConsoleMessage,
});
await commands.destroy();
return stubs;
}

View File

@ -5,7 +5,8 @@
const {
STUBS_UPDATE_ENV,
createResourceWatcherForTab,
createCommandsForTab,
createResourceWatcherForCommands,
getCleanedPacket,
getStubFile,
writeStubsToFile,
@ -61,7 +62,8 @@ async function generateCssMessageStubs() {
const stubs = new Map();
const tab = await addTab(TEST_URI);
const resourceWatcher = await createResourceWatcherForTab(tab);
const commands = await createCommandsForTab(tab);
const resourceWatcher = await createResourceWatcherForCommands(commands);
// The resource-watcher only supports a single call to watch/unwatch per
// instance, so we attach a unique watch callback, which will forward the
@ -103,7 +105,7 @@ async function generateCssMessageStubs() {
onAvailable: onCSSMessageAvailable,
});
await closeTabAndToolbox().catch(() => {});
await commands.destroy();
return stubs;
}

View File

@ -4,7 +4,8 @@
"use strict";
const {
createResourceWatcherForTab,
createCommandsForTab,
createResourceWatcherForCommands,
STUBS_UPDATE_ENV,
getStubFile,
getCleanedPacket,
@ -57,7 +58,8 @@ add_task(async function() {
async function generateNetworkEventStubs() {
const stubs = new Map();
const tab = await addTab(TEST_URI);
const resourceWatcher = await createResourceWatcherForTab(tab);
const commands = await createCommandsForTab(tab);
const resourceWatcher = await createResourceWatcherForCommands(commands);
const stacktraces = new Map();
let addNetworkStub = function() {};
let addNetworkUpdateStub = function() {};
@ -145,6 +147,9 @@ async function generateNetworkEventStubs() {
onUpdated,
}
);
await commands.destroy();
return stubs;
}
// Ensures the order of the resource properties

View File

@ -5,7 +5,8 @@
const {
STUBS_UPDATE_ENV,
createResourceWatcherForTab,
createCommandsForTab,
createResourceWatcherForCommands,
getCleanedPacket,
getSerializedPacket,
getStubFile,
@ -57,15 +58,14 @@ add_task(async function() {
} else {
ok(true, "Stubs are up to date");
}
await closeTabAndToolbox();
});
async function generatePageErrorStubs() {
const stubs = new Map();
const tab = await addTab(TEST_URI);
const resourceWatcher = await createResourceWatcherForTab(tab);
const commands = await createCommandsForTab(tab);
const resourceWatcher = await createResourceWatcherForCommands(commands);
// The resource-watcher only supports a single call to watch/unwatch per
// instance, so we attach a unique watch callback, which will forward the

View File

@ -5,7 +5,8 @@
const {
STUBS_UPDATE_ENV,
createResourceWatcherForDescriptor,
createCommandsForMainProcess,
createResourceWatcherForCommands,
getCleanedPacket,
getSerializedPacket,
getStubFile,
@ -59,22 +60,8 @@ add_task(async function() {
async function generatePlatformMessagesStubs() {
const stubs = new Map();
// Instantiate a minimal server
const { DevToolsClient } = require("devtools/client/devtools-client");
const { DevToolsServer } = require("devtools/server/devtools-server");
DevToolsServer.init();
DevToolsServer.allowChromeProcess = true;
if (!DevToolsServer.createRootActor) {
DevToolsServer.registerAllActors();
}
const transport = DevToolsServer.connectPipe();
const client = new DevToolsClient(transport);
await client.connect();
const mainProcessDescriptor = await client.mainRoot.getMainProcess();
const resourceWatcher = await createResourceWatcherForDescriptor(
mainProcessDescriptor
);
const commands = await createCommandsForMainProcess();
const resourceWatcher = await createResourceWatcherForCommands(commands);
// The resource-watcher only supports a single call to watch/unwatch per
// instance, so we attach a unique watch callback, which will forward the
@ -105,7 +92,7 @@ async function generatePlatformMessagesStubs() {
}
resourceWatcher.targetList.destroy();
await client.close();
await commands.destroy();
return stubs;
}

View File

@ -14,25 +14,28 @@ const CHROME_PREFIX = "chrome://mochitests/content/browser/";
const STUBS_FOLDER = "devtools/client/webconsole/test/node/fixtures/stubs/";
const STUBS_UPDATE_ENV = "WEBCONSOLE_STUBS_UPDATE";
async function createResourceWatcherForTab(tab) {
async function createCommandsForTab(tab) {
const {
TabDescriptorFactory,
} = require("devtools/client/framework/tab-descriptor-factory");
const descriptor = await TabDescriptorFactory.createDescriptorForTab(tab);
const target = await descriptor.getTarget();
const resourceWatcher = await createResourceWatcherForDescriptor(
target.descriptorFront
);
return resourceWatcher;
CommandsFactory,
} = require("devtools/shared/commands/commands-factory");
const commands = await CommandsFactory.forTab(tab);
return commands;
}
async function createResourceWatcherForDescriptor(descriptor) {
async function createCommandsForMainProcess() {
const {
CommandsFactory,
} = require("devtools/shared/commands/commands-factory");
const commands = await CommandsFactory.forMainProcess();
return commands;
}
async function createResourceWatcherForCommands(commands) {
// Avoid mocha to try to load these module and fail while doing it when running node tests
const {
ResourceWatcher,
} = require("devtools/shared/resources/resource-watcher");
const commands = await descriptor.getCommands();
const targetList = commands.targetCommand;
await targetList.startListening();
return new ResourceWatcher(targetList);
@ -542,8 +545,9 @@ function parsePacketAndCreateFronts(packet) {
module.exports = {
STUBS_UPDATE_ENV,
createResourceWatcherForTab,
createResourceWatcherForDescriptor,
createCommandsForTab,
createCommandsForMainProcess,
createResourceWatcherForCommands,
getStubFile,
getCleanedPacket,
getSerializedPacket,

View File

@ -104,7 +104,11 @@ function connectToContentProcess(connection, mm, onDestroy) {
if (subject == mm) {
// Send the "tabDetached" event before closing the connection which
// will destroy fronts on the client.
connection.send({ from: actor.actor, type: "tabDetached" });
// Note that the content process may be destroyed before the actor is created.
// Avoid trying to send any tabDetached in such situation.
if (actor) {
connection.send({ from: actor.actor, type: "tabDetached" });
}
onClose();
}
}

View File

@ -11,15 +11,17 @@ const TEST_URI = `data:text/html;charset=utf-8,<style>${encodeURIComponent(
)}</style>`;
add_task(async function() {
const target = await addTabTarget(TEST_URI);
const browser = await addTab(TEST_URI);
const tab = gBrowser.getTabForBrowser(browser);
const {
ResourceWatcher,
} = require("devtools/shared/resources/resource-watcher");
const commands = await target.descriptorFront.getCommands();
const commands = await CommandsFactory.forTab(tab);
const targetList = commands.targetCommand;
await targetList.startListening();
const target = targetList.targetFront;
const resourceWatcher = new ResourceWatcher(targetList);
const styleSheetsFront = await target.getFront("stylesheets");

View File

@ -8,6 +8,9 @@
"use strict";
const TEST_DOC_URL = MAIN_DOMAIN + "doc_iframe.html";
const {
createLocalClientForTests,
} = require("devtools/shared/commands/commands-factory");
// URL of the <iframe> already loaded in doc_iframe.html (the top level test document)
const TEST_DOC_URL1 =
@ -19,15 +22,18 @@ const TEST_DOC_URL2 =
MAIN_DOMAIN.replace("test1.example.org", "example.net") + "doc_iframe2.html";
add_task(async function() {
const tabTarget = await addTabTarget(TEST_DOC_URL);
const { mainRoot } = tabTarget.client;
const browser = await addTab(TEST_DOC_URL);
const tab = gBrowser.getTabForBrowser(browser);
const client = await createLocalClientForTests();
// First test watching all frames. Really all of them.
// From all top level windows, tabs, and any inner remoted iframe.
await testWatchAllFrames(mainRoot);
await testWatchAllFrames(client.mainRoot);
// Then use the Watcher to watch only frames of a given tab.
await testWatchOneTabFrames(tabTarget);
const tabDescriptor = await client.mainRoot.getTab({ tab });
await testWatchOneTabFrames(tabDescriptor);
});
async function testWatchAllFrames(mainRoot) {
@ -83,12 +89,12 @@ async function testWatchAllFrames(mainRoot) {
watcher.off("target-available", onNewTarget);
}
async function testWatchOneTabFrames(tabTarget) {
async function testWatchOneTabFrames(tabDescriptor) {
info("Assert watchTargets against a given Tab");
const targets = [];
const tabDescriptor = tabTarget.descriptorFront;
const watcher = await tabDescriptor.getWatcher();
const tabTarget = await tabDescriptor.getTarget();
is(
await tabTarget.getWatcherFront(),

View File

@ -7,8 +7,8 @@
const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm");
const {
TabDescriptorFactory,
} = require("devtools/client/framework/tab-descriptor-factory");
CommandsFactory,
} = require("devtools/shared/commands/commands-factory");
const { DevToolsServer } = require("devtools/server/devtools-server");
const {
BrowserTestUtils,
@ -45,27 +45,14 @@ SimpleTest.registerCleanupFunction(function() {
}
});
/**
* Add a new test tab in the browser and load the given url.
* @return Promise a promise that resolves to the new target representing
* the page currently opened.
*/
async function getTargetForSelectedTab(gBrowser) {
const selectedTab = gBrowser.selectedTab;
await BrowserTestUtils.browserLoaded(selectedTab.linkedBrowser);
const descriptor = await TabDescriptorFactory.createDescriptorForTab(
selectedTab
);
return descriptor.getTarget();
}
/**
* Open a tab, load the url, wait for it to signal its readiness,
* find the tab with the devtools server, and call the callback.
* connect to this tab via DevTools protocol and return.
*
* Returns a function which can be called to close the opened ta
* and disconnect its devtools client.
* Returns an object with a few helpful attributes:
* - commands {Object}: The commands object defined by modules from devtools/shared/commands
* - target {TargetFront}: The current top-level target front.
* - doc {HtmlDocument}: the tab's document that got opened
*/
async function attachURL(url) {
// Get the current browser window
@ -81,18 +68,22 @@ async function attachURL(url) {
const win = window.open(url, "_blank");
await windowOpened;
const target = await getTargetForSelectedTab(gBrowser);
await target.attach();
const commands = await CommandsFactory.forTab(gBrowser.selectedTab);
await commands.targetCommand.startListening();
const cleanup = async function() {
await target.destroy();
await commands.destroy();
if (win) {
win.close();
}
};
gAttachCleanups.push(cleanup);
return { target, doc: win.document };
return {
commands,
target: commands.targetCommand.targetFront,
doc: win.document,
};
}
function promiseOnce(target, event) {
@ -144,9 +135,6 @@ function runNextTest() {
}
}
async function createResourceWatcher(target) {
const commands = await target.descriptorFront.getCommands();
const targetList = commands.targetCommand;
await targetList.startListening();
return new ResourceWatcher(targetList);
async function createResourceWatcher(commands) {
return new ResourceWatcher(commands.targetCommand);
}

View File

@ -31,11 +31,12 @@ async function reloadTarget() {
addAsyncTest(async function() {
const url = document.getElementById("inspectorContent").href;
const { target, doc } = await attachURL(url);
const { commands, doc } = await attachURL(url);
const target = commands.targetCommand.targetFront;
gDoc = doc;
const inspector = await target.getFront("inspector");
gWalker = inspector.walker;
gResourceWatcher = await createResourceWatcher(target);
gResourceWatcher = await createResourceWatcher(commands);
info("Start watching for root nodes and wait for the initial root node");
const rootNodePromise = new Promise(r => (gRootNodeResolve = r));

View File

@ -24,12 +24,13 @@ let gResourceWatcher = null;
addTest(async function setup() {
const url = document.getElementById("inspectorContent").href;
const { target, doc } = await attachURL(url);
const { commands, doc } = await attachURL(url);
const target = commands.targetCommand.targetFront;
const inspector = await target.getFront("inspector");
gInspectee = doc;
const walker = inspector.walker;
gWalker = await inspector.getWalker();
gResourceWatcher = await createResourceWatcher(target);
gResourceWatcher = await createResourceWatcher(commands);
ok(walker === gWalker, "getWalker() twice should return the same walker.");
runNextTest();

View File

@ -0,0 +1,174 @@
/* 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 { createCommandsDictionary } = require("devtools/shared/commands/index");
const ChromeUtils = require("ChromeUtils");
const { DevToolsLoader } = ChromeUtils.import(
"resource://devtools/shared/Loader.jsm"
);
loader.lazyRequireGetter(
this,
"DevToolsServer",
"devtools/server/devtools-server",
true
);
// eslint-disable-next-line mozilla/reject-some-requires
loader.lazyRequireGetter(
this,
"DevToolsClient",
"devtools/client/devtools-client",
true
);
/**
* Functions for creating Commands for all debuggable contexts.
*
* All methods of this `CommandsFactory` object receive argument to describe to
* which particular context we want to debug. And all returns a new instance of `commands` object.
* Commands are implemented by modules defined in devtools/shared/commands.
*/
exports.CommandsFactory = {
async forTab(tab) {
const client = await createLocalClient();
const descriptor = await client.mainRoot.getTab({ tab });
const commands = await createCommandsDictionary(descriptor);
return commands;
},
async forMainProcess() {
const client = await createLocalClient();
const descriptor = await client.mainRoot.getMainProcess();
const commands = await createCommandsDictionary(descriptor);
return commands;
},
/**
* For now, this method is only used by browser_target_list_various_descriptors.js
* in order to cover about:debugging codepath, where we connect to remote tabs via
* their current outerWindowID.
* But:
* 1) this can also be used to debug local tab, but TabDescriptor.localTab/isLocalTab will be null/false.
* 2) beyond this test, this isn't used to connect to remote tab just yet.
* Bug 1700909 should start using this from toolbox-init/descriptor-from-url
* and will finaly be used to connect to remote tabs.
*/
async forRemoteTabInTest({ outerWindowID }) {
const client = await createLocalClient();
const descriptor = await client.mainRoot.getTab({ outerWindowID });
const commands = await createCommandsDictionary(descriptor);
return commands;
},
/**
* `id` is the WorkerDebugger's id, which is a unique ID computed by the platform code.
* These ids are exposed via WorkerDescriptor's id attributes.
* WorkerDescritpors can be retrieved via MainFront.listAllWorkers()/listWorkers().
*/
async forWorker(id) {
const client = await createLocalClient();
const descriptor = await client.mainRoot.getWorker(id);
const commands = await createCommandsDictionary(descriptor);
return commands;
},
async forAddon(id) {
const client = await createLocalClient();
const descriptor = await client.mainRoot.getAddon({ id });
const commands = await createCommandsDictionary(descriptor);
return commands;
},
async forProcess(osPid) {
const client = await createLocalClient();
const descriptor = await client.mainRoot.getProcess(osPid);
const commands = await createCommandsDictionary(descriptor);
return commands;
},
/**
* One method to handle the whole setup sequence to connect to RDP backend for the Browser Console.
*
* This will instantiate a special DevTools module loader for the DevToolsServer.
* Then spawn a DevToolsClient to connect to it.
* Get a Main Process Descriptor from it.
* Finally spawn a commands object for this descriptor.
*/
async forBrowserConsole() {
// The Browser console ends up using the debugger in autocomplete.
// Because the debugger can't be running in the same compartment than its debuggee,
// we have to load the server in a dedicated Loader, flagged with
// `freshCompartment`, which will force it to be loaded in another compartment.
// We aren't using `invisibleToDebugger` in order to allow the Browser toolbox to
// debug the Browser console. This is fine as they will spawn distinct Loaders and
// so distinct `DevToolsServer` and actor modules.
const customLoader = new DevToolsLoader({
freshCompartment: true,
});
const { DevToolsServer: customDevToolsServer } = customLoader.require(
"devtools/server/devtools-server"
);
customDevToolsServer.init();
// We want all the actors (root, browser and target-scoped) to be registered on the
// DevToolsServer. This is needed so the Browser Console can retrieve:
// - the console actors, which are target-scoped (See Bug 1416105)
// - the screenshotActor, which is browser-scoped (for the `:screenshot` command)
customDevToolsServer.registerAllActors();
customDevToolsServer.allowChromeProcess = true;
const client = new DevToolsClient(customDevToolsServer.connectPipe());
await client.connect();
const descriptor = await client.mainRoot.getMainProcess();
// Hack something in order to help TargetMixinFront to distinguish the BrowserConsole
descriptor.createdForBrowserConsole = true;
const target = await descriptor.getTarget();
await target.attach();
const commands = await createCommandsDictionary(descriptor);
return commands;
},
};
async function createLocalClient() {
// Make sure the DevTools server is started.
ensureDevToolsServerInitialized();
// Create the client and connect it to the local server.
const client = new DevToolsClient(DevToolsServer.connectPipe());
await client.connect();
return client;
}
// Also expose this method for tests which would like to create a client
// without involving commands. This would typically be tests against the Watcher actor
// and requires to prevent having TargetCommand from running.
// Or tests which are covering RootFront or global actor's fronts.
exports.createLocalClientForTests = createLocalClient;
function ensureDevToolsServerInitialized() {
// Since a remote protocol connection will be made, let's start the
// DevToolsServer here, once and for all tools.
DevToolsServer.init();
// Enable all the actors. We may not need all of them and registering
// only root and target might be enough
DevToolsServer.registerAllActors();
// Enable being able to get child process actors
// Same, this might not be useful
DevToolsServer.allowChromeProcess = true;
}

View File

@ -23,7 +23,27 @@ async function createCommandsDictionary(descriptorFront) {
if (supportsWatcher) {
watcherFront = await descriptorFront.getWatcher();
}
const dictionary = {};
const { client } = descriptorFront;
const dictionary = {
// Expose both client and descriptor for legacy codebases, or tests.
// But ideally only commands should interact with these two objects
client,
descriptorFront,
// Expose for tests
waitForRequestsToSettle() {
return descriptorFront.client.waitForRequestsToSettle();
},
// We want to keep destroy being defined last
// eslint-disable-next-line sort-keys
async destroy() {
await descriptorFront.destroy();
await client.close();
},
};
for (const name in Commands) {
loader.lazyGetter(dictionary, name, () => {
const Constructor = require(Commands[name]);

View File

@ -7,5 +7,6 @@ DIRS += [
]
DevToolsModules(
"commands-factory.js",
"index.js",
)

View File

@ -18,8 +18,6 @@ add_task(async function() {
// which forces the emission of RDP requests we aren't correctly waiting for.
await pushPref("dom.ipc.processPrelaunch.enabled", false);
const client = await createLocalClient();
const mainRoot = client.mainRoot;
const tab = await addTab(FISSION_TEST_URL);
info("Test TargetCommand against workers via the parent process target");
@ -30,8 +28,7 @@ add_task(async function() {
// eslint-disable-next-line no-unused-vars
const sharedWorker = new SharedWorker(CHROME_WORKER_URL + "#shared-worker");
const targetDescriptor = await mainRoot.getMainProcess();
const commands = await targetDescriptor.getCommands();
const commands = await CommandsFactory.forMainProcess();
const targetList = commands.targetCommand;
const { TYPES } = targetList;
await targetList.startListening();
@ -105,7 +102,7 @@ add_task(async function() {
"Check that watchTargets will call the create callback for all existing workers"
);
const targets = [];
const topLevelTarget = await targetDescriptor.getTarget();
const topLevelTarget = await commands.targetCommand.targetFront;
const onAvailable = async ({ targetFront }) => {
ok(
targetFront.targetType === TYPES.WORKER ||
@ -180,9 +177,9 @@ add_task(async function() {
targetList.destroy();
info("Unregister service workers so they don't appear in other tests.");
await unregisterAllServiceWorkers(client);
await unregisterAllServiceWorkers(commands.client);
await client.close();
await commands.destroy();
await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
// registrationPromise is set by the test page.

View File

@ -18,23 +18,17 @@ add_task(async function() {
// This preference helps destroying the content process when we close the tab
await pushPref("dom.ipc.keepProcessesAlive.web", 1);
const client = await createLocalClient();
const mainRoot = client.mainRoot;
// Test fetching the frames from the main process target
await testBrowserFrames(mainRoot);
await testBrowserFrames();
// Test fetching the frames from a tab target
await testTabFrames(mainRoot);
await client.close();
await testTabFrames();
});
async function testBrowserFrames(mainRoot) {
async function testBrowserFrames() {
info("Test TargetCommand against frames via the parent process target");
const targetDescriptor = await mainRoot.getMainProcess();
const commands = await targetDescriptor.getCommands();
const commands = await CommandsFactory.forMainProcess();
const targetList = commands.targetCommand;
const { TYPES } = targetList;
await targetList.startListening();
@ -61,7 +55,7 @@ async function testBrowserFrames(mainRoot) {
// Assert that watchTargets will call the create callback for all existing frames
const targets = [];
const topLevelTarget = await targetDescriptor.getTarget();
const topLevelTarget = targetList.targetFront;
const onAvailable = ({ targetFront }) => {
is(
targetFront.targetType,
@ -108,6 +102,8 @@ async function testBrowserFrames(mainRoot) {
targetList.destroy();
await waitForAllTargetsToBeAttached(targetList);
await commands.destroy();
}
async function testTabFrames(mainRoot) {
@ -115,8 +111,7 @@ async function testTabFrames(mainRoot) {
// Create a TargetCommand for a given test tab
const tab = await addTab(FISSION_TEST_URL);
const descriptor = await mainRoot.getTab({ tab });
const commands = await descriptor.getCommands();
const commands = await CommandsFactory.forTab(tab);
const targetList = commands.targetCommand;
const { TYPES } = targetList;
@ -135,7 +130,7 @@ async function testTabFrames(mainRoot) {
// Assert that watchTargets will call the create callback for all existing frames
const targets = [];
const destroyedTargets = [];
const topLevelTarget = await descriptor.getTarget();
const topLevelTarget = targetList.targetFront;
const onAvailable = ({ targetFront, isTargetSwitching }) => {
is(
targetFront.targetType,
@ -290,4 +285,6 @@ async function testTabFrames(mainRoot) {
targetList.destroy();
BrowserTestUtils.removeTab(tab);
await commands.destroy();
}

View File

@ -17,8 +17,6 @@ add_task(async function() {
await pushPref("dom.ipc.processPrelaunch.enabled", false);
info("Setup the test page with workers of all types");
const client = await createLocalClient();
const mainRoot = client.mainRoot;
const tab = await addTab(FISSION_TEST_URL);
@ -29,8 +27,7 @@ add_task(async function() {
const sharedWorker = new SharedWorker(CHROME_WORKER_URL + "#shared-worker");
info("Create a target list for the main process target");
const targetDescriptor = await mainRoot.getMainProcess();
const commands = await targetDescriptor.getCommands();
const commands = await CommandsFactory.forMainProcess();
const targetList = commands.targetCommand;
const { TYPES } = targetList;
await targetList.startListening();
@ -97,7 +94,7 @@ add_task(async function() {
// Wait for all the targets to be fully attached so we don't have pending requests.
await waitForAllTargetsToBeAttached(targetList);
await client.close();
await commands.destroy();
await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
// registrationPromise is set by the test page.
const registration = await content.wrappedJSObject.registrationPromise;

View File

@ -12,29 +12,23 @@ add_task(async function() {
// This preference helps destroying the content process when we close the tab
await pushPref("dom.ipc.keepProcessesAlive.web", 1);
const client = await createLocalClient();
const mainRoot = client.mainRoot;
// Assert the limited behavior of this API with fission preffed off
await pushPref("devtools.browsertoolbox.fission", false);
// Test with Main process targets as top level target
await testPreffedOffMainProcess(mainRoot);
await client.close();
await testPreffedOffMainProcess();
});
async function testPreffedOffMainProcess(mainRoot) {
async function testPreffedOffMainProcess() {
info(
"Test TargetCommand when devtools's fission pref is false, via the parent process target"
);
const targetDescriptor = await mainRoot.getMainProcess();
const mainProcess = await targetDescriptor.getTarget();
const commands = await targetDescriptor.getCommands();
const commands = await CommandsFactory.forMainProcess();
const targetList = commands.targetCommand;
const { TYPES } = targetList;
await targetList.startListening();
const mainProcess = targetList.targetFront;
// The API should only report the top level target,
// i.e. the Main process target, which is considered as frame
@ -88,4 +82,6 @@ async function testPreffedOffMainProcess(mainRoot) {
targetList.unwatchTargets([TYPES.FRAME], onFrameAvailable);
targetList.destroy();
await commands.destroy();
}

View File

@ -17,10 +17,7 @@ add_task(async function() {
// This preference helps destroying the content process when we close the tab
await pushPref("dom.ipc.keepProcessesAlive.web", 1);
const client = await createLocalClient();
const targetDescriptor = await client.mainRoot.getMainProcess();
const commands = await targetDescriptor.getCommands();
const commands = await CommandsFactory.forMainProcess();
const targetList = commands.targetCommand;
await targetList.startListening();
@ -34,14 +31,11 @@ add_task(async function() {
.map(t => t.attachAndInitThread(targetList))
);
await client.close();
await commands.destroy();
});
add_task(async function() {
const client = await createLocalClient();
const targetDescriptor = await client.mainRoot.getMainProcess();
const commands = await targetDescriptor.getCommands();
const commands = await CommandsFactory.forMainProcess();
const targetList = commands.targetCommand;
await targetList.startListening();
@ -80,7 +74,7 @@ add_task(async function() {
.map(t => t.attachAndInitThread(targetList))
);
await client.close();
await commands.destroy();
});
async function testProcesses(targetList, target) {

View File

@ -16,13 +16,11 @@ add_task(async function() {
await pushPref("dom.ipc.processPrelaunch.enabled", false);
info("Setup the test page with workers of all types");
const client = await createLocalClient();
const tab = await addTab(FISSION_TEST_URL);
info("Create a target list for a tab target");
const descriptor = await client.mainRoot.getTab({ tab });
const commands = await descriptor.getCommands();
const commands = await CommandsFactory.forTab(tab);
const targetList = commands.targetCommand;
const { TYPES } = targetList;
@ -74,7 +72,7 @@ add_task(async function() {
// Stop listening to avoid worker related requests
targetList.destroy();
await client.waitForRequestsToSettle();
await commands.waitForRequestsToSettle();
await client.close();
await commands.destroy();
});

View File

@ -43,12 +43,11 @@ const ORG_WORKER_URL = URL_ROOT_ORG_SSL + "test_sw_page_worker.js";
* - onDestroyed should be called for the .com worker
*/
add_task(async function test_NavigationBetweenTwoDomains_NoDestroy() {
const { client, mainRoot } = await setupServiceWorkerNavigationTest();
await setupServiceWorkerNavigationTest();
const tab = await addTab(COM_PAGE_URL);
const { hooks, targetList } = await watchServiceWorkerTargets({
mainRoot,
const { hooks, commands, targetList } = await watchServiceWorkerTargets({
tab,
destroyServiceWorkersOnNavigation: false,
});
@ -102,8 +101,8 @@ add_task(async function test_NavigationBetweenTwoDomains_NoDestroy() {
// Stop listening to avoid worker related requests
targetList.destroy();
await client.waitForRequestsToSettle();
await client.close();
await commands.waitForRequestsToSettle();
await commands.destroy();
await removeTab(tab);
});
@ -128,12 +127,11 @@ add_task(async function test_NavigationBetweenTwoDomains_NoDestroy() {
* - onDestroyed should be called for the .com worker
*/
add_task(async function test_NavigationBetweenTwoDomains_WithDestroy() {
const { client, mainRoot } = await setupServiceWorkerNavigationTest();
await setupServiceWorkerNavigationTest();
const tab = await addTab(COM_PAGE_URL);
const { hooks, targetList } = await watchServiceWorkerTargets({
mainRoot,
const { hooks, commands, targetList } = await watchServiceWorkerTargets({
tab,
destroyServiceWorkersOnNavigation: true,
});
@ -181,8 +179,8 @@ add_task(async function test_NavigationBetweenTwoDomains_WithDestroy() {
// Stop listening to avoid worker related requests
targetList.destroy();
await client.waitForRequestsToSettle();
await client.close();
await commands.waitForRequestsToSettle();
await commands.destroy();
await removeTab(tab);
});
@ -230,7 +228,7 @@ add_task(async function test_NavigationToPageWithExistingWorker_WithDestroy() {
async function testNavigationToPageWithExistingWorker({
destroyServiceWorkersOnNavigation,
}) {
const { client, mainRoot } = await setupServiceWorkerNavigationTest();
await setupServiceWorkerNavigationTest();
const tab = await addTab(COM_PAGE_URL);
@ -245,8 +243,7 @@ async function testNavigationToPageWithExistingWorker({
info("Wait until we have fully navigated to the .org page");
await waitForRegistrationReady(tab, ORG_PAGE_URL);
const { hooks, targetList } = await watchServiceWorkerTargets({
mainRoot,
const { hooks, commands, targetList } = await watchServiceWorkerTargets({
tab,
destroyServiceWorkersOnNavigation,
});
@ -278,8 +275,8 @@ async function testNavigationToPageWithExistingWorker({
// Stop listening to avoid worker related requests
targetList.destroy();
await client.waitForRequestsToSettle();
await client.close();
await commands.waitForRequestsToSettle();
await commands.destroy();
await removeTab(tab);
}
@ -290,21 +287,14 @@ async function setupServiceWorkerNavigationTest() {
// Disable the preloaded process as it creates processes intermittently
// which forces the emission of RDP requests we aren't correctly waiting for.
await pushPref("dom.ipc.processPrelaunch.enabled", false);
info("Setup the test page with workers of all types");
const client = await createLocalClient();
const mainRoot = client.mainRoot;
return { client, mainRoot };
}
async function watchServiceWorkerTargets({
destroyServiceWorkersOnNavigation,
mainRoot,
tab,
}) {
info("Create a target list for a tab target");
const descriptor = await mainRoot.getTab({ tab });
const commands = await descriptor.getCommands();
const commands = await CommandsFactory.forTab(tab);
const targetList = commands.targetCommand;
// Enable Service Worker listening.
@ -340,7 +330,7 @@ async function watchServiceWorkerTargets({
onDestroyed
);
return { hooks, targetList };
return { hooks, commands, targetList };
}
async function unregisterServiceWorker(tab, expectedPageUrl) {

View File

@ -6,45 +6,37 @@
// Test the TargetCommand API switchToTarget function
add_task(async function() {
const client = await createLocalClient();
const commands = await CommandsFactory.forTab(gBrowser.selectedTab);
await testSwitchToTarget(client);
await testSwitchToTarget(commands);
await client.close();
await commands.destroy();
});
async function testSwitchToTarget(client) {
async function testSwitchToTarget(commands) {
info("Test TargetCommand.switchToTarget method");
const { mainRoot } = client;
// Create a first target to switch from, a new tab with an iframe
const firstTab = await addTab(
`data:text/html,<iframe src="data:text/html,foo"></iframe>`
);
const firstDescriptor = await mainRoot.getTab({ tab: gBrowser.selectedTab });
const firstTarget = await firstDescriptor.getTarget();
const commands = await firstDescriptor.getCommands();
const targetList = commands.targetCommand;
const { TYPES } = targetList;
await targetList.startListening();
is(
targetList.targetFront,
firstTarget,
"The target list top level target is the main process one"
);
// Create a second target to switch to, a new tab with an iframe
const secondTab = await addTab(
`data:text/html,<iframe src="data:text/html,bar"></iframe>`
);
const secondDescriptor = await mainRoot.getTab({ tab: gBrowser.selectedTab });
const secondDescriptor = await commands.client.mainRoot.getTab({
tab: gBrowser.selectedTab,
});
const secondTarget = await secondDescriptor.getTarget();
const frameTargets = [];
let currentTarget = firstTarget;
const firstTarget = targetList.targetFront;
let currentTarget = targetList.targetFront;
const onFrameAvailable = ({ targetFront, isTargetSwitching }) => {
is(
targetFront.targetType,
@ -142,6 +134,8 @@ async function testSwitchToTarget(client) {
targetList.destroy();
await commands.destroy();
BrowserTestUtils.removeTab(firstTab);
BrowserTestUtils.removeTab(secondTab);
}

View File

@ -18,9 +18,6 @@ add_task(async function() {
// which forces the emission of RDP requests we aren't correctly waiting for.
await pushPref("dom.ipc.processPrelaunch.enabled", false);
const client = await createLocalClient();
const mainRoot = client.mainRoot;
// The WorkerDebuggerManager#getWorkerDebuggerEnumerator method we're using to retrieve
// workers loops through _all_ the workers in the process, which means it goes over workers
// from other tabs as well. Here we add a few tabs that are not going to be used in the
@ -32,16 +29,15 @@ add_task(async function() {
const tab = await addTab(`${FISSION_TEST_URL}?&noServiceWorker`);
// Create a TargetCommand for the tab
const descriptor = await mainRoot.getTab({ tab });
const target = await descriptor.getTarget();
const commands = await CommandsFactory.forTab(tab);
await commands.targetCommand.startListening();
// Ensure attaching the target as BrowsingContextTargetActor.listWorkers
// assert that the target actor is attached.
// It isn't clear if this assertion is meaningful?
const target = commands.targetCommand.targetFront;
await target.attach();
const commands = await descriptor.getCommands();
const targetList = commands.targetCommand;
const { TYPES } = targetList;
// Workaround to allow listening for workers in the content toolbox
@ -322,8 +318,8 @@ add_task(async function() {
targetList.destroy();
info("Unregister service workers so they don't appear in other tests.");
await unregisterAllServiceWorkers(client);
await unregisterAllServiceWorkers(commands.client);
BrowserTestUtils.removeTab(tab);
await client.close();
await commands.destroy();
});

View File

@ -18,24 +18,18 @@ add_task(async function() {
// This preference helps destroying the content process when we close the tab
await pushPref("dom.ipc.keepProcessesAlive.web", 1);
const client = await createLocalClient();
const mainRoot = client.mainRoot;
await testLocalTab(mainRoot);
await testRemoteTab(mainRoot);
await testParentProcess(mainRoot);
await testContentProcess(mainRoot);
await testWorker(mainRoot);
await testWebExtension(mainRoot);
await client.close();
await testLocalTab();
await testRemoteTab();
await testParentProcess();
await testContentProcess();
await testWorker();
await testWebExtension();
});
async function testParentProcess(rootFront) {
async function testParentProcess() {
info("Test TargetCommand against parent process descriptor");
const descriptor = await rootFront.getMainProcess();
const commands = await descriptor.getCommands();
const commands = await CommandsFactory.forMainProcess();
const targetList = commands.targetCommand;
await targetList.startListening();
@ -55,14 +49,15 @@ async function testParentProcess(rootFront) {
targetList.destroy();
await waitForAllTargetsToBeAttached(targetList);
await commands.destroy();
}
async function testLocalTab(rootFront) {
async function testLocalTab() {
info("Test TargetCommand against local tab descriptor (via getTab({ tab }))");
const tab = await addTab(TEST_URL);
const descriptor = await rootFront.getTab({ tab });
const commands = await descriptor.getCommands();
const commands = await CommandsFactory.forTab(tab);
const targetList = commands.targetCommand;
await targetList.startListening();
// Avoid the target to close the client when we destroy the target list and the target
@ -82,18 +77,19 @@ async function testLocalTab(rootFront) {
targetList.destroy();
BrowserTestUtils.removeTab(tab);
await commands.destroy();
}
async function testRemoteTab(rootFront) {
async function testRemoteTab() {
info(
"Test TargetCommand against remote tab descriptor (via getTab({ outerWindowID }))"
);
const tab = await addTab(TEST_URL);
const descriptor = await rootFront.getTab({
const commands = await CommandsFactory.forRemoteTabInTest({
outerWindowID: tab.linkedBrowser.outerWindowID,
});
const commands = await descriptor.getCommands();
const targetList = commands.targetCommand;
await targetList.startListening();
@ -120,7 +116,7 @@ async function testRemoteTab(rootFront) {
if (isFissionEnabled()) {
info("With fission, cross process switching destroy everything");
ok(targetFront.isDestroyed(), "Top level target is destroyed");
ok(descriptor.isDestroyed(), "Descriptor is also destroyed");
ok(commands.descriptorFront.isDestroyed(), "Descriptor is also destroyed");
} else {
is(
targetList.targetFront,
@ -132,9 +128,11 @@ async function testRemoteTab(rootFront) {
targetList.destroy();
BrowserTestUtils.removeTab(tab);
await commands.destroy();
}
async function testWebExtension(rootFront) {
async function testWebExtension() {
info("Test TargetCommand against webextension descriptor");
const extension = ExtensionTestUtils.loadExtension({
@ -146,8 +144,7 @@ async function testWebExtension(rootFront) {
await extension.startup();
const descriptor = await rootFront.getAddon({ id: extension.id });
const commands = await descriptor.getCommands();
const commands = await CommandsFactory.forAddon(extension.id);
const targetList = commands.targetCommand;
await targetList.startListening();
@ -165,9 +162,11 @@ async function testWebExtension(rootFront) {
targetList.destroy();
await extension.unload();
await commands.destroy();
}
async function testContentProcess(rootFront) {
async function testContentProcess() {
info("Test TargetCommand against content process descriptor");
const tab = await BrowserTestUtils.openNewForegroundTab({
@ -178,8 +177,7 @@ async function testContentProcess(rootFront) {
const { osPid } = tab.linkedBrowser.browsingContext.currentWindowGlobal;
const descriptor = await rootFront.getProcess(osPid);
const commands = await descriptor.getCommands();
const commands = await CommandsFactory.forProcess(osPid);
const targetList = commands.targetCommand;
await targetList.startListening();
@ -197,17 +195,35 @@ async function testContentProcess(rootFront) {
targetList.destroy();
BrowserTestUtils.removeTab(tab);
await commands.destroy();
}
async function testWorker(rootFront) {
// CommandsFactory expect the worker id, which is computed from the nsIWorkerDebugger.id attribute
function getWorkerDebuggerId(url) {
const wdm = Cc[
"@mozilla.org/dom/workers/workerdebuggermanager;1"
].createInstance(Ci.nsIWorkerDebuggerManager);
const workers = wdm.getWorkerDebuggerEnumerator();
while (workers.hasMoreElements()) {
const worker = workers.getNext();
worker.QueryInterface(Ci.nsIWorkerDebugger);
if (worker.url == url) {
return worker.id;
}
}
return null;
}
async function testWorker() {
info("Test TargetCommand against worker descriptor");
const workerUrl = CHROME_WORKER_URL + "#descriptor";
new Worker(workerUrl);
const { workers } = await rootFront.listWorkers();
const descriptor = workers.find(w => w.url == workerUrl);
const commands = await descriptor.getCommands();
const workerId = getWorkerDebuggerId(workerUrl);
ok(workerId, "Found the worker Debugger ID");
const commands = await CommandsFactory.forWorker(workerId);
const targetList = commands.targetCommand;
await targetList.startListening();
@ -223,4 +239,11 @@ async function testWorker(rootFront) {
is(targetFront.isTopLevel, true, "This is flagged as top level");
targetList.destroy();
// Calling CommandsFactory.forWorker, will call RootFront.getWorker
// which will spawn lots of worker legacy code, firing lots of requests,
// which may still be pending
await commands.waitForRequestsToSettle();
await commands.destroy();
}

View File

@ -17,21 +17,15 @@ add_task(async function() {
// This preference helps destroying the content process when we close the tab
await pushPref("dom.ipc.keepProcessesAlive.web", 1);
const client = await createLocalClient();
const mainRoot = client.mainRoot;
await testWatchTargets(mainRoot);
await testContentProcessTarget(mainRoot);
await testThrowingInOnAvailable(mainRoot);
await client.close();
await testWatchTargets();
await testContentProcessTarget();
await testThrowingInOnAvailable();
});
async function testWatchTargets(mainRoot) {
async function testWatchTargets() {
info("Test TargetCommand watchTargets function");
const targetDescriptor = await mainRoot.getMainProcess();
const commands = await targetDescriptor.getCommands();
const commands = await CommandsFactory.forMainProcess();
const targetList = commands.targetCommand;
const { TYPES } = targetList;
@ -44,7 +38,7 @@ async function testWatchTargets(mainRoot) {
"Check that onAvailable is called for processes already created *before* the call to watchTargets"
);
const targets = new Set();
const topLevelTarget = await targetDescriptor.getTarget();
const topLevelTarget = targetList.targetFront;
const onAvailable = ({ targetFront }) => {
if (targets.has(targetFront)) {
ok(false, "The same target is notified multiple times via onAvailable");
@ -157,16 +151,16 @@ async function testWatchTargets(mainRoot) {
targetList.destroy();
// Also destroy the descriptor so that testThrowingInOnAvailable can get a fresh
// commands object and also a fresh TargetList instance
targetDescriptor.destroy();
await commands.destroy();
}
async function testContentProcessTarget(mainRoot) {
async function testContentProcessTarget() {
info("Test TargetCommand watchTargets with a content process target");
const processes = await mainRoot.listProcesses();
const commands = await processes[1].getCommands();
const {
osPid,
} = gBrowser.selectedBrowser.browsingContext.currentWindowGlobal;
const commands = await CommandsFactory.forProcess(osPid);
const targetList = commands.targetCommand;
const { TYPES } = targetList;
@ -176,7 +170,7 @@ async function testContentProcessTarget(mainRoot) {
// as listening for additional target is only enable for the parent process target.
// See bug 1593928.
const targets = new Set();
const topLevelTarget = await processes[1].getTarget();
const topLevelTarget = targetList.targetFront;
const onAvailable = ({ targetFront }) => {
if (targets.has(targetFront)) {
// This may fail if the top level target is reported by LegacyImplementation
@ -206,15 +200,16 @@ async function testContentProcessTarget(mainRoot) {
targetList.unwatchTargets([TYPES.PROCESS], onAvailable, onDestroyed);
targetList.destroy();
await commands.destroy();
}
async function testThrowingInOnAvailable(mainRoot) {
async function testThrowingInOnAvailable() {
info(
"Test TargetCommand watchTargets function when an exception is thrown in onAvailable callback"
);
const targetDescriptor = await mainRoot.getMainProcess();
const commands = await targetDescriptor.getCommands();
const commands = await CommandsFactory.forMainProcess();
const targetList = commands.targetCommand;
const { TYPES } = targetList;
@ -243,4 +238,6 @@ async function testThrowingInOnAvailable(mainRoot) {
);
targetList.destroy();
await commands.destroy();
}

View File

@ -9,12 +9,10 @@ const TEST_URL = "data:text/html;charset=utf-8,Actor caching test";
add_task(async function() {
info("Setup the test page with workers of all types");
const client = await createLocalClient();
const tab = await addTab(TEST_URL);
info("Create a target list for a tab target");
const descriptor = await client.mainRoot.getTab({ tab });
const commands = await descriptor.getCommands();
const commands = await CommandsFactory.forTab(tab);
const targetList = commands.targetCommand;
await targetList.startListening();
@ -54,8 +52,8 @@ add_task(async function() {
);
targetList.destroy();
await client.waitForRequestsToSettle();
await client.close();
await commands.waitForRequestsToSettle();
await commands.destroy();
});
/**

View File

@ -7,13 +7,10 @@
add_task(async function() {
info("Setup the test page with workers of all types");
const client = await createLocalClient();
const mainRoot = client.mainRoot;
const tab = await addTab("data:text/html;charset=utf-8,Configuration actor");
info("Create a target list for a tab target");
const descriptor = await mainRoot.getTab({ tab });
const commands = await descriptor.getCommands();
const commands = await CommandsFactory.forTab(tab);
const targetList = commands.targetCommand;
await targetList.startListening();
@ -68,7 +65,7 @@ add_task(async function() {
);
targetList.destroy();
await client.close();
await commands.destroy();
});
function compareOptions(options, expected, message) {

View File

@ -15,29 +15,14 @@ Services.scriptloader.loadSubScript(
const { DevToolsClient } = require("devtools/client/devtools-client");
const { DevToolsServer } = require("devtools/server/devtools-server");
async function createLocalClient() {
// Instantiate a minimal server
DevToolsServer.init();
DevToolsServer.allowChromeProcess = true;
if (!DevToolsServer.createRootActor) {
DevToolsServer.registerAllActors();
}
const transport = DevToolsServer.connectPipe();
const client = new DevToolsClient(transport);
await client.connect();
return client;
}
async function _initResourceWatcherFromDescriptor(
client,
descriptor,
async function _initResourceWatcherFromCommands(
commands,
{ listenForWorkers = false } = {}
) {
const {
ResourceWatcher,
} = require("devtools/shared/resources/resource-watcher");
const commands = await descriptor.getCommands();
const targetList = commands.targetCommand;
if (listenForWorkers) {
targetList.listenForWorkers = true;
@ -47,7 +32,7 @@ async function _initResourceWatcherFromDescriptor(
// Now create a ResourceWatcher
const resourceWatcher = new ResourceWatcher(targetList);
return { client, resourceWatcher, targetList };
return { client: commands.client, commands, resourceWatcher, targetList };
}
/**
@ -58,34 +43,36 @@ async function _initResourceWatcherFromDescriptor(
* @param {Object} options
* @param {Boolean} options.listenForWorkers
* @return {Object} object
* @return {ResourceWatcher} object.client
* The underlying client instance.
* @return {ResourceWatcher} object.resourceWatcher
* The underlying resource watcher interface.
* @return {Object} object.commands
* The commands object defined by modules from devtools/shared/commands.
* @return {DevToolsClient} object.client
* The underlying client instance.
* @return {DevToolsClient} object.targetList
* The underlying target list instance.
*/
async function initResourceWatcher(tab, options) {
const client = await createLocalClient();
const descriptor = await client.mainRoot.getTab({ tab });
return _initResourceWatcherFromDescriptor(client, descriptor, options);
const commands = await CommandsFactory.forTab(tab);
return _initResourceWatcherFromCommands(commands, options);
}
/**
* Instantiate a multi-process ResourceWatcher, watching all type of targets.
*
* @return {Object} object
* @return {ResourceWatcher} object.client
* The underlying client instance.
* @return {ResourceWatcher} object.resourceWatcher
* The underlying resource watcher interface.
* @return {Object} object.commands
* The commands object defined by modules from devtools/shared/commands.
* @return {DevToolsClient} object.client
* The underlying client instance.
* @return {DevToolsClient} object.targetList
* The underlying target list instance.
*/
async function initMultiProcessResourceWatcher() {
const client = await createLocalClient();
const descriptor = await client.mainRoot.getMainProcess();
return _initResourceWatcherFromDescriptor(client, descriptor);
const commands = await CommandsFactory.forMainProcess();
return _initResourceWatcherFromCommands(commands);
}
// Copied from devtools/shared/webconsole/test/chrome/common.js

View File

@ -13,16 +13,15 @@ let ORIGINAL_LONG_STRING_LENGTH, ORIGINAL_LONG_STRING_INITIAL_LENGTH;
add_task(async function() {
const tab = await addTab(URL_ROOT + "network_requests_iframe.html");
const target = await createAndAttachTargetForTab(tab);
// Avoid mocha to try to load these module and fail while doing it when running node tests
const {
ResourceWatcher,
} = require("devtools/shared/resources/resource-watcher");
const commands = await target.descriptorFront.getCommands();
const commands = await CommandsFactory.forTab(tab);
const targetList = commands.targetCommand;
await targetList.startListening();
const target = commands.targetCommand.targetFront;
const resourceWatcher = new ResourceWatcher(targetList);
// Override the default long string settings to lower values.

View File

@ -12,6 +12,9 @@ const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm");
const { DevToolsServer } = require("devtools/server/devtools-server");
// eslint-disable-next-line mozilla/reject-some-requires
const { DevToolsClient } = require("devtools/client/devtools-client");
const {
CommandsFactory,
} = require("devtools/shared/commands/commands-factory");
const Services = require("Services");
@ -113,18 +116,15 @@ var _attachConsole = async function(listeners, attachToTab, attachToWorker) {
};
async function createResourceWatcherForTab() {
const client = await connectToDebugger();
const targetDescriptor = await client.mainRoot.getMainProcess();
const target = await targetDescriptor.getTarget();
// Avoid mocha to try to load these module and fail while doing it when running node tests
const {
ResourceWatcher,
} = require("devtools/shared/resources/resource-watcher");
const commands = await targetDescriptor.getCommands();
const commands = await CommandsFactory.forMainProcess();
const targetList = commands.targetCommand;
await targetList.startListening();
const target = commands.targetCommand.targetFront;
const resourceWatcher = new ResourceWatcher(targetList);
return { resourceWatcher, target };