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;