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

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

mercurial