|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 "use strict"; |
|
5 |
|
6 const { Cc, Ci, Cr } = require("chrome"); |
|
7 const { Input, start, stop, receive, outputs } = require("../event/utils"); |
|
8 const { id: addonID } = require("../self"); |
|
9 const { setImmediate } = require("../timers"); |
|
10 const { notifyObservers } = Cc['@mozilla.org/observer-service;1']. |
|
11 getService(Ci.nsIObserverService); |
|
12 |
|
13 const NOT_AN_INPUT = "OutputPort can be used only for sending messages"; |
|
14 |
|
15 // `OutputPort` creates a port to which messages can be send. Those |
|
16 // messages are actually disptached as `subject`'s of the observer |
|
17 // notifications. This is handy for communicating between different |
|
18 // components of the SDK. By default messages are dispatched |
|
19 // asynchronously, although `options.sync` can be used to make them |
|
20 // synchronous. If `options.id` is given `topic` for observer |
|
21 // notifications is generated by namespacing it, to avoid spamming |
|
22 // other SDK add-ons. It's also possible to provide `options.topic` |
|
23 // to use excat `topic` without namespacing it. |
|
24 // |
|
25 // Note: Symmetric `new InputPort({ id: "x" })` instances can be used to |
|
26 // receive messages send to the instances of `new OutputPort({ id: "x" })`. |
|
27 const OutputPort = function({id, topic, sync}) { |
|
28 this.id = id || topic; |
|
29 this.sync = !!sync; |
|
30 this.topic = topic || "sdk:" + addonID + ":" + id; |
|
31 }; |
|
32 // OutputPort extends base signal type to implement same message |
|
33 // receiving interface. |
|
34 OutputPort.prototype = new Input(); |
|
35 OutputPort.constructor = OutputPort; |
|
36 |
|
37 // OutputPort can not be consumed there for starting or stopping it |
|
38 // is not supported. |
|
39 OutputPort.prototype[start] = _ => { throw TypeError(NOT_AN_INPUT); }; |
|
40 OutputPort.prototype[stop] = _ => { throw TypeError(NOT_AN_INPUT); }; |
|
41 |
|
42 // Port reecives message send to it, which will be dispatched via |
|
43 // observer notification service. |
|
44 OutputPort.receive = ({topic, sync}, message) => { |
|
45 const type = typeof(message); |
|
46 const supported = message === null || |
|
47 type === "object" || |
|
48 type === "function"; |
|
49 |
|
50 // There is no sensible way to wrap JS primitives that would make sense |
|
51 // for general observer notification users. It's also probably not very |
|
52 // useful to dispatch JS primitives as subject of observer service, there |
|
53 // for we do not support those use cases. |
|
54 if (!supported) |
|
55 throw new TypeError("Unsupproted message type: `" + type + "`"); |
|
56 |
|
57 // Normalize `message` to create a valid observer notification `subject`. |
|
58 // If `message` is `null`, implements `nsISupports` interface or already |
|
59 // represents wrapped JS object use it as is. Otherwise create a wrapped |
|
60 // object so that observers could receive it. |
|
61 const subject = message === null ? null : |
|
62 message instanceof Ci.nsISupports ? message : |
|
63 message.wrappedJSObject ? message : |
|
64 {wrappedJSObject: message}; |
|
65 if (sync) |
|
66 notifyObservers(subject, topic, null); |
|
67 else |
|
68 setImmediate(notifyObservers, subject, topic, null); |
|
69 }; |
|
70 OutputPort.prototype[receive] = OutputPort.receive; |
|
71 exports.OutputPort = OutputPort; |