addon-sdk/source/lib/sdk/input/system.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/addon-sdk/source/lib/sdk/input/system.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,109 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +"use strict";
     1.8 +
     1.9 +const { Cc, Ci, Cr, Cu } = require("chrome");
    1.10 +const { Input, start, stop, end, receive, outputs } = require("../event/utils");
    1.11 +const { once, off } = require("../event/core");
    1.12 +const { id: addonID } = require("../self");
    1.13 +
    1.14 +const unloadMessage = require("@loader/unload");
    1.15 +const { addObserver, removeObserver } = Cc['@mozilla.org/observer-service;1'].
    1.16 +                                          getService(Ci.nsIObserverService);
    1.17 +
    1.18 +const addonUnloadTopic = "sdk:loader:destroy";
    1.19 +
    1.20 +const isXrayWrapper = Cu.isXrayWrapper;
    1.21 +// In the past SDK used to double-wrap notifications dispatched, which
    1.22 +// made them awkward to use outside of SDK. At present they no longer
    1.23 +// do that, although we still supported for legacy reasons.
    1.24 +const isLegacyWrapper = x =>
    1.25 +    x && x.wrappedJSObject &&
    1.26 +    "observersModuleSubjectWrapper" in x.wrappedJSObject;
    1.27 +
    1.28 +const unwrapLegacy = x => x.wrappedJSObject.object;
    1.29 +
    1.30 +// `InputPort` provides a way to create a signal out of the observer
    1.31 +// notification subject's for the given `topic`. If `options.initial`
    1.32 +// is provided it is used as initial value otherwise `null` is used.
    1.33 +// Constructor can be given `options.id` that will be used to create
    1.34 +// a `topic` which is namespaced to an add-on (this avoids conflicts
    1.35 +// when multiple add-on are used, although in a future host probably
    1.36 +// should just be shared across add-ons). It is also possible to
    1.37 +// specify a specific `topic` via `options.topic` which is used as
    1.38 +// without namespacing. Created signal ends whenever add-on is
    1.39 +// unloaded.
    1.40 +const InputPort = function InputPort({id, topic, initial}) {
    1.41 +  this.id = id || topic;
    1.42 +  this.topic = topic || "sdk:" + addonID + ":" + id;
    1.43 +  this.value = initial === void(0) ? null : initial;
    1.44 +  this.observing = false;
    1.45 +  this[outputs] = [];
    1.46 +};
    1.47 +
    1.48 +// InputPort type implements `Input` signal interface.
    1.49 +InputPort.prototype = new Input();
    1.50 +InputPort.prototype.constructor = InputPort;
    1.51 +
    1.52 +// When port is started (which is when it's subgraph get's
    1.53 +// first subscriber) actual observer is registered.
    1.54 +InputPort.start = input => {
    1.55 +  input.addListener(input);
    1.56 +  // Also register add-on unload observer to end this signal
    1.57 +  // when that happens.
    1.58 +  addObserver(input, addonUnloadTopic, false);
    1.59 +};
    1.60 +InputPort.prototype[start] = InputPort.start;
    1.61 +
    1.62 +InputPort.addListener = input => addObserver(input, input.topic, false);
    1.63 +InputPort.prototype.addListener = InputPort.addListener;
    1.64 +
    1.65 +// When port is stopped (which is when it's subgraph has no
    1.66 +// no subcribers left) an actual observer unregistered.
    1.67 +// Note that port stopped once it ends as well (which is when
    1.68 +// add-on is unloaded).
    1.69 +InputPort.stop = input => {
    1.70 +  input.removeListener(input);
    1.71 +  removeObserver(input, addonUnloadTopic);
    1.72 +};
    1.73 +InputPort.prototype[stop] = InputPort.stop;
    1.74 +
    1.75 +InputPort.removeListener = input => removeObserver(input, input.topic);
    1.76 +InputPort.prototype.removeListener = InputPort.removeListener;
    1.77 +
    1.78 +// `InputPort` also implements `nsIObserver` interface and
    1.79 +// `nsISupportsWeakReference` interfaces as it's going to be used as such.
    1.80 +InputPort.prototype.QueryInterface = function(iid) {
    1.81 +  if (!iid.equals(Ci.nsIObserver) && !iid.equals(Ci.nsISupportsWeakReference))
    1.82 +    throw Cr.NS_ERROR_NO_INTERFACE;
    1.83 +
    1.84 +  return this;
    1.85 +};
    1.86 +
    1.87 +// `InputPort` instances implement `observe` method, which is invoked when
    1.88 +// observer notifications are dispatched. The `subject` of that notification
    1.89 +// are received on this signal.
    1.90 +InputPort.prototype.observe = function(subject, topic, data) {
    1.91 +  // Unwrap message from the subject. SDK used to have it's own version of
    1.92 +  // wrappedJSObjects which take precedence, if subject has `wrappedJSObject`
    1.93 +  // and it's not an XrayWrapper use it as message. Otherwise use subject as
    1.94 +  // is.
    1.95 +  const message = subject === null ? null :
    1.96 +        isLegacyWrapper(subject) ? unwrapLegacy(subject) :
    1.97 +        isXrayWrapper(subject) ? subject :
    1.98 +        subject.wrappedJSObject ? subject.wrappedJSObject :
    1.99 +        subject;
   1.100 +
   1.101 +  // If observer topic matches topic of the input port receive a message.
   1.102 +  if (topic === this.topic) {
   1.103 +    receive(this, message);
   1.104 +  }
   1.105 +
   1.106 +  // If observe topic is add-on unload topic we create an end message.
   1.107 +  if (topic === addonUnloadTopic && message === unloadMessage) {
   1.108 +    end(this);
   1.109 +  }
   1.110 +};
   1.111 +
   1.112 +exports.InputPort = InputPort;

mercurial