зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1830404 - [remote] Support sending message handler commands from windowglobal to root r=webdriver-reviewers,whimboo
Depends on D176713 Differential Revision: https://phabricator.services.mozilla.com/D176714
This commit is contained in:
Родитель
9fa2ad7bd9
Коммит
fea56ca2b2
|
@ -88,6 +88,7 @@ export class MessageHandler extends EventEmitter {
|
|||
#contextId;
|
||||
#eventsDispatcher;
|
||||
#moduleCache;
|
||||
#registry;
|
||||
#sessionId;
|
||||
|
||||
/**
|
||||
|
@ -97,8 +98,10 @@ export class MessageHandler extends EventEmitter {
|
|||
* ID of the session the handler is used for.
|
||||
* @param {object} context
|
||||
* The context linked to this MessageHandler instance.
|
||||
* @param {MessageHandlerRegistry} registry
|
||||
* The MessageHandlerRegistry which owns this MessageHandler instance.
|
||||
*/
|
||||
constructor(sessionId, context) {
|
||||
constructor(sessionId, context, registry) {
|
||||
super();
|
||||
|
||||
this.#moduleCache = new lazy.ModuleCache(this);
|
||||
|
@ -107,6 +110,7 @@ export class MessageHandler extends EventEmitter {
|
|||
this.#context = context;
|
||||
this.#contextId = this.constructor.getIdFromContext(context);
|
||||
this.#eventsDispatcher = new lazy.EventsDispatcher(this);
|
||||
this.#registry = registry;
|
||||
}
|
||||
|
||||
get context() {
|
||||
|
@ -129,6 +133,10 @@ export class MessageHandler extends EventEmitter {
|
|||
return [this.sessionId, this.constructor.type, this.contextId].join("-");
|
||||
}
|
||||
|
||||
get registry() {
|
||||
return this.#registry;
|
||||
}
|
||||
|
||||
get sessionId() {
|
||||
return this.#sessionId;
|
||||
}
|
||||
|
|
|
@ -195,7 +195,8 @@ export class MessageHandlerRegistry extends EventEmitter {
|
|||
_createMessageHandler(sessionId, sessionDataItems) {
|
||||
const messageHandler = new this._messageHandlerClass(
|
||||
sessionId,
|
||||
this._context
|
||||
this._context,
|
||||
this
|
||||
);
|
||||
|
||||
messageHandler.on(
|
||||
|
|
|
@ -192,12 +192,23 @@ export class ModuleCache {
|
|||
}
|
||||
|
||||
_getModuleFolder(originType, destinationType) {
|
||||
// root messages should always target the root layer.
|
||||
// NB: The idea here is just to avoid confusing the module cache when
|
||||
// trying to send to `root` from `windowglobal`. The general rule should
|
||||
// normally be "if the destination has a higher level than the origin, just
|
||||
// use the destination as target folder", but as we don't support other
|
||||
// levels than root & windowglobal, we can simplify this to this for now.
|
||||
if (destinationType === "root") {
|
||||
return "root";
|
||||
}
|
||||
|
||||
const originPath = lazy.getMessageHandlerClass(originType).modulePath;
|
||||
if (originType === destinationType) {
|
||||
// If the command is targeting the current type, the module is expected to
|
||||
// be in eg "windowglobal/${moduleName}.jsm".
|
||||
return originPath;
|
||||
}
|
||||
|
||||
// If the command is targeting another type, the module is expected to
|
||||
// be in a composed folder eg "windowglobal-in-root/${moduleName}.jsm".
|
||||
const destinationPath = lazy.getMessageHandlerClass(destinationType)
|
||||
|
|
|
@ -11,6 +11,10 @@ const lazy = {};
|
|||
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs",
|
||||
getMessageHandlerFrameChildActor:
|
||||
"chrome://remote/content/shared/messagehandler/transports/js-window-actors/MessageHandlerFrameChild.sys.mjs",
|
||||
RootMessageHandler:
|
||||
"chrome://remote/content/shared/messagehandler/RootMessageHandler.sys.mjs",
|
||||
WindowRealm: "chrome://remote/content/shared/Realm.sys.mjs",
|
||||
});
|
||||
|
||||
|
@ -171,9 +175,16 @@ export class WindowGlobalMessageHandler extends MessageHandler {
|
|||
}
|
||||
|
||||
forwardCommand(command) {
|
||||
throw new Error(
|
||||
`Cannot forward commands from a "WINDOW_GLOBAL" MessageHandler`
|
||||
);
|
||||
switch (command.destination.type) {
|
||||
case lazy.RootMessageHandler.type:
|
||||
return lazy
|
||||
.getMessageHandlerFrameChildActor(this)
|
||||
.sendCommand(command, this.sessionId);
|
||||
default:
|
||||
throw new Error(
|
||||
`Cannot forward command to "${command.destination.type}" from "${this.constructor.type}".`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -214,4 +225,21 @@ export class WindowGlobalMessageHandler extends MessageHandler {
|
|||
contextDescriptor.id === this.context.browserId)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a command to the root MessageHandler.
|
||||
*
|
||||
* @param {Command} command
|
||||
* The command to send to the root MessageHandler.
|
||||
* @returns {Promise}
|
||||
* A promise which resolves with the return value of the command.
|
||||
*/
|
||||
sendRootCommand(command) {
|
||||
return this.handleCommand({
|
||||
...command,
|
||||
destination: {
|
||||
type: lazy.RootMessageHandler.type,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,3 +22,4 @@ prefs =
|
|||
[browser_session_data_update.js]
|
||||
[browser_session_data_update_categories.js]
|
||||
[browser_session_data_update_contexts.js]
|
||||
[browser_windowglobal_to_root.js]
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { RootMessageHandler } = ChromeUtils.importESModule(
|
||||
"chrome://remote/content/shared/messagehandler/RootMessageHandler.sys.mjs"
|
||||
);
|
||||
|
||||
add_task(async function test_windowGlobal_to_root_command() {
|
||||
const browsingContextId = gBrowser.selectedBrowser.browsingContext.id;
|
||||
|
||||
const rootMessageHandler = createRootMessageHandler(
|
||||
"session-id-windowglobal-to-rootModule"
|
||||
);
|
||||
|
||||
for (const commandName of [
|
||||
"testHandleCommandToRoot",
|
||||
"testSendRootCommand",
|
||||
]) {
|
||||
const valueFromRoot = await rootMessageHandler.handleCommand({
|
||||
moduleName: "windowglobaltoroot",
|
||||
commandName,
|
||||
destination: {
|
||||
type: WindowGlobalMessageHandler.type,
|
||||
id: browsingContextId,
|
||||
},
|
||||
});
|
||||
|
||||
is(
|
||||
valueFromRoot,
|
||||
"root-value-called-from-windowglobal",
|
||||
"Retrieved the expected value from windowglobaltoroot using " +
|
||||
commandName
|
||||
);
|
||||
}
|
||||
|
||||
rootMessageHandler.destroy();
|
||||
});
|
|
@ -0,0 +1,19 @@
|
|||
/* 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 { Module } from "chrome://remote/content/shared/messagehandler/Module.sys.mjs";
|
||||
|
||||
class WindowGlobalToRootModule extends Module {
|
||||
destroy() {}
|
||||
|
||||
/**
|
||||
* Commands
|
||||
*/
|
||||
|
||||
getValueFromRoot() {
|
||||
return "root-value-called-from-windowglobal";
|
||||
}
|
||||
}
|
||||
|
||||
export const windowglobaltoroot = WindowGlobalToRootModule;
|
|
@ -0,0 +1,33 @@
|
|||
/* 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 { Module } from "chrome://remote/content/shared/messagehandler/Module.sys.mjs";
|
||||
import { RootMessageHandler } from "chrome://remote/content/shared/messagehandler/RootMessageHandler.sys.mjs";
|
||||
|
||||
class WindowGlobalToRootModule extends Module {
|
||||
destroy() {}
|
||||
|
||||
/**
|
||||
* Commands
|
||||
*/
|
||||
|
||||
testHandleCommandToRoot(params, destination) {
|
||||
return this.messageHandler.handleCommand({
|
||||
moduleName: "windowglobaltoroot",
|
||||
commandName: "getValueFromRoot",
|
||||
destination: {
|
||||
type: RootMessageHandler.type,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
testSendRootCommand(params, destination) {
|
||||
return this.messageHandler.sendRootCommand({
|
||||
moduleName: "windowglobaltoroot",
|
||||
commandName: "getValueFromRoot",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const windowglobaltoroot = WindowGlobalToRootModule;
|
|
@ -13,6 +13,26 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
|||
"chrome://remote/content/shared/messagehandler/WindowGlobalMessageHandler.sys.mjs",
|
||||
});
|
||||
|
||||
/**
|
||||
* Map from MessageHandlerRegistry to MessageHandlerFrameChild actor. This will
|
||||
* allow a WindowGlobalMessageHandler to find the JSWindowActorChild instance to
|
||||
* use to send commands.
|
||||
*/
|
||||
const registryToActor = new WeakMap();
|
||||
|
||||
/**
|
||||
* Retrieve the MessageHandlerFrameChild which is linked to the provided
|
||||
* WindowGlobalMessageHandler instance.
|
||||
*
|
||||
* @param {WindowGlobalMessageHandler} messageHandler
|
||||
* The WindowGlobalMessageHandler for which to get the JSWindowActor.
|
||||
* @returns {MessageHandlerFrameChild}
|
||||
* The corresponding MessageHandlerFrameChild instance.
|
||||
*/
|
||||
export function getMessageHandlerFrameChildActor(messageHandler) {
|
||||
return registryToActor.get(messageHandler.registry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Child actor for the MessageHandlerFrame JSWindowActor. The
|
||||
* MessageHandlerFrame actor is used by RootTransport to communicate between
|
||||
|
@ -24,6 +44,8 @@ export class MessageHandlerFrameChild extends JSWindowActorChild {
|
|||
this.context = this.manager.browsingContext;
|
||||
|
||||
this._registry = new lazy.MessageHandlerRegistry(this.type, this.context);
|
||||
registryToActor.set(this._registry, this);
|
||||
|
||||
this._onRegistryEvent = this._onRegistryEvent.bind(this);
|
||||
|
||||
// MessageHandlerFrameChild is responsible for forwarding events from
|
||||
|
@ -69,6 +91,13 @@ export class MessageHandlerFrameChild extends JSWindowActorChild {
|
|||
return null;
|
||||
}
|
||||
|
||||
sendCommand(command, sessionId) {
|
||||
return this.sendQuery("MessageHandlerFrameChild:sendCommand", {
|
||||
command,
|
||||
sessionId,
|
||||
});
|
||||
}
|
||||
|
||||
_onRegistryEvent(eventName, wrappedEvent) {
|
||||
this.sendAsyncMessage(
|
||||
"MessageHandlerFrameChild:messageHandlerEvent",
|
||||
|
|
|
@ -28,37 +28,12 @@ XPCOMUtils.defineLazyGetter(lazy, "WebDriverError", () => {
|
|||
export class MessageHandlerFrameParent extends JSWindowActorParent {
|
||||
async receiveMessage(message) {
|
||||
switch (message.name) {
|
||||
case "MessageHandlerFrameChild:messageHandlerEvent":
|
||||
const { name, contextInfo, data, sessionId } = message.data;
|
||||
const [moduleName] = name.split(".");
|
||||
|
||||
// Re-emit the event on the RootMessageHandler.
|
||||
const messageHandler = lazy.RootMessageHandlerRegistry.getExistingMessageHandler(
|
||||
sessionId
|
||||
);
|
||||
// TODO: getModuleInstance expects a CommandDestination in theory,
|
||||
// but only uses the MessageHandler type in practice, see Bug 1776389.
|
||||
const module = messageHandler.moduleCache.getModuleInstance(
|
||||
moduleName,
|
||||
{ type: lazy.WindowGlobalMessageHandler.type }
|
||||
);
|
||||
let eventPayload = data;
|
||||
|
||||
// Modify an event payload if there is a special method in the targeted module.
|
||||
// If present it can be found in windowglobal-in-root module.
|
||||
if (module?.interceptEvent) {
|
||||
eventPayload = await module.interceptEvent(name, data);
|
||||
|
||||
// Make sure that an event payload is returned.
|
||||
if (!eventPayload) {
|
||||
throw new Error(
|
||||
`${moduleName}.interceptEvent doesn't return the event payload`
|
||||
);
|
||||
}
|
||||
}
|
||||
messageHandler.emitEvent(name, eventPayload, contextInfo);
|
||||
|
||||
break;
|
||||
case "MessageHandlerFrameChild:sendCommand": {
|
||||
return this.#handleSendCommandMessage(message.data);
|
||||
}
|
||||
case "MessageHandlerFrameChild:messageHandlerEvent": {
|
||||
return this.#handleMessageHandlerEventMessage(message.data);
|
||||
}
|
||||
default:
|
||||
throw new Error("Unsupported message:" + message.name);
|
||||
}
|
||||
|
@ -96,4 +71,52 @@ export class MessageHandlerFrameParent extends JSWindowActorParent {
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
async #handleMessageHandlerEventMessage(messageData) {
|
||||
const { name, contextInfo, data, sessionId } = messageData;
|
||||
const [moduleName] = name.split(".");
|
||||
|
||||
// Re-emit the event on the RootMessageHandler.
|
||||
const messageHandler = lazy.RootMessageHandlerRegistry.getExistingMessageHandler(
|
||||
sessionId
|
||||
);
|
||||
// TODO: getModuleInstance expects a CommandDestination in theory,
|
||||
// but only uses the MessageHandler type in practice, see Bug 1776389.
|
||||
const module = messageHandler.moduleCache.getModuleInstance(moduleName, {
|
||||
type: lazy.WindowGlobalMessageHandler.type,
|
||||
});
|
||||
let eventPayload = data;
|
||||
|
||||
// Modify an event payload if there is a special method in the targeted module.
|
||||
// If present it can be found in windowglobal-in-root module.
|
||||
if (module?.interceptEvent) {
|
||||
eventPayload = await module.interceptEvent(name, data);
|
||||
|
||||
// Make sure that an event payload is returned.
|
||||
if (!eventPayload) {
|
||||
throw new Error(
|
||||
`${moduleName}.interceptEvent doesn't return the event payload`
|
||||
);
|
||||
}
|
||||
}
|
||||
messageHandler.emitEvent(name, eventPayload, contextInfo);
|
||||
}
|
||||
|
||||
async #handleSendCommandMessage(messageData) {
|
||||
const { sessionId, command } = messageData;
|
||||
const messageHandler = lazy.RootMessageHandlerRegistry.getExistingMessageHandler(
|
||||
sessionId
|
||||
);
|
||||
try {
|
||||
return await messageHandler.handleCommand(command);
|
||||
} catch (e) {
|
||||
if (e?.isRemoteError) {
|
||||
return {
|
||||
error: e.toJSON(),
|
||||
isMessageHandlerError: e.isMessageHandlerError,
|
||||
};
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче