Left: | ||
Right: |
LEFT | RIGHT |
---|---|
1 /* | 1 /* |
2 * This file is part of Adblock Plus <https://adblockplus.org/>, | 2 * This file is part of Adblock Plus <https://adblockplus.org/>, |
3 * Copyright (C) 2006-2016 Eyeo GmbH | 3 * Copyright (C) 2006-2016 Eyeo GmbH |
4 * | 4 * |
5 * Adblock Plus is free software: you can redistribute it and/or modify | 5 * Adblock Plus is free software: you can redistribute it and/or modify |
6 * it under the terms of the GNU General Public License version 3 as | 6 * it under the terms of the GNU General Public License version 3 as |
7 * published by the Free Software Foundation. | 7 * published by the Free Software Foundation. |
8 * | 8 * |
9 * Adblock Plus is distributed in the hope that it will be useful, | 9 * Adblock Plus is distributed in the hope that it will be useful, |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 * GNU General Public License for more details. | 12 * GNU General Public License for more details. |
13 * | 13 * |
14 * You should have received a copy of the GNU General Public License | 14 * You should have received a copy of the GNU General Public License |
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. | 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
16 */ | 16 */ |
17 | 17 |
18 "use strict"; | 18 "use strict"; |
19 | 19 |
20 const MESSAGE_NAME = "AdblockPlus:Message"; | 20 const MESSAGE_NAME = "AdblockPlus:Message"; |
21 const RESPONSE_NAME = "AdblockPlus:Response"; | 21 const RESPONSE_NAME = "AdblockPlus:Response"; |
22 | 22 |
23 function isPromise(value) | |
24 { | |
25 // value instanceof Promise won't work - there can be different Promise | |
26 // classes (e.g. in different contexts) and there can also be promise-like | |
27 // classes (e.g. Task). | |
28 return (value && typeof value.then == "function"); | |
29 } | |
30 | |
23 function sendMessage(messageManager, messageName, payload, callbackID) | 31 function sendMessage(messageManager, messageName, payload, callbackID) |
24 { | 32 { |
25 let request = {messageName, payload, callbackID}; | 33 let request = {messageName, payload, callbackID}; |
26 if (messageManager instanceof Ci.nsIMessageSender) | 34 if (messageManager instanceof Ci.nsIMessageSender) |
27 { | 35 { |
28 messageManager.sendAsyncMessage(MESSAGE_NAME, request); | 36 messageManager.sendAsyncMessage(MESSAGE_NAME, request); |
29 return 1; | 37 return 1; |
30 } | 38 } |
31 else if (messageManager instanceof Ci.nsIMessageBroadcaster) | 39 else if (messageManager instanceof Ci.nsIMessageBroadcaster) |
32 { | 40 { |
33 messageManager.broadcastAsyncMessage(MESSAGE_NAME, request); | 41 messageManager.broadcastAsyncMessage(MESSAGE_NAME, request); |
34 return messageManager.childCount; | 42 return messageManager.childCount; |
35 } | 43 } |
36 else | 44 else |
37 { | 45 { |
38 Cu.reportError("Unexpected message manager, impossible to send message"); | 46 Cu.reportError("Unexpected message manager, impossible to send message"); |
39 return 0; | 47 return 0; |
40 } | 48 } |
41 } | 49 } |
42 | 50 |
43 function sendSyncMessage(messageManager, messageName, payload) | 51 function sendSyncMessage(messageManager, messageName, payload) |
44 { | 52 { |
45 let request = {messageName, payload}; | 53 let request = {messageName, payload}; |
46 let responses = messageManager.sendRpcMessage(MESSAGE_NAME, request); | 54 let responses = messageManager.sendRpcMessage(MESSAGE_NAME, request); |
55 let processor = new ResponseProcessor(messageName); | |
47 for (let response of responses) | 56 for (let response of responses) |
48 if (typeof response != "undefined") | 57 processor.add(response); |
49 return response; | 58 return processor.value; |
Thomas Greiner
2016/03/17 12:09:26
Detail: What's the reason for not using `flattenRe
Wladimir Palant
2016/03/21 15:31:31
No real reason, flattenResponses() was simply intr
| |
50 return undefined; | 59 } |
51 } | 60 |
52 | 61 function ResponseProcessor(messageName) |
53 function flattenResponses(responses, messageName) | 62 { |
54 { | 63 this.value = undefined; |
55 let result = undefined; | 64 this.add = function(response) |
56 for (let response of responses) | |
57 { | 65 { |
58 if (typeof response == "undefined") | 66 if (typeof response == "undefined") |
Erik
2016/03/17 05:02:04
nit `==` -> `===`
Wladimir Palant
2016/03/21 15:31:32
We generally don't use === unless it makes a diffe
| |
59 continue; | 67 return; |
60 | 68 |
61 if (typeof result == "undefined") | 69 if (typeof this.value == "undefined") |
Erik
2016/03/17 05:02:04
nit `==` -> `===`
Wladimir Palant
2016/03/21 15:31:33
Same as above.
| |
62 result = response; | 70 this.value = response; |
63 else | 71 else |
64 Cu.reportError("Got multiple responses to message '" + messageName + "', o nly first response was accepted."); | 72 Cu.reportError("Got multiple responses to message '" + messageName + "', o nly first response was accepted."); |
65 } | 73 }; |
66 return result; | |
67 } | 74 } |
68 | 75 |
69 function getSender(origin) | 76 function getSender(origin) |
70 { | 77 { |
71 if (origin instanceof Ci.nsIDOMXULElement) | 78 if (origin instanceof Ci.nsIDOMXULElement) |
72 origin = origin.messageManager; | 79 origin = origin.messageManager; |
73 | 80 |
74 if (origin instanceof Ci.nsIMessageSender) | 81 if (origin instanceof Ci.nsIMessageSender) |
75 return new LightWeightPort(origin); | 82 return new LightWeightPort(origin); |
76 else | 83 else |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
132 { | 139 { |
133 this._messageManager.removeMessageListener(MESSAGE_NAME, this._handleRequest ); | 140 this._messageManager.removeMessageListener(MESSAGE_NAME, this._handleRequest ); |
134 this._messageManager.removeMessageListener(RESPONSE_NAME, this._handleRespon se); | 141 this._messageManager.removeMessageListener(RESPONSE_NAME, this._handleRespon se); |
135 }, | 142 }, |
136 | 143 |
137 _sendResponse: function(sender, callbackID, payload) | 144 _sendResponse: function(sender, callbackID, payload) |
138 { | 145 { |
139 if (!sender || typeof callbackID == "undefined") | 146 if (!sender || typeof callbackID == "undefined") |
140 return; | 147 return; |
141 | 148 |
142 var response = {callbackID, payload}; | 149 let response = {callbackID, payload}; |
Thomas Greiner
2016/03/17 12:09:26
Detail: Unless there's a particular reason not to,
Wladimir Palant
2016/03/21 15:31:33
Done.
| |
143 sender._messageManager.sendAsyncMessage(RESPONSE_NAME, response); | 150 sender._messageManager.sendAsyncMessage(RESPONSE_NAME, response); |
144 }, | 151 }, |
145 | 152 |
146 _handleRequest: function(message) | 153 _handleRequest: function(message) |
147 { | 154 { |
148 let sender = getSender(message.target); | 155 let sender = getSender(message.target); |
149 let {callbackID, messageName, payload} = message.data; | 156 let {callbackID, messageName, payload} = message.data; |
150 | 157 |
151 let result = this._dispatch(messageName, payload, sender); | 158 let result = this._dispatch(messageName, payload, sender); |
152 if (result && typeof result.then == "function") | 159 if (isPromise(result)) |
Erik
2016/03/17 05:02:04
It'd be nice to have a `isPromise` function to reu
Thomas Greiner
2016/03/17 12:09:26
You mean `result instanceof Promise`?
Wladimir Palant
2016/03/21 15:31:33
No, `result instanceof Promise` would be wrong - t
| |
153 { | 160 { |
154 // This is a promise - asynchronous response | 161 // This is a promise - asynchronous response |
155 if (message.sync) | 162 if (message.sync) |
156 { | 163 { |
157 Cu.reportError("Asynchronous response to the synchronous message '" + me ssageName + "' is not possible"); | 164 Cu.reportError("Asynchronous response to the synchronous message '" + me ssageName + "' is not possible"); |
158 return undefined; | 165 return undefined; |
159 } | 166 } |
160 | 167 |
161 result.then(result => { | 168 result.then(result => |
169 { | |
162 this._sendResponse(sender, callbackID, result) | 170 this._sendResponse(sender, callbackID, result) |
163 }, e => { | 171 }, e => |
172 { | |
164 Cu.reportError(e); | 173 Cu.reportError(e); |
165 this._sendResponse(sender, callbackID, undefined); | 174 this._sendResponse(sender, callbackID, undefined); |
166 }); | 175 }); |
167 } | 176 } |
168 else | 177 else |
169 this._sendResponse(sender, callbackID, result); | 178 this._sendResponse(sender, callbackID, result); |
170 | 179 |
171 return result; | 180 return result; |
172 }, | 181 }, |
173 | 182 |
174 _handleResponse: function(message) | 183 _handleResponse: function(message) |
175 { | 184 { |
176 let {callbackID, payload} = message.data; | 185 let {callbackID, payload} = message.data; |
177 if (!this._responseCallbacks.has(callbackID)) | 186 let callbackData = this._responseCallbacks.get(callbackID); |
187 if (!callbackData) | |
178 return; | 188 return; |
179 | 189 |
180 let [callback, messageName, responses, expectedResponses] = | 190 let [callback, processor, expectedResponses] = callbackData; |
181 this._responseCallbacks.get(callbackID); | 191 |
182 responses.push(payload); | 192 try |
Thomas Greiner
2016/03/17 12:09:26
Why do you wait for and store all the responses if
Wladimir Palant
2016/03/21 15:31:31
Because otherwise this code gets rather complicate
Thomas Greiner
2016/03/21 18:43:22
So decrementing `expectedResponses` is not an opti
Wladimir Palant
2016/03/21 20:42:34
Without storing all the values in an array we cann
| |
183 if (responses.length == expectedResponses) | 193 { |
Erik
2016/03/17 05:02:04
nit `==` -> `===`
Wladimir Palant
2016/03/21 15:31:32
Same as above.
| |
194 processor.add(payload); | |
195 } | |
196 catch (e) | |
197 { | |
198 Cu.reportError(e); | |
199 } | |
200 | |
201 callbackData[2] = --expectedResponses; | |
202 if (expectedResponses <= 0) | |
184 { | 203 { |
185 this._responseCallbacks.delete(callbackID); | 204 this._responseCallbacks.delete(callbackID); |
186 callback(flattenResponses(responses, messageName)); | 205 callback(processor.value); |
187 } | 206 } |
188 }, | 207 }, |
189 | 208 |
190 _dispatch: function(messageName, payload, sender) | 209 _dispatch: function(messageName, payload, sender) |
191 { | 210 { |
192 let callbacks = this._callbacks.get(messageName); | 211 let callbacks = this._callbacks.get(messageName); |
193 if (!callbacks) | 212 if (!callbacks) |
194 return undefined; | 213 return undefined; |
195 | 214 |
196 callbacks = callbacks.slice(); | 215 callbacks = callbacks.slice(); |
Thomas Greiner
2016/03/17 12:09:26
Why do you copy the array? I don't see that a call
Wladimir Palant
2016/03/21 15:31:32
It sure could. Classic example is a callback that
Thomas Greiner
2016/03/21 18:43:22
Of course, in theory it could happen because it's
Wladimir Palant
2016/03/21 20:42:34
No, it would merely have to call port.off(argument
Thomas Greiner
2016/03/22 11:15:07
Ah, right. Nevermind then.
| |
197 let responses = []; | 216 let processor = new ResponseProcessor(messageName); |
198 for (let callback of callbacks) | 217 for (let callback of callbacks) |
199 { | 218 { |
200 try | 219 try |
201 { | 220 { |
202 responses.push(callback(payload, sender)); | 221 processor.add(callback(payload, sender)); |
203 } | 222 } |
204 catch (e) | 223 catch (e) |
205 { | 224 { |
206 Cu.reportError(e); | 225 Cu.reportError(e); |
207 } | 226 } |
208 } | 227 } |
209 return flattenResponses(responses, messageName); | 228 return processor.value; |
210 }, | 229 }, |
211 | 230 |
212 /** | 231 /** |
213 * Function to be called when a particular message is received | 232 * Function to be called when a particular message is received |
214 * @callback Port~messageHandler | 233 * @callback Port~messageHandler |
215 * @param payload data attached to the message if any | 234 * @param payload data attached to the message if any |
216 * @param {LightWeightPort} sender object that can be used to communicate with | 235 * @param {LightWeightPort} sender object that can be used to communicate with |
Erik
2016/03/17 05:02:05
this param appears to just accept a function, and
Wladimir Palant
2016/03/21 15:31:32
Explanation below.
| |
217 * the sender of the message, could be null | 236 * the sender of the message, could be null |
218 * @return the handler can return undefined (no response), a value (response | 237 * @return the handler can return undefined (no response), a value (response |
219 * to be sent to sender immediately) or a promise (asynchronous | 238 * to be sent to sender immediately) or a promise (asynchronous |
220 * response). | 239 * response). |
Erik
2016/03/17 05:02:05
`on` doesn't appear to return a promise, or anythi
Wladimir Palant
2016/03/21 15:31:32
Explanation below.
| |
221 */ | 240 */ |
222 | 241 |
223 /** | 242 /** |
224 * Adds a handler for the specified message. | 243 * Adds a handler for the specified message. |
225 * @param {string} messageName message that would trigger the callback | 244 * @param {string} messageName message that would trigger the callback |
226 * @param {Port~messageHandler} callback | 245 * @param {Port~messageHandler} callback |
Erik
2016/03/17 05:02:04
same comment as above, this appears to just accept
Wladimir Palant
2016/03/21 15:31:32
Please see http://usejsdoc.org/tags-callback.html
| |
227 */ | 246 */ |
228 on: function(messageName, callback) | 247 on: function(messageName, callback) |
229 { | 248 { |
230 if (!this._callbacks.has(messageName)) | |
231 this._callbacks.set(messageName, []); | |
Thomas Greiner
2016/03/17 12:09:26
Detail: Since you're using `Map` I wonder why you'
Wladimir Palant
2016/03/21 15:31:31
As Sebastian pointed out, Node.js (unlike Add-on S
Thomas Greiner
2016/03/21 18:43:23
Interesting, I wonder why they thought that'd be a
Wladimir Palant
2016/03/21 20:42:34
Probably because it's simpler/more efficient to im
| |
232 | |
233 let callbacks = this._callbacks.get(messageName); | 249 let callbacks = this._callbacks.get(messageName); |
234 if (callbacks.indexOf(callback) < 0) | 250 if (callbacks) |
235 callbacks.push(callback); | 251 callbacks.push(callback); |
252 else | |
253 this._callbacks.set(messageName, [callback]); | |
236 }, | 254 }, |
237 | 255 |
238 /** | 256 /** |
239 * Removes a handler for the specified message. | 257 * Removes a handler for the specified message. |
240 * @param {string} messageName message that would trigger the callback | 258 * @param {string} messageName message that would trigger the callback |
241 * @param {Port~messageHandler} callback | 259 * @param {Port~messageHandler} callback |
Erik
2016/03/17 05:02:04
if I am not mistaken about the above comments for
Wladimir Palant
2016/03/21 15:31:32
Same as above.
| |
242 */ | 260 */ |
243 off: function(messageName, callback) | 261 off: function(messageName, callback) |
244 { | 262 { |
245 let callbacks = this._callbacks.get(messageName); | 263 let callbacks = this._callbacks.get(messageName); |
246 if (!callbacks) | 264 if (!callbacks) |
247 return; | 265 return; |
248 | 266 |
249 let index = callbacks.indexOf(callback); | 267 let index = callbacks.indexOf(callback); |
250 if (index >= 0) | 268 if (index >= 0) |
251 callbacks.splice(index, 1); | 269 callbacks.splice(index, 1); |
252 }, | 270 }, |
253 | 271 |
254 /** | 272 /** |
255 * Sends a message. | 273 * Sends a message. |
256 * @param {string} messageName message identifier | 274 * @param {string} messageName message identifier |
257 * @param [payload] data to attach to the message | 275 * @param [payload] data to attach to the message |
Thomas Greiner
2016/03/17 12:09:26
Detail: Is this valid JSDoc syntax even with the t
Wladimir Palant
2016/03/21 15:31:32
Yes, it is.
| |
258 */ | 276 */ |
259 emit: function(messageName, payload) | 277 emit: function(messageName, payload) |
260 { | 278 { |
261 sendMessage(this._messageManager, messageName, payload, undefined); | 279 sendMessage(this._messageManager, messageName, payload, undefined); |
262 }, | 280 }, |
263 | 281 |
264 /** | 282 /** |
265 * Sends a message and expects a response. | 283 * Sends a message and expects a response. |
266 * @param {string} messageName message identifier | 284 * @param {string} messageName message identifier |
267 * @param [payload] data to attach to the message | 285 * @param [payload] data to attach to the message |
268 * @return {Promise} promise that will be resolved with the response | 286 * @return {Promise} promise that will be resolved with the response |
269 */ | 287 */ |
270 emitWithResponse: function(messageName, payload) | 288 emitWithResponse: function(messageName, payload) |
271 { | 289 { |
272 let callbackID = ++this._responseCallbackCounter; | 290 let callbackID = ++this._responseCallbackCounter; |
273 let expectedResponses = sendMessage( | 291 let expectedResponses = sendMessage( |
274 this._messageManager, messageName, payload, callbackID); | 292 this._messageManager, messageName, payload, callbackID); |
275 return new Promise((resolve, reject) => { | 293 return new Promise((resolve, reject) => |
Erik
2016/03/17 05:02:04
nit add a newline here?
Wladimir Palant
2016/03/21 15:31:32
Done.
| |
294 { | |
276 this._responseCallbacks.set(callbackID, | 295 this._responseCallbacks.set(callbackID, |
277 [resolve, messageName, [], expectedResponses]); | 296 [resolve, new ResponseProcessor(messageName), expectedResponses]); |
278 }); | 297 }); |
279 }, | 298 }, |
280 | 299 |
281 /** | 300 /** |
282 * Sends a synchonous message (DO NOT USE unless absolutely unavoidable). | 301 * Sends a synchonous message (DO NOT USE unless absolutely unavoidable). |
283 * @param {string} messageName message identifier | 302 * @param {string} messageName message identifier |
284 * @param [payload] data to attach to the message | 303 * @param [payload] data to attach to the message |
285 * @return response returned by the handler | 304 * @return response returned by the handler |
286 */ | 305 */ |
287 emitSync: function(messageName, payload) | 306 emitSync: function(messageName, payload) |
(...skipping 12 matching lines...) Expand all Loading... | |
300 catch (e) | 319 catch (e) |
301 { | 320 { |
302 // Parent | 321 // Parent |
303 messageManager = Cc["@mozilla.org/parentprocessmessagemanager;1"] | 322 messageManager = Cc["@mozilla.org/parentprocessmessagemanager;1"] |
304 .getService(Ci.nsIMessageListenerManager); | 323 .getService(Ci.nsIMessageListenerManager); |
305 } | 324 } |
306 | 325 |
307 let port = new Port(messageManager); | 326 let port = new Port(messageManager); |
308 onShutdown.add(() => port.disconnect()); | 327 onShutdown.add(() => port.disconnect()); |
309 exports.port = port; | 328 exports.port = port; |
LEFT | RIGHT |