@@ -291,13 +291,16 @@ async function nativeChecks() {
291291}
292292
293293/**
294- * handles messages sent with browser.runtime.sendMessage
294+ * Handles messages sent with `browser.runtime.sendMessage`
295+ * Make sure not to return `undefined` or `rejection`, otherwise the reply may never be delivered
295296 * @see {@link https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onMessage#listener }
296297 * @type {Parameters<typeof browser.runtime.onMessage.addListener>[0] }
297- * @param { (response: any) => void } sendResponse send a response to the message
298+ * @returns { Promise<{status: "pending"|"fulfilled"|"rejected", result: any}> }
298299 */
299- async function handleMessage ( request , sender , sendResponse ) {
300- switch ( request . name ) {
300+ async function handleMessage ( message , sender ) {
301+ /** @type {Promise } */
302+ let promise ;
303+ switch ( message . name ) {
301304 case "REQ_USERSCRIPTS" : {
302305 // get the page url from the content script that sent request
303306 const url = sender . url ;
@@ -306,73 +309,75 @@ async function handleMessage(request, sender, sendResponse) {
306309 const isTop = sender . frameId === 0 ;
307310 // send request to swift layer to provide code for page url
308311 const message = { name : "REQ_USERSCRIPTS" , url, isTop } ;
309- const response = await sendNativeMessage ( message ) ;
310- if ( import . meta. env . MODE === "development" ) {
311- console . debug ( "REQ_USERSCRIPTS" , message , response ) ;
312+ try {
313+ const response = await sendNativeMessage ( message ) ;
314+ if ( import . meta. env . MODE === "development" ) {
315+ console . debug ( "REQ_USERSCRIPTS" , message , response ) ;
316+ }
317+ // if request failed, send error to content script for logging
318+ if ( response . error ) return response ;
319+ // sort files
320+ response . files . js . sort ( userscriptSort ) ;
321+ response . files . css . sort ( ( a , b ) => Number ( a . weight ) < Number ( b . weight ) ) ;
322+ // return sorted files for injection
323+ return response ;
324+ } catch ( error ) {
325+ console . error ( error ) ;
326+ // @ts -ignore -- ignore for now and will reconstruct this in the future.
327+ return { error } ;
312328 }
313- // if request failed, send error to content script for logging
314- if ( response . error ) return sendResponse ( response ) ;
315- // sort files
316- response . files . js . sort ( userscriptSort ) ;
317- response . files . css . sort ( ( a , b ) => {
318- return Number ( a . weight ) < Number ( b . weight ) ;
319- } ) ;
320- // return sorted files for injection
321- return response ;
322329 }
323330 case "API_CLOSE_TAB" : {
324- const tabId = request . tabId || sender . tab . id ;
325- return browser . tabs . remove ( tabId ) ;
331+ promise = browser . tabs . remove ( message . tabId || sender . tab . id ) ;
332+ break ;
326333 }
327334 case "API_OPEN_TAB" : {
328- const props = {
329- active : request . active ,
335+ promise = browser . tabs . create ( {
336+ active : message . active ,
330337 index : sender . tab . index + 1 ,
331- url : request . url ,
332- } ;
333- return browser . tabs . create ( props ) ;
338+ url : message . url ,
339+ } ) ;
340+ break ;
334341 }
335342 case "API_ADD_STYLE" : {
336- const tabId = sender . tab . id ;
337- /** @type {import("webextension-polyfill").ExtensionTypes.InjectDetails } */
338- const details = { code : request . css , cssOrigin : "user" } ;
339- return browser . tabs . insertCSS ( tabId , details ) ;
343+ promise = browser . tabs . insertCSS ( sender . tab . id , {
344+ code : message . css ,
345+ cssOrigin : "user" ,
346+ } ) ;
347+ break ;
340348 }
341349 case "API_GET_TAB" : {
342- let tab = null ;
343- if ( typeof sender . tab !== "undefined" ) {
350+ if ( typeof sender . tab === "undefined" ) {
351+ const error = "unable to deliver tab due to empty tab id" ;
352+ return { status : "rejected" , result : error } ;
353+ }
354+ try {
344355 const tabData = sessionStorage . getItem ( `tab-${ sender . tab . id } ` ) ;
345- try {
346- // if tabData is null, can still parse it and return that
347- tab = JSON . parse ( tabData ) ;
348- } catch ( error ) {
349- console . error ( "failed to parse tab data for getTab" , error ) ;
350- }
351- } else {
352- console . error ( "unable to deliver tab due to empty tab id" ) ;
356+ // if tabData is null, can still parse it and return that
357+ const tabObj = JSON . parse ( tabData ) ;
358+ return { status : "fulfilled" , result : tabObj } ;
359+ } catch ( error ) {
360+ console . error ( "failed to parse tab data for getTab" , error ) ;
361+ return { status : "rejected" , result : error } ;
353362 }
354- sendResponse ( tab == null ? { } : tab ) ;
355- break ;
356363 }
357364 case "API_SAVE_TAB" : {
358365 if ( sender . tab != null && sender . tab . id ) {
359366 const key = `tab-${ sender . tab . id } ` ;
360- sessionStorage . setItem ( key , JSON . stringify ( request . tab ) ) ;
361- sendResponse ( { success : true } ) ;
367+ sessionStorage . setItem ( key , JSON . stringify ( message . tabObj ) ) ;
368+ return { status : "fulfilled" , result : undefined } ;
362369 } else {
363- console . error ( "unable to save tab, empty tab id" ) ;
364- sendResponse ( { success : false } ) ;
370+ const error = "unable to save tab, empty tab id" ;
371+ return { status : "rejected" , result : error } ;
365372 }
366- break ;
367373 }
368374 case "API_SET_CLIPBOARD" : {
369- const result = setClipboard ( request . clipboardData , request . type ) ;
370- sendResponse ( result ) ;
371- break ;
375+ const result = setClipboard ( message . clipboardData , message . type ) ;
376+ return { status : "fulfilled" , result } ;
372377 }
373378 case "API_XHR" : {
374379 // parse details and set up for XMLHttpRequest
375- const details = request . details ;
380+ const details = message . details ;
376381 const method = details . method ? details . method : "GET" ;
377382 const user = details . user || null ;
378383 const password = details . password || null ;
@@ -387,7 +392,7 @@ async function handleMessage(request, sender, sendResponse) {
387392 }
388393 // establish a long-lived port connection to content script
389394 const port = browser . tabs . connect ( sender . tab . id , {
390- name : request . xhrPortName ,
395+ name : message . xhrPortName ,
391396 } ) ;
392397 // set up XMLHttpRequest
393398 const xhr = new XMLHttpRequest ( ) ;
@@ -397,7 +402,7 @@ async function handleMessage(request, sender, sendResponse) {
397402 xhr . overrideMimeType ( details . overrideMimeType ) ;
398403 }
399404 // add required listeners and send result back to the content script
400- for ( const e of request . events ) {
405+ for ( const e of message . events ) {
401406 if ( ! details [ e ] ) continue ;
402407 xhr [ e ] = async ( event ) => {
403408 // can not send xhr through postMessage
@@ -487,6 +492,13 @@ async function handleMessage(request, sender, sendResponse) {
487492 break ;
488493 }
489494 }
495+ try {
496+ const result = await promise ;
497+ return { status : "fulfilled" , result } ;
498+ } catch ( error ) {
499+ console . error ( message , sender , error ) ;
500+ return { status : "rejected" , result : error } ;
501+ }
490502}
491503browser . runtime . onInstalled . addListener ( async ( ) => {
492504 await nativeChecks ( ) ;
0 commit comments