tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

observers.sys.mjs (5042B)


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