services/common/observers.js

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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
michael@0 5 #ifndef MERGED_COMPARTMENT
michael@0 6
michael@0 7 this.EXPORTED_SYMBOLS = ["Observers"];
michael@0 8
michael@0 9 const Cc = Components.classes;
michael@0 10 const Ci = Components.interfaces;
michael@0 11 const Cr = Components.results;
michael@0 12 const Cu = Components.utils;
michael@0 13
michael@0 14 #endif
michael@0 15
michael@0 16 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
michael@0 17
michael@0 18 /**
michael@0 19 * A service for adding, removing and notifying observers of notifications.
michael@0 20 * Wraps the nsIObserverService interface.
michael@0 21 *
michael@0 22 * @version 0.2
michael@0 23 */
michael@0 24 this.Observers = {
michael@0 25 /**
michael@0 26 * Register the given callback as an observer of the given topic.
michael@0 27 *
michael@0 28 * @param topic {String}
michael@0 29 * the topic to observe
michael@0 30 *
michael@0 31 * @param callback {Object}
michael@0 32 * the callback; an Object that implements nsIObserver or a Function
michael@0 33 * that gets called when the notification occurs
michael@0 34 *
michael@0 35 * @param thisObject {Object} [optional]
michael@0 36 * the object to use as |this| when calling a Function callback
michael@0 37 *
michael@0 38 * @returns the observer
michael@0 39 */
michael@0 40 add: function(topic, callback, thisObject) {
michael@0 41 let observer = new Observer(topic, callback, thisObject);
michael@0 42 this._cache.push(observer);
michael@0 43 this._service.addObserver(observer, topic, true);
michael@0 44
michael@0 45 return observer;
michael@0 46 },
michael@0 47
michael@0 48 /**
michael@0 49 * Unregister the given callback as an observer of the given topic.
michael@0 50 *
michael@0 51 * @param topic {String}
michael@0 52 * the topic being observed
michael@0 53 *
michael@0 54 * @param callback {Object}
michael@0 55 * the callback doing the observing
michael@0 56 *
michael@0 57 * @param thisObject {Object} [optional]
michael@0 58 * the object being used as |this| when calling a Function callback
michael@0 59 */
michael@0 60 remove: function(topic, callback, thisObject) {
michael@0 61 // This seems fairly inefficient, but I'm not sure how much better
michael@0 62 // we can make it. We could index by topic, but we can't index by callback
michael@0 63 // or thisObject, as far as I know, since the keys to JavaScript hashes
michael@0 64 // (a.k.a. objects) can apparently only be primitive values.
michael@0 65 let [observer] = this._cache.filter(function(v) v.topic == topic &&
michael@0 66 v.callback == callback &&
michael@0 67 v.thisObject == thisObject);
michael@0 68 if (observer) {
michael@0 69 this._service.removeObserver(observer, topic);
michael@0 70 this._cache.splice(this._cache.indexOf(observer), 1);
michael@0 71 }
michael@0 72 },
michael@0 73
michael@0 74 /**
michael@0 75 * Notify observers about something.
michael@0 76 *
michael@0 77 * @param topic {String}
michael@0 78 * the topic to notify observers about
michael@0 79 *
michael@0 80 * @param subject {Object} [optional]
michael@0 81 * some information about the topic; can be any JS object or primitive
michael@0 82 *
michael@0 83 * @param data {String} [optional] [deprecated]
michael@0 84 * some more information about the topic; deprecated as the subject
michael@0 85 * is sufficient to pass all needed information to the JS observers
michael@0 86 * that this module targets; if you have multiple values to pass to
michael@0 87 * the observer, wrap them in an object and pass them via the subject
michael@0 88 * parameter (i.e.: { foo: 1, bar: "some string", baz: myObject })
michael@0 89 */
michael@0 90 notify: function(topic, subject, data) {
michael@0 91 subject = (typeof subject == "undefined") ? null : new Subject(subject);
michael@0 92 data = (typeof data == "undefined") ? null : data;
michael@0 93 this._service.notifyObservers(subject, topic, data);
michael@0 94 },
michael@0 95
michael@0 96 _service: Cc["@mozilla.org/observer-service;1"].
michael@0 97 getService(Ci.nsIObserverService),
michael@0 98
michael@0 99 /**
michael@0 100 * A cache of observers that have been added.
michael@0 101 *
michael@0 102 * We use this to remove observers when a caller calls |remove|.
michael@0 103 *
michael@0 104 * XXX This might result in reference cycles, causing memory leaks,
michael@0 105 * if we hold a reference to an observer that holds a reference to us.
michael@0 106 * Could we fix that by making this an independent top-level object
michael@0 107 * rather than a property of this object?
michael@0 108 */
michael@0 109 _cache: []
michael@0 110 };
michael@0 111
michael@0 112
michael@0 113 function Observer(topic, callback, thisObject) {
michael@0 114 this.topic = topic;
michael@0 115 this.callback = callback;
michael@0 116 this.thisObject = thisObject;
michael@0 117 }
michael@0 118
michael@0 119 Observer.prototype = {
michael@0 120 QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
michael@0 121 observe: function(subject, topic, data) {
michael@0 122 // Extract the wrapped object for subjects that are one of our wrappers
michael@0 123 // around a JS object. This way we support both wrapped subjects created
michael@0 124 // using this module and those that are real XPCOM components.
michael@0 125 if (subject && typeof subject == "object" &&
michael@0 126 ("wrappedJSObject" in subject) &&
michael@0 127 ("observersModuleSubjectWrapper" in subject.wrappedJSObject))
michael@0 128 subject = subject.wrappedJSObject.object;
michael@0 129
michael@0 130 if (typeof this.callback == "function") {
michael@0 131 if (this.thisObject)
michael@0 132 this.callback.call(this.thisObject, subject, data);
michael@0 133 else
michael@0 134 this.callback(subject, data);
michael@0 135 }
michael@0 136 else // typeof this.callback == "object" (nsIObserver)
michael@0 137 this.callback.observe(subject, topic, data);
michael@0 138 }
michael@0 139 }
michael@0 140
michael@0 141
michael@0 142 function Subject(object) {
michael@0 143 // Double-wrap the object and set a property identifying the wrappedJSObject
michael@0 144 // as one of our wrappers to distinguish between subjects that are one of our
michael@0 145 // wrappers (which we should unwrap when notifying our observers) and those
michael@0 146 // that are real JS XPCOM components (which we should pass through unaltered).
michael@0 147 this.wrappedJSObject = { observersModuleSubjectWrapper: true, object: object };
michael@0 148 }
michael@0 149
michael@0 150 Subject.prototype = {
michael@0 151 QueryInterface: XPCOMUtils.generateQI([]),
michael@0 152 getHelperForLanguage: function() {},
michael@0 153 getInterfaces: function() {}
michael@0 154 };

mercurial