services/common/observers.js

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

mercurial