PageRenderTime 13ms CodeModel.GetById 2ms app.highlight 8ms RepoModel.GetById 1ms app.codeStats 0ms

/services/sync/modules/ext/Observers.js

http://github.com/zpao/v8monkey
JavaScript | 183 lines | 61 code | 16 blank | 106 comment | 13 complexity | 2315d8e85be7c0dd3f137ea5d2faab33 MD5 | raw file
  1/* ***** BEGIN LICENSE BLOCK *****
  2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3 *
  4 * The contents of this file are subject to the Mozilla Public License Version
  5 * 1.1 (the "License"); you may not use this file except in compliance with
  6 * the License. You may obtain a copy of the License at
  7 * http://www.mozilla.org/MPL/
  8 *
  9 * Software distributed under the License is distributed on an "AS IS" basis,
 10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 11 * for the specific language governing rights and limitations under the
 12 * License.
 13 *
 14 * The Original Code is Observers.
 15 *
 16 * The Initial Developer of the Original Code is Daniel Aquino.
 17 * Portions created by the Initial Developer are Copyright (C) 2008
 18 * the Initial Developer. All Rights Reserved.
 19 *
 20 * Contributor(s):
 21 *   Daniel Aquino <mr.danielaquino@gmail.com>
 22 *   Myk Melez <myk@mozilla.org>
 23 *
 24 * Alternatively, the contents of this file may be used under the terms of
 25 * either the GNU General Public License Version 2 or later (the "GPL"), or
 26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 27 * in which case the provisions of the GPL or the LGPL are applicable instead
 28 * of those above. If you wish to allow use of your version of this file only
 29 * under the terms of either the GPL or the LGPL, and not to allow others to
 30 * use your version of this file under the terms of the MPL, indicate your
 31 * decision by deleting the provisions above and replace them with the notice
 32 * and other provisions required by the GPL or the LGPL. If you do not delete
 33 * the provisions above, a recipient may use your version of this file under
 34 * the terms of any one of the MPL, the GPL or the LGPL.
 35 *
 36 * ***** END LICENSE BLOCK ***** */
 37
 38let EXPORTED_SYMBOLS = ["Observers"];
 39
 40const Cc = Components.classes;
 41const Ci = Components.interfaces;
 42const Cr = Components.results;
 43const Cu = Components.utils;
 44
 45Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 46
 47/**
 48 * A service for adding, removing and notifying observers of notifications.
 49 * Wraps the nsIObserverService interface.
 50 *
 51 * @version 0.2
 52 */
 53let Observers = {
 54  /**
 55   * Register the given callback as an observer of the given topic.
 56   *
 57   * @param   topic       {String}
 58   *          the topic to observe
 59   *
 60   * @param   callback    {Object}
 61   *          the callback; an Object that implements nsIObserver or a Function
 62   *          that gets called when the notification occurs
 63   *
 64   * @param   thisObject  {Object}  [optional]
 65   *          the object to use as |this| when calling a Function callback
 66   *
 67   * @returns the observer
 68   */
 69  add: function(topic, callback, thisObject) {
 70    let observer = new Observer(topic, callback, thisObject);
 71    this._cache.push(observer);
 72    this._service.addObserver(observer, topic, true);
 73
 74    return observer;
 75  },
 76
 77  /**
 78   * Unregister the given callback as an observer of the given topic.
 79   *
 80   * @param topic       {String}
 81   *        the topic being observed
 82   *
 83   * @param callback    {Object}
 84   *        the callback doing the observing
 85   *
 86   * @param thisObject  {Object}  [optional]
 87   *        the object being used as |this| when calling a Function callback
 88   */
 89  remove: function(topic, callback, thisObject) {
 90    // This seems fairly inefficient, but I'm not sure how much better
 91    // we can make it.  We could index by topic, but we can't index by callback
 92    // or thisObject, as far as I know, since the keys to JavaScript hashes
 93    // (a.k.a. objects) can apparently only be primitive values.
 94    let [observer] = this._cache.filter(function(v) v.topic      == topic    &&
 95                                                    v.callback   == callback &&
 96                                                    v.thisObject == thisObject);
 97    if (observer) {
 98      this._service.removeObserver(observer, topic);
 99      this._cache.splice(this._cache.indexOf(observer), 1);
100    }
101  },
102
103  /**
104   * Notify observers about something.
105   *
106   * @param topic   {String}
107   *        the topic to notify observers about
108   *
109   * @param subject {Object}  [optional]
110   *        some information about the topic; can be any JS object or primitive
111   *
112   * @param data    {String}  [optional] [deprecated]
113   *        some more information about the topic; deprecated as the subject
114   *        is sufficient to pass all needed information to the JS observers
115   *        that this module targets; if you have multiple values to pass to
116   *        the observer, wrap them in an object and pass them via the subject
117   *        parameter (i.e.: { foo: 1, bar: "some string", baz: myObject })
118   */
119  notify: function(topic, subject, data) {
120    subject = (typeof subject == "undefined") ? null : new Subject(subject);
121       data = (typeof    data == "undefined") ? null : data;
122    this._service.notifyObservers(subject, topic, data);
123  },
124
125  _service: Cc["@mozilla.org/observer-service;1"].
126            getService(Ci.nsIObserverService),
127
128  /**
129   * A cache of observers that have been added.
130   *
131   * We use this to remove observers when a caller calls |remove|.
132   *
133   * XXX This might result in reference cycles, causing memory leaks,
134   * if we hold a reference to an observer that holds a reference to us.
135   * Could we fix that by making this an independent top-level object
136   * rather than a property of this object?
137   */
138  _cache: []
139};
140
141
142function Observer(topic, callback, thisObject) {
143  this.topic = topic;
144  this.callback = callback;
145  this.thisObject = thisObject;
146}
147
148Observer.prototype = {
149  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
150  observe: function(subject, topic, data) {
151    // Extract the wrapped object for subjects that are one of our wrappers
152    // around a JS object.  This way we support both wrapped subjects created
153    // using this module and those that are real XPCOM components.
154    if (subject && typeof subject == "object" &&
155        ("wrappedJSObject" in subject) &&
156        ("observersModuleSubjectWrapper" in subject.wrappedJSObject))
157      subject = subject.wrappedJSObject.object;
158
159    if (typeof this.callback == "function") {
160      if (this.thisObject)
161        this.callback.call(this.thisObject, subject, data);
162      else
163        this.callback(subject, data);
164    }
165    else // typeof this.callback == "object" (nsIObserver)
166      this.callback.observe(subject, topic, data);
167  }
168}
169
170
171function Subject(object) {
172  // Double-wrap the object and set a property identifying the wrappedJSObject
173  // as one of our wrappers to distinguish between subjects that are one of our
174  // wrappers (which we should unwrap when notifying our observers) and those
175  // that are real JS XPCOM components (which we should pass through unaltered).
176  this.wrappedJSObject = { observersModuleSubjectWrapper: true, object: object };
177}
178
179Subject.prototype = {
180  QueryInterface: XPCOMUtils.generateQI([]),
181  getHelperForLanguage: function() {},
182  getInterfaces: function() {}
183};