// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview A collection of functions and behaviors helpful for message
* passing between renderers.
*/
type MessageSender = chrome.runtime.MessageSender;
type TargetHandlers = Record<string, Function>;
/** Targets should be constants, defined in a central place. */
export type TargetType = string;
/** Actions should be constants, defined in a central place. */
export type ActionType = string;
export class BridgeHelper {
/**
* This function should only be used by Bridges (e.g. BackgroundBridge,
* PanelBridge) and not called directly by other classes.
*
* @param target The name of the class that will handle this request.
* @param action The name of the intended function or, if not a direct method of the class,
* a pseudo-function name.
*
* Any arguments to be passed through can be appended to the function. They
* must be convertible to JSON objects for message passing - i.e., no
* functions or type information will be retained, and no direct
* modification of the original context is possible.
*
* @return A promise, that resolves when the handler function has finished and
* contains any value returned by the handler.
*/
static sendMessage(target: TargetType, action: ActionType, ...args: any[]):
Promise<any> {
return new Promise(
resolve => chrome.runtime.sendMessage(
undefined, {target, action, args}, undefined, resolve));
}
static clearAllHandlersForTarget(target: TargetType): void {
handlers[target] = {};
}
/**
* @param target The name of the class that is registering the handler.
* @param action The name of the intended function or, if not a direct method
* of the class, a pseudo-function name.
* @param handler A function that performs the indicated action. It may
* optionally take parameters, and may have an optional return value
* that will be forwarded to the requestor.
*/
static registerHandler(
target: TargetType, action: ActionType, handler: Function): void {
if (!target || !action) {
return;
}
if (!handlers[target]) {
handlers[target] = {};
}
if (handlers[target][action]) {
throw new Error(
`Re-assigning handlers for ${target}.${action} is not permitted`);
}
handlers[target][action] = handler;
}
}
// Local to module.
const handlers: Record<TargetType, TargetHandlers> = {};
chrome.runtime.onMessage.addListener(
(message: any, _sender: MessageSender, respond: (value: any) => void) => {
const targetHandlers = handlers[message.target];
if (!targetHandlers || !targetHandlers[message.action]) {
return false;
}
const handler = targetHandlers[message.action];
Promise.resolve(handler(...message.args)).then(respond);
return true; /** Wait for asynchronous response. */
});