summaryrefslogtreecommitdiffstats
path: root/toolkit/jetpack/sdk/output/system.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/jetpack/sdk/output/system.js')
-rw-r--r--toolkit/jetpack/sdk/output/system.js71
1 files changed, 71 insertions, 0 deletions
diff --git a/toolkit/jetpack/sdk/output/system.js b/toolkit/jetpack/sdk/output/system.js
new file mode 100644
index 000000000..4fb16dcd5
--- /dev/null
+++ b/toolkit/jetpack/sdk/output/system.js
@@ -0,0 +1,71 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const { Cc, Ci, Cr } = require("chrome");
+const { Input, start, stop, receive, outputs } = require("../event/utils");
+const { id: addonID } = require("../self");
+const { setImmediate } = require("../timers");
+const { notifyObservers } = Cc['@mozilla.org/observer-service;1'].
+ getService(Ci.nsIObserverService);
+
+const NOT_AN_INPUT = "OutputPort can be used only for sending messages";
+
+// `OutputPort` creates a port to which messages can be send. Those
+// messages are actually disptached as `subject`'s of the observer
+// notifications. This is handy for communicating between different
+// components of the SDK. By default messages are dispatched
+// asynchronously, although `options.sync` can be used to make them
+// synchronous. If `options.id` is given `topic` for observer
+// notifications is generated by namespacing it, to avoid spamming
+// other SDK add-ons. It's also possible to provide `options.topic`
+// to use excat `topic` without namespacing it.
+//
+// Note: Symmetric `new InputPort({ id: "x" })` instances can be used to
+// receive messages send to the instances of `new OutputPort({ id: "x" })`.
+const OutputPort = function({id, topic, sync}) {
+ this.id = id || topic;
+ this.sync = !!sync;
+ this.topic = topic || "sdk:" + addonID + ":" + id;
+};
+// OutputPort extends base signal type to implement same message
+// receiving interface.
+OutputPort.prototype = new Input();
+OutputPort.constructor = OutputPort;
+
+// OutputPort can not be consumed there for starting or stopping it
+// is not supported.
+OutputPort.prototype[start] = _ => { throw TypeError(NOT_AN_INPUT); };
+OutputPort.prototype[stop] = _ => { throw TypeError(NOT_AN_INPUT); };
+
+// Port reecives message send to it, which will be dispatched via
+// observer notification service.
+OutputPort.receive = ({topic, sync}, message) => {
+ const type = typeof(message);
+ const supported = message === null ||
+ type === "object" ||
+ type === "function";
+
+ // There is no sensible way to wrap JS primitives that would make sense
+ // for general observer notification users. It's also probably not very
+ // useful to dispatch JS primitives as subject of observer service, there
+ // for we do not support those use cases.
+ if (!supported)
+ throw new TypeError("Unsupproted message type: `" + type + "`");
+
+ // Normalize `message` to create a valid observer notification `subject`.
+ // If `message` is `null`, implements `nsISupports` interface or already
+ // represents wrapped JS object use it as is. Otherwise create a wrapped
+ // object so that observers could receive it.
+ const subject = message === null ? null :
+ message instanceof Ci.nsISupports ? message :
+ message.wrappedJSObject ? message :
+ {wrappedJSObject: message};
+ if (sync)
+ notifyObservers(subject, topic, null);
+ else
+ setImmediate(notifyObservers, subject, topic, null);
+};
+OutputPort.prototype[receive] = OutputPort.receive;
+exports.OutputPort = OutputPort;