gecko-dev/remote/shared/messagehandler/WindowGlobalMessageHandler.sys.mjs

147 lines
4 KiB
JavaScript

/* 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 {
ContextDescriptorType,
MessageHandler,
} from "chrome://remote/content/shared/messagehandler/MessageHandler.sys.mjs";
/**
* A WindowGlobalMessageHandler is dedicated to debugging a single window
* global. It follows the lifecycle of the corresponding window global and will
* therefore not survive any navigation. This MessageHandler cannot forward
* commands further to other MessageHandlers and represents a leaf node in a
* MessageHandler network.
*/
export class WindowGlobalMessageHandler extends MessageHandler {
#innerWindowId;
constructor() {
super(...arguments);
this.#innerWindowId = this.context.window.windowGlobalChild.innerWindowId;
}
/**
* Returns the WindowGlobalMessageHandler module path.
*
* @return {String}
*/
static get modulePath() {
return "windowglobal";
}
/**
* Returns the WindowGlobalMessageHandler type.
*
* @return {String}
*/
static get type() {
return "WINDOW_GLOBAL";
}
/**
* For WINDOW_GLOBAL MessageHandlers, `context` is a BrowsingContext,
* and BrowsingContext.id can be used as the context id.
*
* @param {BrowsingContext} context
* WindowGlobalMessageHandler contexts are expected to be
* BrowsingContexts.
* @return {String}
* The browsing context id.
*/
static getIdFromContext(context) {
return context.id;
}
get innerWindowId() {
return this.#innerWindowId;
}
get processActor() {
return ChromeUtils.domProcessChild.getActor("WebDriverProcessData");
}
get window() {
return this.context.window;
}
async applyInitialSessionDataItems(sessionDataItems) {
if (!Array.isArray(sessionDataItems)) {
return;
}
const destination = {
type: WindowGlobalMessageHandler.type,
};
// Create a Map with the structure moduleName -> category -> relevant session data items.
const structuredUpdates = new Map();
for (const sessionDataItem of sessionDataItems) {
const { category, contextDescriptor, moduleName } = sessionDataItem;
if (!this.matchesContext(contextDescriptor)) {
continue;
}
if (!structuredUpdates.has(moduleName)) {
// Skip session data item if the module is not present
// for the destination.
if (!this.moduleCache.hasModule(moduleName, destination)) {
continue;
}
structuredUpdates.set(moduleName, new Map());
}
if (!structuredUpdates.get(moduleName).has(category)) {
structuredUpdates.get(moduleName).set(category, new Set());
}
structuredUpdates
.get(moduleName)
.get(category)
.add(sessionDataItem);
}
const sessionDataPromises = [];
for (const [moduleName, categories] of structuredUpdates.entries()) {
for (const [category, relevantSessionData] of categories.entries()) {
sessionDataPromises.push(
this.handleCommand({
moduleName,
commandName: "_applySessionData",
params: {
category,
initial: true,
sessionData: Array.from(relevantSessionData),
},
destination,
})
);
}
}
await Promise.all(sessionDataPromises);
// With the session data applied the handler is now ready to be used.
this.emitEvent("window-global-handler-created", {
contextId: this.contextId,
innerWindowId: this.#innerWindowId,
});
}
forwardCommand(command) {
throw new Error(
`Cannot forward commands from a "WINDOW_GLOBAL" MessageHandler`
);
}
matchesContext(contextDescriptor) {
return (
contextDescriptor.type === ContextDescriptorType.All ||
(contextDescriptor.type === ContextDescriptorType.TopBrowsingContext &&
contextDescriptor.id === this.context.browserId)
);
}
}