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

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4 "use strict";
michael@0 5
michael@0 6 const { Cc, Ci, Cr, Cu } = require("chrome");
michael@0 7 const { Input, start, stop, end, receive, outputs } = require("../event/utils");
michael@0 8 const { once, off } = require("../event/core");
michael@0 9 const { id: addonID } = require("../self");
michael@0 10
michael@0 11 const unloadMessage = require("@loader/unload");
michael@0 12 const { addObserver, removeObserver } = Cc['@mozilla.org/observer-service;1'].
michael@0 13 getService(Ci.nsIObserverService);
michael@0 14
michael@0 15 const addonUnloadTopic = "sdk:loader:destroy";
michael@0 16
michael@0 17 const isXrayWrapper = Cu.isXrayWrapper;
michael@0 18 // In the past SDK used to double-wrap notifications dispatched, which
michael@0 19 // made them awkward to use outside of SDK. At present they no longer
michael@0 20 // do that, although we still supported for legacy reasons.
michael@0 21 const isLegacyWrapper = x =>
michael@0 22 x && x.wrappedJSObject &&
michael@0 23 "observersModuleSubjectWrapper" in x.wrappedJSObject;
michael@0 24
michael@0 25 const unwrapLegacy = x => x.wrappedJSObject.object;
michael@0 26
michael@0 27 // `InputPort` provides a way to create a signal out of the observer
michael@0 28 // notification subject's for the given `topic`. If `options.initial`
michael@0 29 // is provided it is used as initial value otherwise `null` is used.
michael@0 30 // Constructor can be given `options.id` that will be used to create
michael@0 31 // a `topic` which is namespaced to an add-on (this avoids conflicts
michael@0 32 // when multiple add-on are used, although in a future host probably
michael@0 33 // should just be shared across add-ons). It is also possible to
michael@0 34 // specify a specific `topic` via `options.topic` which is used as
michael@0 35 // without namespacing. Created signal ends whenever add-on is
michael@0 36 // unloaded.
michael@0 37 const InputPort = function InputPort({id, topic, initial}) {
michael@0 38 this.id = id || topic;
michael@0 39 this.topic = topic || "sdk:" + addonID + ":" + id;
michael@0 40 this.value = initial === void(0) ? null : initial;
michael@0 41 this.observing = false;
michael@0 42 this[outputs] = [];
michael@0 43 };
michael@0 44
michael@0 45 // InputPort type implements `Input` signal interface.
michael@0 46 InputPort.prototype = new Input();
michael@0 47 InputPort.prototype.constructor = InputPort;
michael@0 48
michael@0 49 // When port is started (which is when it's subgraph get's
michael@0 50 // first subscriber) actual observer is registered.
michael@0 51 InputPort.start = input => {
michael@0 52 input.addListener(input);
michael@0 53 // Also register add-on unload observer to end this signal
michael@0 54 // when that happens.
michael@0 55 addObserver(input, addonUnloadTopic, false);
michael@0 56 };
michael@0 57 InputPort.prototype[start] = InputPort.start;
michael@0 58
michael@0 59 InputPort.addListener = input => addObserver(input, input.topic, false);
michael@0 60 InputPort.prototype.addListener = InputPort.addListener;
michael@0 61
michael@0 62 // When port is stopped (which is when it's subgraph has no
michael@0 63 // no subcribers left) an actual observer unregistered.
michael@0 64 // Note that port stopped once it ends as well (which is when
michael@0 65 // add-on is unloaded).
michael@0 66 InputPort.stop = input => {
michael@0 67 input.removeListener(input);
michael@0 68 removeObserver(input, addonUnloadTopic);
michael@0 69 };
michael@0 70 InputPort.prototype[stop] = InputPort.stop;
michael@0 71
michael@0 72 InputPort.removeListener = input => removeObserver(input, input.topic);
michael@0 73 InputPort.prototype.removeListener = InputPort.removeListener;
michael@0 74
michael@0 75 // `InputPort` also implements `nsIObserver` interface and
michael@0 76 // `nsISupportsWeakReference` interfaces as it's going to be used as such.
michael@0 77 InputPort.prototype.QueryInterface = function(iid) {
michael@0 78 if (!iid.equals(Ci.nsIObserver) && !iid.equals(Ci.nsISupportsWeakReference))
michael@0 79 throw Cr.NS_ERROR_NO_INTERFACE;
michael@0 80
michael@0 81 return this;
michael@0 82 };
michael@0 83
michael@0 84 // `InputPort` instances implement `observe` method, which is invoked when
michael@0 85 // observer notifications are dispatched. The `subject` of that notification
michael@0 86 // are received on this signal.
michael@0 87 InputPort.prototype.observe = function(subject, topic, data) {
michael@0 88 // Unwrap message from the subject. SDK used to have it's own version of
michael@0 89 // wrappedJSObjects which take precedence, if subject has `wrappedJSObject`
michael@0 90 // and it's not an XrayWrapper use it as message. Otherwise use subject as
michael@0 91 // is.
michael@0 92 const message = subject === null ? null :
michael@0 93 isLegacyWrapper(subject) ? unwrapLegacy(subject) :
michael@0 94 isXrayWrapper(subject) ? subject :
michael@0 95 subject.wrappedJSObject ? subject.wrappedJSObject :
michael@0 96 subject;
michael@0 97
michael@0 98 // If observer topic matches topic of the input port receive a message.
michael@0 99 if (topic === this.topic) {
michael@0 100 receive(this, message);
michael@0 101 }
michael@0 102
michael@0 103 // If observe topic is add-on unload topic we create an end message.
michael@0 104 if (topic === addonUnloadTopic && message === unloadMessage) {
michael@0 105 end(this);
michael@0 106 }
michael@0 107 };
michael@0 108
michael@0 109 exports.InputPort = InputPort;

mercurial