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

     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/. */
     5 #ifndef MERGED_COMPARTMENT
     7 this.EXPORTED_SYMBOLS = ["Observers"];
     9 const Cc = Components.classes;
    10 const Ci = Components.interfaces;
    11 const Cr = Components.results;
    12 const Cu = Components.utils;
    14 #endif
    16 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    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);
    45     return observer;
    46   },
    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   },
    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   },
    96   _service: Cc["@mozilla.org/observer-service;1"].
    97             getService(Ci.nsIObserverService),
    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 };
   113 function Observer(topic, callback, thisObject) {
   114   this.topic = topic;
   115   this.callback = callback;
   116   this.thisObject = thisObject;
   117 }
   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;
   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 }
   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 }
   150 Subject.prototype = {
   151   QueryInterface: XPCOMUtils.generateQI([]),
   152   getHelperForLanguage: function() {},
   153   getInterfaces: function() {}
   154 };

mercurial