-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement "to content script" via background page #29
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -53,8 +53,14 @@ type Method = (this: MessengerMeta, ...args: Arguments) => Promise<unknown>; | |
|
||
// TODO: It may include additional meta, like information about the original sender | ||
type Message<TArguments extends Arguments = Arguments> = { | ||
type: string; | ||
type: keyof MessengerMethods; | ||
args: TArguments; | ||
|
||
/** If the message is being sent to an intermediary receiver, also set the target */ | ||
target?: Target; | ||
|
||
/** If the message is being sent to an intermediary receiver, also set the options */ | ||
options?: Target; | ||
}; | ||
|
||
type MessengerMessage = Message & { | ||
|
@@ -82,20 +88,14 @@ function isMessengerResponse(response: unknown): response is MessengerResponse { | |
|
||
const handlers = new Map<string, Method>(); | ||
|
||
async function handleMessage( | ||
async function handleCall( | ||
message: Message, | ||
sender: MessengerMeta | ||
sender: MessengerMeta, | ||
call: Promise<unknown> | unknown | ||
): Promise<MessengerResponse> { | ||
const handler = handlers.get(message.type); | ||
if (!handler) { | ||
throw new Error("No handler registered for " + message.type); | ||
} | ||
|
||
console.debug(`Messenger:`, message.type, message.args, "from", { sender }); | ||
// The handler could actually be a synchronous function | ||
const response = await Promise.resolve( | ||
handler.call(sender, ...message.args) | ||
).then( | ||
const response = await Promise.resolve(call).then( | ||
(value) => ({ value }), | ||
(error: unknown) => ({ | ||
// Errors must be serialized because the stacktraces are currently lost on Chrome and | ||
|
@@ -108,6 +108,27 @@ async function handleMessage( | |
return { ...response, __webext_messenger__ }; | ||
} | ||
|
||
async function handleMessage( | ||
message: Message, | ||
sender: MessengerMeta | ||
): Promise<MessengerResponse> { | ||
if (message.target) { | ||
const publicMethod = getContentScriptMethod(message.type); | ||
return handleCall( | ||
message, | ||
sender, | ||
publicMethod(message.target, ...message.args) | ||
); | ||
} | ||
Comment on lines
+115
to
+122
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the core of the forwarder at this point. Information about the original sender is not preserved yet. I think I will turn |
||
|
||
const handler = handlers.get(message.type); | ||
if (handler) { | ||
return handleCall(message, sender, handler.apply(sender, message.args)); | ||
} | ||
|
||
throw new Error("No handler registered for " + message.type); | ||
} | ||
|
||
// Do not turn this into an `async` function; Notifications must turn `void` | ||
function manageConnection( | ||
type: string, | ||
|
@@ -179,11 +200,16 @@ interface Options { | |
isNotification?: boolean; | ||
} | ||
|
||
function makeMessage(type: string, args: unknown[]): MessengerMessage { | ||
function makeMessage( | ||
type: keyof MessengerMethods, | ||
args: unknown[], | ||
target?: Target | ||
): MessengerMessage { | ||
return { | ||
__webext_messenger__, | ||
type, | ||
args, | ||
target, | ||
}; | ||
} | ||
|
||
|
@@ -210,13 +236,16 @@ function getContentScriptMethod< | |
TPublicMethod extends PublicMethodWithTarget<TMethod> | ||
>(type: TType, options: Options = {}): TPublicMethod { | ||
const publicMethod = (target: Target, ...args: Parameters<TMethod>) => { | ||
const sendMessage = async () => | ||
browser.tabs.sendMessage( | ||
target.tabId, | ||
makeMessage(type, args), | ||
// `frameId` must be specified. If missing, the message would be sent to every frame | ||
{ frameId: target.frameId ?? 0 } | ||
); | ||
// eslint-disable-next-line no-negated-condition -- Looks better | ||
const sendMessage = !browser.tabs | ||
? async () => browser.runtime.sendMessage(makeMessage(type, args, target)) | ||
: async () => | ||
browser.tabs.sendMessage( | ||
target.tabId, | ||
makeMessage(type, args), | ||
// `frameId` must be specified. If missing, the message is sent to every frame | ||
{ frameId: target.frameId ?? 0 } | ||
); | ||
Comment on lines
+240
to
+248
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the current "routing" implementation. Since |
||
|
||
return manageConnection(type, options, sendMessage); | ||
}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
export async function ensureScripts(tabId: number): Promise<void> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hey this looks familiar! |
||
await browser.tabs.executeScript(tabId, { | ||
// https://github.com/parcel-bundler/parcel/issues/5758 | ||
file: | ||
"/up_/up_/node_modules/webextension-polyfill/dist/browser-polyfill.js", | ||
}); | ||
await browser.tabs.executeScript(tabId, { | ||
file: "contentscript/registration.js", | ||
}); | ||
} | ||
|
||
export async function getAllFrames( | ||
tabId: number | ||
): Promise<[parentFrame: number, iframe: number]> { | ||
const [parentFrame, iframe] = await browser.webNavigation.getAllFrames({ | ||
tabId, | ||
}); | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
return [parentFrame!.frameId, iframe!.frameId]; | ||
} | ||
|
||
export async function openTab(url: string): Promise<number> { | ||
const tab = await browser.tabs.create({ | ||
active: false, | ||
url, | ||
}); | ||
return tab.id!; | ||
} | ||
|
||
export async function closeTab(tabId: number): Promise<void> { | ||
await browser.tabs.remove(tabId); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Notification forwarding hasn't been implemented yet, so this is currently unused. I'll implement them separately