@@ -14,6 +14,8 @@ import { getActionForMessage } from "./targetLogic.js";
1414import { didUserRegisterMethods , handlers } from "./handlers.js" ;
1515import { getTabDataStatus , thisTarget } from "./thisTarget.js" ;
1616
17+ type SendResponse = ( response : unknown ) => void ;
18+
1719export function isMessengerMessage ( message : unknown ) : message is Message {
1820 return (
1921 isObject ( message ) &&
@@ -23,10 +25,17 @@ export function isMessengerMessage(message: unknown): message is Message {
2325 ) ;
2426}
2527
28+ /**
29+ * Decides what to do with a message and sends a response (value or error) back to the sender.
30+ *
31+ * @warn This function cannot return a Promise.
32+ * @warn Limit the amount of logic here because errors won't make it to `sendResponse`
33+ */
34+ //
2635function onMessageListener (
2736 message : unknown ,
2837 sender : Sender ,
29- sendResponse : ( response : unknown ) => void ,
38+ sendResponse : SendResponse ,
3039) : true | undefined {
3140 if ( ! isMessengerMessage ( message ) ) {
3241 // TODO: Add test for this eventuality: ignore unrelated messages
@@ -44,74 +53,64 @@ function onMessageListener(
4453 return ;
4554 }
4655
56+ const { type, target, args, options = { } } = message ;
57+ const { trace = [ ] , seq } = options ;
58+
59+ if ( action === "forward" ) {
60+ log . debug ( type , seq , "🔀 forwarded" , { sender, target } ) ;
61+ } else {
62+ log . debug ( type , seq , "↘️ received in" , getContextName ( ) , {
63+ sender,
64+ args,
65+ wasForwarded : trace . length > 1 ,
66+ } ) ;
67+ }
68+
69+ // Prepare the response asynchronously because the listener must return `true` synchronously
4770 ( async ( ) => {
4871 try {
49- sendResponse ( await handleMessage ( message , sender , action ) ) ;
72+ trace . push ( sender ) ;
73+
74+ const value = await prepareResponse ( message , action , { trace } ) ;
75+ log . debug ( type , seq , "↗️ responding" , { value } ) ;
76+ sendResponse ( { __webextMessenger, value } ) ;
5077 } catch ( error ) {
51- sendResponse ( { __webextMessenger : true , error : serializeError ( error ) } ) ;
78+ log . debug ( type , seq , "↗️ responding" , { error } ) ;
79+ sendResponse ( { __webextMessenger, error : serializeError ( error ) } ) ;
5280 }
5381 } ) ( ) ;
5482
55- // Make `sendMessage` wait for an async response. This stops other `onMessage` listeners from being called.
83+ // This indicates that the message is being handled and a response will be sent asynchronously
5684 // TODO: Just return a promise if this is ever implemented https://issues.chromium.org/issues/40753031
5785 return true ;
5886}
5987
60- // This function can only be called when the message *will* be handled locally.
61- // Returning "undefined" or throwing an error will still handle it.
62- async function handleMessage (
88+ /** Generates the value or error to return to the sender; does not include further messaging logic */
89+ async function prepareResponse (
6390 message : Message ,
64- sender : Sender ,
65-
66- // Once messages reach this function they cannot be "ignored", they're already being handled
6791 action : "respond" | "forward" ,
92+ meta : MessengerMeta ,
6893) : Promise < unknown > {
69- const { type, target, args, options = { } } = message ;
70-
71- const { trace = [ ] , seq } = options ;
72- trace . push ( sender ) ;
73- const meta : MessengerMeta = { trace } ;
74-
75- let handleMessage : ( ) => Promise < unknown > ;
94+ const { type, target, args } = message ;
7695
7796 if ( action === "forward" ) {
78- log . debug ( type , seq , "🔀 forwarded" , { sender, target } ) ;
79- handleMessage = async ( ) => messenger ( type , meta , target , ...args ) ;
80- } else {
81- log . debug ( type , seq , "↘️ received in" , getContextName ( ) , {
82- sender,
83- args,
84- wasForwarded : trace . length > 1 ,
85- } ) ;
86-
87- const localHandler = handlers . get ( type ) ;
88- if ( ! localHandler ) {
89- if ( ! didUserRegisterMethods ( ) ) {
90- // TODO: Test the handling of __getTabData in contexts that have no registered methods
91- // https://github.com/pixiebrix/webext-messenger/pull/82
92- throw new MessengerError (
93- `No handlers registered in ${ getContextName ( ) } ` ,
94- ) ;
95- }
96-
97- throw new MessengerError (
98- `No handler registered for ${ type } in ${ getContextName ( ) } ` ,
99- ) ;
100- }
97+ return messenger ( type , meta , target , ...args ) ;
98+ }
10199
102- handleMessage = async ( ) => localHandler . apply ( meta , args ) ;
100+ const localHandler = handlers . get ( type ) ;
101+ if ( localHandler ) {
102+ return localHandler . apply ( meta , args ) ;
103103 }
104104
105- const response = await handleMessage ( ) . then (
106- ( value ) => ( { value } ) ,
107- ( error : unknown ) => ( {
108- // Errors must be serialized because the stack traces are currently lost on Chrome
109- error : serializeError ( error ) ,
110- } ) ,
111- ) ;
105+ if ( didUserRegisterMethods ( ) ) {
106+ throw new MessengerError (
107+ `No handler registered for ${ type } in ${ getContextName ( ) } ` ,
108+ ) ;
109+ }
112110
113- log . debug ( type , seq , "↗️ responding" , response ) ;
114- return { ...response , __webextMessenger } ;
111+ // TODO: Test the handling of __getTabData in contexts that have no registered methods
112+ // https://github.com/pixiebrix/webext-messenger/pull/82
113+ throw new MessengerError ( `No handlers registered in ${ getContextName ( ) } ` ) ;
115114}
116115
117116export function registerMethods ( methods : Partial < MessengerMethods > ) : void {
0 commit comments