Skip to content

Commit 63f961c

Browse files
Mehdi-Hpaklinker1
andauthored
feat(messaging): Add support for frameId (#88)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
1 parent 7c4ba67 commit 63f961c

File tree

5 files changed

+103
-25
lines changed

5 files changed

+103
-25
lines changed

docs/content/messaging/0.installation.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,9 @@ console.log(length); // 11
9191

9292
### Sending Messages to Tabs
9393

94-
You can also send messages from your background script to a tab, but you need to know the `tabId`.
94+
You can also send messages from your background script to a tab, but you need to know the `tabId`. This would send the message to all frames in the tab.
95+
96+
If you want to send a message to a specific frame, you can pass an object to `sendMessage` with the `tabId` and `frameId`.
9597

9698
::code-group
9799

@@ -107,6 +109,7 @@ onMessage('getStringLength', message => {
107109
import { sendMessage } from './messaging';
108110

109111
const length = await sendMessage('getStringLength', 'hello world', tabId);
112+
const length = await sendMessage('getStringLength', 'hello world', { tabId, frameId });
110113
```
111114

112115
::

docs/content/messaging/api.md

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ See [`@webext-core/messaging`](/messaging/installation/)
1313
```ts
1414
interface BaseMessagingConfig {
1515
logger?: Logger;
16+
breakError?: boolean;
1617
}
1718
```
1819

@@ -22,6 +23,8 @@ Shared configuration between all the different messengers.
2223

2324
- ***`logger?: Logger`*** (default: `console`)<br/>The logger to use when logging messages. Set to `null` to disable logging.
2425

26+
- ***`breakError?: boolean`*** (default: `undefined`)<br/>Whether to break an error when an invalid message is received.
27+
2528
## `CustomEventMessage`
2629

2730
```ts
@@ -87,6 +90,8 @@ websiteMessenger.sendMessage("initInjectedScript", ...);
8790
websiteMessenger.onMessage("initInjectedScript", (...) => {
8891
// ...
8992
})
93+
94+
*
9095
```
9196

9297
## `defineExtensionMessaging`
@@ -173,11 +178,13 @@ Messenger returned by `defineExtensionMessaging`.
173178
## `ExtensionSendMessageArgs`
174179

175180
```ts
176-
type ExtensionSendMessageArgs = [tabId?: number];
181+
type ExtensionSendMessageArgs = [arg?: number | SendMessageOptions];
177182
```
178183

179-
Send messsage accepts an additional, optional argument `tabId`. Pass it to send a message to a
180-
specific tab from the background script.
184+
Send message accepts either:
185+
- No arguments to send to background
186+
- A tabId number to send to a specific tab
187+
- A SendMessageOptions object to target a specific tab and frame
181188

182189
You cannot message between tabs directly. It must go through the background script.
183190

@@ -374,6 +381,23 @@ Call to ensure an active listener has been removed.
374381

375382
If the listener has already been removed with `Messenger.removeAllListeners`, this is a noop.
376383

384+
## `SendMessageOptions`
385+
386+
```ts
387+
interface SendMessageOptions {
388+
tabId: number;
389+
frameId?: number;
390+
}
391+
```
392+
393+
Options for sending a message to a specific tab/frame
394+
395+
### Properties
396+
397+
- ***`tabId: number`***<br/>The tab to send a message to
398+
399+
- ***`frameId?: number`***<br/>The frame to send a message to. 0 represents the main frame.
400+
377401
## `WindowMessagingConfig`
378402

379403
```ts

packages/messaging/src/extension.test-d.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { describe, expectTypeOf, it } from 'vitest';
22
import { MaybePromise, ProtocolWithReturn } from './types';
3-
import { defineExtensionMessaging } from './extension';
3+
import { defineExtensionMessaging, SendMessageOptions } from './extension';
44

55
describe('Messenger Typing', () => {
66
it('should use any for data and return type when a protocol map is not passed', () => {
@@ -73,7 +73,7 @@ describe('Messenger Typing', () => {
7373

7474
expectTypeOf(sendMessage).parameter(0).toMatchTypeOf<'ping'>();
7575
expectTypeOf(sendMessage).parameter(1).toBeUndefined();
76-
expectTypeOf(sendMessage).parameter(2).toEqualTypeOf<number | undefined>();
76+
expectTypeOf(sendMessage).parameter(2).toEqualTypeOf<number | undefined | SendMessageOptions>();
7777
});
7878

7979
it('should require passing undefined to sendMessage when there is no arguments in a function definition', () => {
@@ -83,6 +83,6 @@ describe('Messenger Typing', () => {
8383

8484
expectTypeOf(sendMessage).parameter(0).toMatchTypeOf<'ping'>();
8585
expectTypeOf(sendMessage).parameter(1).toBeUndefined();
86-
expectTypeOf(sendMessage).parameter(2).toEqualTypeOf<number | undefined>();
86+
expectTypeOf(sendMessage).parameter(2).toEqualTypeOf<number | undefined | SendMessageOptions>();
8787
});
8888
});

packages/messaging/src/extension.test.ts

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ describe('Messaging Wrapper', () => {
3434
expect(actual).toBe(expected);
3535
});
3636

37-
it('should send messages to tabs', async () => {
37+
it('should send messages to tabs with only tabId', async () => {
3838
const { sendMessage } = defineExtensionMessaging<ProtocolMap>();
3939
const input = 'test';
4040
const tabId = 0;
@@ -45,12 +45,40 @@ describe('Messaging Wrapper', () => {
4545

4646
expect(actual).toBe(expected);
4747
expect(fakeBrowser.tabs.sendMessage).toBeCalledTimes(1);
48-
expect(fakeBrowser.tabs.sendMessage).toBeCalledWith(tabId, {
49-
id: expect.any(Number),
50-
timestamp: expect.any(Number),
51-
type: 'getLength',
52-
data: input,
53-
});
48+
expect(fakeBrowser.tabs.sendMessage).toBeCalledWith(
49+
tabId,
50+
{
51+
id: expect.any(Number),
52+
timestamp: expect.any(Number),
53+
type: 'getLength',
54+
data: input,
55+
},
56+
undefined,
57+
);
58+
});
59+
60+
it('should send messages to tabs with tabId and frameId', async () => {
61+
const { sendMessage } = defineExtensionMessaging<ProtocolMap>();
62+
const input = 'test';
63+
const tabId = 0;
64+
const frameId = 0;
65+
const expected = 4;
66+
vi.spyOn(fakeBrowser.tabs, 'sendMessage').mockResolvedValueOnce({ res: expected });
67+
68+
const actual = await sendMessage('getLength', input, { tabId, frameId });
69+
70+
expect(actual).toBe(expected);
71+
expect(fakeBrowser.tabs.sendMessage).toBeCalledTimes(1);
72+
expect(fakeBrowser.tabs.sendMessage).toBeCalledWith(
73+
tabId,
74+
{
75+
id: expect.any(Number),
76+
timestamp: expect.any(Number),
77+
type: 'getLength',
78+
data: input,
79+
},
80+
{ frameId },
81+
);
5482
});
5583

5684
it('should handle errors', async () => {

packages/messaging/src/extension.ts

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,28 @@ export interface ExtensionMessage {
1919
}
2020

2121
/**
22-
* Send messsage accepts an additional, optional argument `tabId`. Pass it to send a message to a
23-
* specific tab from the background script.
24-
*
25-
* You cannot message between tabs directly. It must go through the background script.
22+
* Options for sending a message to a specific tab/frame
2623
*/
27-
export type ExtensionSendMessageArgs = [
24+
export interface SendMessageOptions {
25+
/**
26+
* The tab to send a message to
27+
*/
28+
tabId: number;
2829
/**
29-
* The tab to send a message to.
30+
* The frame to send a message to. 0 represents the main frame.
3031
*/
31-
tabId?: number,
32-
];
32+
frameId?: number;
33+
}
34+
35+
/**
36+
* Send message accepts either:
37+
* - No arguments to send to background
38+
* - A tabId number to send to a specific tab
39+
* - A SendMessageOptions object to target a specific tab and frame
40+
*
41+
* You cannot message between tabs directly. It must go through the background script.
42+
*/
43+
export type ExtensionSendMessageArgs = [arg?: number | SendMessageOptions];
3344

3445
/**
3546
* Messenger returned by `defineExtensionMessaging`.
@@ -51,9 +62,21 @@ export function defineExtensionMessaging<
5162
>(config?: ExtensionMessagingConfig): ExtensionMessenger<TProtocolMap> {
5263
return defineGenericMessanging({
5364
...config,
54-
sendMessage(message, tabId) {
55-
if (tabId == null) return Browser.runtime.sendMessage(message);
56-
return Browser.tabs.sendMessage(tabId, message);
65+
sendMessage(message, arg) {
66+
// No args - send to background
67+
if (arg == null) {
68+
return Browser.runtime.sendMessage(message);
69+
}
70+
71+
// Handle both number and options object
72+
const options: SendMessageOptions = typeof arg === 'number' ? { tabId: arg } : arg;
73+
74+
return Browser.tabs.sendMessage(
75+
options.tabId,
76+
message,
77+
// Pass frameId if specified
78+
options.frameId != null ? { frameId: options.frameId } : undefined,
79+
);
5780
},
5881
addRootListener(processMessage) {
5982
const listener = (message: any, sender: Runtime.MessageSender) => {

0 commit comments

Comments
 (0)