2018-04-13 19:28:03 +01:00
|
|
|
import { l, browserBg, activeTabId } from "./lib/webext"
|
|
|
|
import Logger from "./logging"
|
|
|
|
const logger = new Logger("messaging")
|
2017-11-09 00:41:07 +00:00
|
|
|
|
2017-11-11 02:10:23 +00:00
|
|
|
export type TabMessageType =
|
2018-04-13 19:28:03 +01:00
|
|
|
| "excmd_content"
|
|
|
|
| "keydown_content"
|
|
|
|
| "commandline_content"
|
|
|
|
| "commandline_frame"
|
|
|
|
| "hinting_content"
|
|
|
|
| "finding_content"
|
2017-11-11 02:10:23 +00:00
|
|
|
export type NonTabMessageType =
|
2018-04-13 19:28:03 +01:00
|
|
|
| "keydown_background"
|
|
|
|
| "commandline_background"
|
|
|
|
| "browser_proxy_background"
|
|
|
|
| "download_background"
|
2017-10-28 05:11:10 +01:00
|
|
|
export type MessageType = TabMessageType | NonTabMessageType
|
|
|
|
|
|
|
|
export interface Message {
|
|
|
|
type: MessageType
|
|
|
|
// and other unknown attributes...
|
|
|
|
[key: string]: any
|
|
|
|
}
|
|
|
|
|
2018-04-13 19:28:03 +01:00
|
|
|
export type listener = (
|
|
|
|
message: Message,
|
|
|
|
sender?,
|
|
|
|
sendResponse?,
|
|
|
|
) => void | Promise<any>
|
2017-10-23 09:42:50 +01:00
|
|
|
|
|
|
|
// Calls methods on obj that match .command and sends responses back
|
|
|
|
export function attributeCaller(obj) {
|
|
|
|
function handler(message: Message, sender, sendResponse) {
|
2017-12-30 00:46:26 +00:00
|
|
|
logger.debug(message)
|
2017-10-23 09:42:50 +01:00
|
|
|
|
|
|
|
// Args may be undefined, but you can't spread undefined...
|
|
|
|
if (message.args === undefined) message.args = []
|
|
|
|
|
|
|
|
// Call command on obj
|
2017-10-28 05:11:10 +01:00
|
|
|
try {
|
|
|
|
let response = obj[message.command](...message.args)
|
|
|
|
|
|
|
|
// Return response to sender
|
|
|
|
if (response instanceof Promise) {
|
2017-12-30 00:46:26 +00:00
|
|
|
logger.debug("Returning promise...", response)
|
2017-11-11 02:10:23 +00:00
|
|
|
sendResponse(response)
|
|
|
|
// Docs say you should be able to return a promise, but that
|
|
|
|
// doesn't work.
|
|
|
|
/* return response */
|
|
|
|
} else if (response !== undefined) {
|
2017-12-30 00:46:26 +00:00
|
|
|
logger.debug("Returning synchronously...", response)
|
2017-10-28 05:11:10 +01:00
|
|
|
sendResponse(response)
|
|
|
|
}
|
|
|
|
} catch (e) {
|
2018-04-13 19:28:03 +01:00
|
|
|
logger.error(
|
|
|
|
`Error processing ${message.command}(${message.args})`,
|
|
|
|
e,
|
|
|
|
)
|
|
|
|
return new Promise((resolve, error) => error(e))
|
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 */
|
2017-10-28 05:11:10 +01:00
|
|
|
export async function message(type: NonTabMessageType, command, args?) {
|
2017-10-23 09:42:50 +01:00
|
|
|
// One day typescript will be smart enough to back propagate this cast.
|
2018-04-13 19:28:03 +01:00
|
|
|
return 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
|
|
|
/** Message the active tab of the currentWindow */
|
|
|
|
//#background_helper
|
2018-04-13 19:28:03 +01:00
|
|
|
export async function messageActiveTab(
|
|
|
|
type: TabMessageType,
|
|
|
|
command: string,
|
|
|
|
args?: any[],
|
|
|
|
) {
|
2017-11-09 00:41:07 +00:00
|
|
|
return messageTab(await activeTabId(), type, command, args)
|
2017-10-23 09:42:50 +01:00
|
|
|
}
|
|
|
|
|
2017-10-28 05:11:10 +01:00
|
|
|
export async function messageTab(tabId, type: TabMessageType, command, args?) {
|
2017-10-23 09:42:50 +01:00
|
|
|
let message: Message = {
|
|
|
|
type,
|
|
|
|
command,
|
|
|
|
args,
|
|
|
|
}
|
2017-11-22 19:09:52 +00:00
|
|
|
return l(browserBg.tabs.sendMessage(tabId, message))
|
2017-10-23 09:42:50 +01:00
|
|
|
}
|
|
|
|
|
2018-04-13 19:28:03 +01:00
|
|
|
export async function messageAllTabs(
|
|
|
|
type: TabMessageType,
|
|
|
|
command: string,
|
|
|
|
args?: any[],
|
|
|
|
) {
|
2017-10-28 05:11:10 +01:00
|
|
|
let responses = []
|
2017-11-22 19:09:52 +00:00
|
|
|
for (let tab of await browserBg.tabs.query({})) {
|
2018-04-13 19:28:03 +01:00
|
|
|
try {
|
|
|
|
responses.push(await messageTab(tab.id, type, command, args))
|
|
|
|
} catch (e) {
|
|
|
|
logger.error(e)
|
|
|
|
}
|
2017-10-28 05:11:10 +01:00
|
|
|
}
|
|
|
|
return responses
|
|
|
|
}
|
|
|
|
|
|
|
|
const listeners = new Map<string, Set<listener>>()
|
|
|
|
|
|
|
|
/** Register a listener to be called for each message with type */
|
|
|
|
export function addListener(type: MessageType, callback: listener) {
|
|
|
|
if (!listeners.get(type)) {
|
|
|
|
listeners.set(type, new Set())
|
|
|
|
}
|
|
|
|
listeners.get(type).add(callback)
|
2018-04-13 19:28:03 +01:00
|
|
|
return () => {
|
|
|
|
listeners.get(type).delete(callback)
|
|
|
|
}
|
2017-10-28 05:11:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Recv a message from runtime.onMessage and send to all listeners */
|
|
|
|
function onMessage(message, sender, sendResponse) {
|
|
|
|
if (listeners.get(message.type)) {
|
|
|
|
for (let listener of listeners.get(message.type)) {
|
|
|
|
listener(message, sender, sendResponse)
|
|
|
|
}
|
2017-10-23 09:42:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-05 23:11:56 +01:00
|
|
|
browser.runtime.onMessage.addListener(onMessage)
|