webext-messenger 0.15.0-2 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/distribution/receiver.js
CHANGED
@@ -2,7 +2,7 @@ import browser from "webextension-polyfill";
|
|
2
2
|
import { serializeError } from "serialize-error";
|
3
3
|
import { messenger } from "./sender.js";
|
4
4
|
import { handlers, isObject, MessengerError, debug, __webextMessenger, } from "./shared.js";
|
5
|
-
import { getContextName } from "webext-detect-page";
|
5
|
+
import { getContextName, isBackgroundPage } from "webext-detect-page";
|
6
6
|
import { getActionForMessage, nameThisTarget } from "./thisTarget.js";
|
7
7
|
export function isMessengerMessage(message) {
|
8
8
|
return (isObject(message) &&
|
@@ -16,8 +16,8 @@ function onMessageListener(message, sender) {
|
|
16
16
|
// TODO: Add test for this eventuality: ignore unrelated messages
|
17
17
|
return;
|
18
18
|
}
|
19
|
-
// Target check must be synchronous (`await` means we're
|
20
|
-
const action = getActionForMessage(message.target);
|
19
|
+
// Target check must be synchronous (`await` means we're handling the message)
|
20
|
+
const action = getActionForMessage(sender, message.target);
|
21
21
|
if (action === "ignore") {
|
22
22
|
return;
|
23
23
|
}
|
@@ -25,7 +25,9 @@ function onMessageListener(message, sender) {
|
|
25
25
|
}
|
26
26
|
// This function can only be called when the message *will* be handled locally.
|
27
27
|
// Returning "undefined" or throwing an error will still handle it.
|
28
|
-
async function handleMessage(message, sender,
|
28
|
+
async function handleMessage(message, sender,
|
29
|
+
// Once messages reach this function they cannot be "ignored", they're already being handled
|
30
|
+
action) {
|
29
31
|
const { type, target, args, options: { trace } = {} } = message;
|
30
32
|
debug(type, "↘️ received", { sender, args });
|
31
33
|
let handleMessage;
|
@@ -42,19 +44,18 @@ async function handleMessage(message, sender, action) {
|
|
42
44
|
const meta = { trace: [sender] };
|
43
45
|
handleMessage = async () => localHandler.apply(meta, args);
|
44
46
|
}
|
45
|
-
|
46
|
-
|
47
|
-
// Errors must be serialized because the stacktraces are currently lost on Chrome
|
47
|
+
const response = await handleMessage().then((value) => ({ value }), (error) => ({
|
48
|
+
// Errors must be serialized because the stack traces are currently lost on Chrome
|
48
49
|
// and https://github.com/mozilla/webextension-polyfill/issues/210
|
49
50
|
error: serializeError(error),
|
50
|
-
}))
|
51
|
-
|
52
|
-
|
53
|
-
return { ...response, __webextMessenger };
|
54
|
-
});
|
51
|
+
}));
|
52
|
+
debug(type, "↗️ responding", response);
|
53
|
+
return { ...response, __webextMessenger };
|
55
54
|
}
|
56
55
|
export function registerMethods(methods) {
|
57
|
-
|
56
|
+
if (!isBackgroundPage()) {
|
57
|
+
void nameThisTarget();
|
58
|
+
}
|
58
59
|
for (const [type, method] of Object.entries(methods)) {
|
59
60
|
if (handlers.has(type)) {
|
60
61
|
throw new MessengerError(`Handler already set for ${type}`);
|
package/distribution/sender.js
CHANGED
@@ -17,15 +17,15 @@ function makeMessage(type, args, target, options) {
|
|
17
17
|
};
|
18
18
|
}
|
19
19
|
// Do not turn this into an `async` function; Notifications must turn `void`
|
20
|
-
function manageConnection(type, options, sendMessage) {
|
20
|
+
function manageConnection(type, options, target, sendMessage) {
|
21
21
|
if (!options.isNotification) {
|
22
|
-
return manageMessage(type, sendMessage);
|
22
|
+
return manageMessage(type, target, sendMessage);
|
23
23
|
}
|
24
24
|
void sendMessage().catch((error) => {
|
25
25
|
debug(type, "notification failed", { error });
|
26
26
|
});
|
27
27
|
}
|
28
|
-
async function manageMessage(type, sendMessage) {
|
28
|
+
async function manageMessage(type, target, sendMessage) {
|
29
29
|
const response = await pRetry(async () => {
|
30
30
|
const response = await sendMessage();
|
31
31
|
if (!isMessengerResponse(response)) {
|
@@ -37,7 +37,9 @@ async function manageMessage(type, sendMessage) {
|
|
37
37
|
factor: 1.3,
|
38
38
|
maxRetryTime: 4000,
|
39
39
|
onFailedAttempt(error) {
|
40
|
-
if (
|
40
|
+
if (
|
41
|
+
// Don't retry sending to the background page unless it really hasn't loaded yet
|
42
|
+
(target.page !== "background" && error instanceof MessengerError) ||
|
41
43
|
String(error.message).startsWith(errorNonExistingTarget)) {
|
42
44
|
debug(type, "will retry. Attempt", error.attemptNumber);
|
43
45
|
}
|
@@ -68,11 +70,11 @@ function messenger(type, options, target, ...args) {
|
|
68
70
|
debug(type, "↗️ sending message to runtime");
|
69
71
|
return browser.runtime.sendMessage(makeMessage(type, args, target, options));
|
70
72
|
};
|
71
|
-
return manageConnection(type, options, sendMessage);
|
73
|
+
return manageConnection(type, options, target, sendMessage);
|
72
74
|
}
|
73
75
|
// Contexts without direct Tab access must go through background
|
74
76
|
if (!browser.tabs) {
|
75
|
-
return manageConnection(type, options, async () => {
|
77
|
+
return manageConnection(type, options, target, async () => {
|
76
78
|
debug(type, "↗️ sending message to runtime");
|
77
79
|
return browser.runtime.sendMessage(makeMessage(type, args, target, options));
|
78
80
|
});
|
@@ -80,7 +82,7 @@ function messenger(type, options, target, ...args) {
|
|
80
82
|
// `frameId` must be specified. If missing, the message is sent to every frame
|
81
83
|
const { tabId, frameId = 0 } = target;
|
82
84
|
// Message tab directly
|
83
|
-
return manageConnection(type, options, async () => {
|
85
|
+
return manageConnection(type, options, target, async () => {
|
84
86
|
debug(type, "↗️ sending message to tab", tabId, "frame", frameId);
|
85
87
|
return browser.tabs.sendMessage(tabId, makeMessage(type, args, target, options), {
|
86
88
|
frameId,
|
@@ -1,6 +1,5 @@
|
|
1
|
-
import {
|
2
|
-
declare
|
3
|
-
export declare function getActionForMessage(target: AnyTarget): "respond" | "forward" | "ignore";
|
1
|
+
import { AnyTarget, MessengerMeta, Sender } from "./types.js";
|
2
|
+
export declare function getActionForMessage(from: Sender, { ...to }: AnyTarget): "respond" | "forward" | "ignore";
|
4
3
|
export declare function nameThisTarget(): Promise<void>;
|
5
4
|
declare function __getTabData(this: MessengerMeta): AnyTarget;
|
6
5
|
declare global {
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { isBackgroundPage, isContentScript } from "webext-detect-page";
|
1
|
+
import { isBackgroundPage, isContentScript, isExtensionContext, } from "webext-detect-page";
|
2
2
|
import { messenger } from "./sender.js";
|
3
3
|
import { registerMethods } from "./receiver.js";
|
4
4
|
import { debug } from "./shared.js";
|
@@ -6,9 +6,10 @@ import { debug } from "./shared.js";
|
|
6
6
|
// This CANNOT be awaited because waiting for it means "I will handle the message."
|
7
7
|
// If a message is received before this is ready, it will just have to be ignored.
|
8
8
|
let thisTarget;
|
9
|
-
//
|
10
|
-
|
11
|
-
|
9
|
+
export function getActionForMessage(from, { ...to } // Clone object because we're editing it
|
10
|
+
) {
|
11
|
+
var _a;
|
12
|
+
if (to.page === "any") {
|
12
13
|
return "respond";
|
13
14
|
}
|
14
15
|
// Content scripts only receive messages that are meant for them. In the future
|
@@ -17,7 +18,7 @@ export function getActionForMessage(target) {
|
|
17
18
|
return "respond";
|
18
19
|
}
|
19
20
|
// We're in an extension page, but the target is not one.
|
20
|
-
if (!
|
21
|
+
if (!to.page) {
|
21
22
|
return "forward";
|
22
23
|
}
|
23
24
|
if (!thisTarget) {
|
@@ -25,18 +26,24 @@ export function getActionForMessage(target) {
|
|
25
26
|
// If this *was* the target, then probably no one else answered
|
26
27
|
return "ignore";
|
27
28
|
}
|
29
|
+
// Set "this" tab to the current tabId
|
30
|
+
if (to.tabId === "this" && thisTarget.tabId === ((_a = from.tab) === null || _a === void 0 ? void 0 : _a.id)) {
|
31
|
+
to.tabId = thisTarget.tabId;
|
32
|
+
}
|
28
33
|
// Every `target` key must match `thisTarget`
|
29
|
-
const isThisTarget = Object.entries(
|
34
|
+
const isThisTarget = Object.entries(to).every(
|
30
35
|
// @ts-expect-error Optional properties
|
31
36
|
([key, value]) => thisTarget[key] === value);
|
32
37
|
if (!isThisTarget) {
|
33
|
-
debug("The message’s target is",
|
38
|
+
debug("The message’s target is", to, "but this is", thisTarget);
|
34
39
|
}
|
35
40
|
return isThisTarget ? "respond" : "ignore";
|
36
41
|
}
|
42
|
+
let nameRequested = false;
|
37
43
|
export async function nameThisTarget() {
|
38
44
|
// Same as above: CS receives messages correctly
|
39
|
-
if (!thisTarget && !isContentScript()) {
|
45
|
+
if (!nameRequested && !thisTarget && !isContentScript()) {
|
46
|
+
nameRequested = true;
|
40
47
|
thisTarget = await messenger("__getTabData", {}, { page: "any" });
|
41
48
|
thisTarget.page = location.pathname;
|
42
49
|
}
|
@@ -48,6 +55,9 @@ function __getTabData() {
|
|
48
55
|
export function initPrivateApi() {
|
49
56
|
if (isBackgroundPage()) {
|
50
57
|
thisTarget = { page: "background" };
|
58
|
+
}
|
59
|
+
if (isExtensionContext()) {
|
60
|
+
// Any context can handler this message
|
51
61
|
registerMethods({ __getTabData });
|
52
62
|
}
|
53
63
|
}
|
package/distribution/types.d.ts
CHANGED
@@ -45,12 +45,17 @@ export declare type MessengerMessage = Message & {
|
|
45
45
|
/** Guarantees that a message is meant to be handled by this library */
|
46
46
|
__webextMessenger: true;
|
47
47
|
};
|
48
|
+
export interface AnyTarget {
|
49
|
+
tabId?: number | "this";
|
50
|
+
frameId?: number;
|
51
|
+
page?: string;
|
52
|
+
}
|
48
53
|
export interface Target {
|
49
54
|
tabId: number;
|
50
55
|
frameId?: number;
|
51
56
|
}
|
52
57
|
export interface PageTarget {
|
53
|
-
tabId?: number;
|
58
|
+
tabId?: number | "this";
|
54
59
|
page: string;
|
55
60
|
}
|
56
61
|
export {};
|