123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109 |
- /* 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, Cu } = require("chrome");
- const { Input, start, stop, end, receive, outputs } = require("../event/utils");
- const { once, off } = require("../event/core");
- const { id: addonID } = require("../self");
- const unloadMessage = require("@loader/unload");
- const { addObserver, removeObserver } = Cc['@mozilla.org/observer-service;1'].
- getService(Ci.nsIObserverService);
- const addonUnloadTopic = "sdk:loader:destroy";
- const isXrayWrapper = Cu.isXrayWrapper;
- // In the past SDK used to double-wrap notifications dispatched, which
- // made them awkward to use outside of SDK. At present they no longer
- // do that, although we still supported for legacy reasons.
- const isLegacyWrapper = x =>
- x && x.wrappedJSObject &&
- "observersModuleSubjectWrapper" in x.wrappedJSObject;
- const unwrapLegacy = x => x.wrappedJSObject.object;
- // `InputPort` provides a way to create a signal out of the observer
- // notification subject's for the given `topic`. If `options.initial`
- // is provided it is used as initial value otherwise `null` is used.
- // Constructor can be given `options.id` that will be used to create
- // a `topic` which is namespaced to an add-on (this avoids conflicts
- // when multiple add-on are used, although in a future host probably
- // should just be shared across add-ons). It is also possible to
- // specify a specific `topic` via `options.topic` which is used as
- // without namespacing. Created signal ends whenever add-on is
- // unloaded.
- const InputPort = function InputPort({id, topic, initial}) {
- this.id = id || topic;
- this.topic = topic || "sdk:" + addonID + ":" + id;
- this.value = initial === void(0) ? null : initial;
- this.observing = false;
- this[outputs] = [];
- };
- // InputPort type implements `Input` signal interface.
- InputPort.prototype = new Input();
- InputPort.prototype.constructor = InputPort;
- // When port is started (which is when it's subgraph get's
- // first subscriber) actual observer is registered.
- InputPort.start = input => {
- input.addListener(input);
- // Also register add-on unload observer to end this signal
- // when that happens.
- addObserver(input, addonUnloadTopic, false);
- };
- InputPort.prototype[start] = InputPort.start;
- InputPort.addListener = input => addObserver(input, input.topic, false);
- InputPort.prototype.addListener = InputPort.addListener;
- // When port is stopped (which is when it's subgraph has no
- // no subcribers left) an actual observer unregistered.
- // Note that port stopped once it ends as well (which is when
- // add-on is unloaded).
- InputPort.stop = input => {
- input.removeListener(input);
- removeObserver(input, addonUnloadTopic);
- };
- InputPort.prototype[stop] = InputPort.stop;
- InputPort.removeListener = input => removeObserver(input, input.topic);
- InputPort.prototype.removeListener = InputPort.removeListener;
- // `InputPort` also implements `nsIObserver` interface and
- // `nsISupportsWeakReference` interfaces as it's going to be used as such.
- InputPort.prototype.QueryInterface = function(iid) {
- if (!iid.equals(Ci.nsIObserver) && !iid.equals(Ci.nsISupportsWeakReference))
- throw Cr.NS_ERROR_NO_INTERFACE;
- return this;
- };
- // `InputPort` instances implement `observe` method, which is invoked when
- // observer notifications are dispatched. The `subject` of that notification
- // are received on this signal.
- InputPort.prototype.observe = function(subject, topic, data) {
- // Unwrap message from the subject. SDK used to have it's own version of
- // wrappedJSObjects which take precedence, if subject has `wrappedJSObject`
- // and it's not an XrayWrapper use it as message. Otherwise use subject as
- // is.
- const message = subject === null ? null :
- isLegacyWrapper(subject) ? unwrapLegacy(subject) :
- isXrayWrapper(subject) ? subject :
- subject.wrappedJSObject ? subject.wrappedJSObject :
- subject;
- // If observer topic matches topic of the input port receive a message.
- if (topic === this.topic) {
- receive(this, message);
- }
- // If observe topic is add-on unload topic we create an end message.
- if (topic === addonUnloadTopic && message === unloadMessage) {
- end(this);
- }
- };
- exports.InputPort = InputPort;
|