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