2017-10-23 09:42:50 +01:00
|
|
|
export type listener = (message: Message, sender?, sendResponse?) => void|any
|
|
|
|
|
|
|
|
/** await a promise and console.error and rethrow if it errors
|
|
|
|
|
|
|
|
Errors from promises don't get logged unless you seek them out.
|
|
|
|
*/
|
|
|
|
async function l(promise) {
|
|
|
|
try {
|
|
|
|
return await promise
|
|
|
|
} catch (e) {
|
|
|
|
console.error(e)
|
|
|
|
throw e
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const listeners = new Map<string, Set<listener>>()
|
2017-10-05 23:11:56 +01:00
|
|
|
|
|
|
|
/** Register a listener to be called for each message with type */
|
2017-10-23 09:42:50 +01:00
|
|
|
export function addListener(type, callback: listener) {
|
2017-10-05 23:11:56 +01:00
|
|
|
if (!listeners.get(type)) {
|
|
|
|
listeners.set(type, new Set())
|
|
|
|
}
|
|
|
|
listeners.get(type).add(callback)
|
|
|
|
return () => { listeners.get(type).delete(callback) }
|
|
|
|
}
|
|
|
|
|
2017-10-23 09:42:50 +01:00
|
|
|
function onMessage(message, sender, sendResponse) {
|
2017-10-05 23:11:56 +01:00
|
|
|
if (listeners.get(message.type)) {
|
|
|
|
for (let listener of listeners.get(message.type)) {
|
2017-10-23 09:42:50 +01:00
|
|
|
listener(message, sender, sendResponse)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calls methods on obj that match .command and sends responses back
|
|
|
|
export function attributeCaller(obj) {
|
|
|
|
function handler(message: Message, sender, sendResponse) {
|
|
|
|
console.log(message)
|
|
|
|
|
|
|
|
// Args may be undefined, but you can't spread undefined...
|
|
|
|
if (message.args === undefined) message.args = []
|
|
|
|
|
|
|
|
// Call command on obj
|
|
|
|
let response = obj[message.command](...message.args)
|
|
|
|
|
|
|
|
// Return response to sender
|
|
|
|
if (response instanceof Promise) {
|
|
|
|
return response
|
|
|
|
} else {
|
|
|
|
sendResponse(response)
|
2017-10-05 23:11:56 +01:00
|
|
|
}
|
|
|
|
}
|
2017-10-23 09:42:50 +01:00
|
|
|
return handler
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Send a message to non-content scripts */
|
|
|
|
export async function message(type, command, args?) {
|
|
|
|
// One day typescript will be smart enough to back propagate this cast.
|
|
|
|
return await l(browser.runtime.sendMessage({type, command, args} as Message))
|
2017-10-05 23:11:56 +01:00
|
|
|
}
|
|
|
|
|
2017-10-23 09:42:50 +01:00
|
|
|
/** The first active tab in the currentWindow.
|
|
|
|
*
|
|
|
|
* TODO: Highlander theory: Can there ever be more than one?
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
//#background_helper
|
|
|
|
async function activeTab() {
|
|
|
|
return (await l(browser.tabs.query({active: true, currentWindow: true})))[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
//#background_helper
|
|
|
|
async function activeTabID() {
|
|
|
|
return (await activeTab()).id
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Message the active tab of the currentWindow */
|
|
|
|
//#background_helper
|
|
|
|
export async function messageActiveTab(type, command: string, args?: any[]) {
|
|
|
|
messageTab(await activeTabID(), type, command, args)
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function messageTab(tabId, type, command, args?) {
|
|
|
|
let message: Message = {
|
|
|
|
type,
|
|
|
|
command,
|
|
|
|
args,
|
|
|
|
}
|
|
|
|
l(browser.tabs.sendMessage(tabId, message))
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function messageAllTabs(type, command: string, args?: any[]) {
|
|
|
|
for (let tab of await browser.tabs.query({})) {
|
|
|
|
messageTab(tab.id, type, command, args)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-10-05 23:11:56 +01:00
|
|
|
browser.runtime.onMessage.addListener(onMessage)
|