-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathBrowserCommunication.js
166 lines (144 loc) · 5.22 KB
/
BrowserCommunication.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/**
* Communicates between different browser parts.
*
* @public
* @module BrowserCommunication
* @requires data/BrowserCommunicationTypes
*/
import { COMMUNICATION_MESSAGE_TYPE } from "../data/BrowserCommunicationTypes.js";
const callbacks = {};
/**
* The callback that can be used to send a response.
*
* It is just the default browser callback that is used and documented here.
* However, it is strongly discouraged to return "true", as this is hard to
* handle with multiple registered callbacks.
*
* @callback sendResponseCallback
* @param {string} message the response message, "may be any JSON-ifiable object"
* @return {Promise}
* @see {@link https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/Runtime/onMessage#Parameters}
*/
/**
* This is the listener that is called when a message with that type has arrived.
*
* It is just the default browser callback that is used and documented here.
* However, it is strongly discouraged to return "true", as this is hard to
* handle with multiple registered callbacks.
*
* @callback listenerCallback
* @param {Object} request JSON-ifiable object of the message
* @param {Object} sender the runtime.MessageSender, see {@link https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/runtime/MessageSender}
* @param {sendResponseCallback} sendResponse
* @returns {Promise}
* @see {@link https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/Runtime/onMessage#Parameters}
*/
/**
* Throws an error, if the message type is not known/unknown.
*
* @private
* @param {COMMUNICATION_MESSAGE_TYPE} messageType type of message
* @returns {void}
* @throws {Error}
*/
function checkMessageTypeVadility(messageType) {
if (messageType === undefined) {
throw new Error("message type is undefined");
}
if (Object.values(COMMUNICATION_MESSAGE_TYPE).includes(messageType)) {
return; // all right
}
throw new Error(`message type ${messageType} is not valid/known`);
}
/**
* Handles messages received by other parts.
*
* @private
* @param {Object} request JSON-ifiable object of the message
* @param {Object} sender the runtime.MessageSender, see {@link https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/runtime/MessageSender}
* @param {sendResponseCallback} sendResponse
* @returns {Promise|true}
* @see {@link https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/Runtime/onMessage#Parameters}
*/
function handleMessages(request, sender, sendResponse) {
console.info("Got message", request, "from", sender);
const messageType = request.type;
checkMessageTypeVadility(messageType);
if (!(messageType in callbacks) || callbacks[messageType].length === 0) {
console.warn(`No callbacks for message type "${messageType}" registered.`);
return true;
}
// call all callbacks and keep return values
const promises = [];
let gotTrueAsReturn = false;
for (const callback of callbacks[messageType]) {
const returnValue = callback(request, sender, sendResponse);
// notice if return value is just "true"
if (returnValue === true) {
gotTrueAsReturn = true;
continue;
}
// return value should be a Promise
promises.push(returnValue);
}
// handle returning
if (gotTrueAsReturn) {
if (callbacks[messageType].length !== 1) {
// if it was not the only callback, then show a real error
console.error(`At least one callback for message type "${messageType}" returned the legacy value "true".
As you have registered ${callbacks[messageType].length} listeners this may lead to errors.`);
} else {
// show warning as this behaviour is discouraged
console.warn(`At least one callback for message type "${messageType}" returned the legacy value "true". Please return a Promise instead.`);
}
return true;
}
return Promise.all(promises);
}
/**
* Add a listener for a specific type.
*
* You can add multiple listeners, but may *NOT* preserve the order.
* Actually it does call them in reverse as it uses a stack (LIFO) internally.
*
* @public
* @param {COMMUNICATION_MESSAGE_TYPE} messageType type of message to receive
* @param {listenerCallback} callback
* @returns {void}
*/
export function addListener(messageType, callback) {
checkMessageTypeVadility(messageType);
if (!(messageType in callbacks)) {
callbacks[messageType] = [];
}
callbacks[messageType].push(callback);
}
/**
* Remove an existent listener of a specific type.
*
* @public
* @param {COMMUNICATION_MESSAGE_TYPE} messageType type of message to receive
* @param {listenerCallback} callback
* @returns {void}
*/
export function removeListener(messageType, callback) {
checkMessageTypeVadility(messageType);
if (!(messageType in callbacks)) {
return;
}
callbacks[messageType] = callbacks[messageType]
.filter((addedCallback) => addedCallback !== callback);
}
/**
* Init context menu module.
*
* Adds menu elements.
*
* @public
* @returns {void}
*/
export function init() {
browser.runtime.onMessage.addListener(handleMessages);
}
// automatically init's itself
init();