addon-sdk/source/lib/sdk/system/events.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 'use strict';
     7 module.metadata = {
     8   'stability': 'unstable'
     9 };
    11 const { Cc, Ci, Cu } = require('chrome');
    12 const { Unknown } = require('../platform/xpcom');
    13 const { Class } = require('../core/heritage');
    14 const { ns } = require('../core/namespace');
    15 const { addObserver, removeObserver, notifyObservers } = 
    16   Cc['@mozilla.org/observer-service;1'].getService(Ci.nsIObserverService);
    17 const unloadSubject = require('@loader/unload');
    19 const Subject = Class({
    20   extends: Unknown,
    21   initialize: function initialize(object) {
    22     // Double-wrap the object and set a property identifying the
    23     // wrappedJSObject as one of our wrappers to distinguish between
    24     // subjects that are one of our wrappers (which we should unwrap
    25     // when notifying our observers) and those that are real JS XPCOM
    26     // components (which we should pass through unaltered).
    27     this.wrappedJSObject = {
    28       observersModuleSubjectWrapper: true,
    29       object: object
    30     };
    31   },
    32   getHelperForLanguage: function() {},
    33   getInterfaces: function() {}
    34 });
    36 function emit(type, event) {
    37   // From bug 910599
    38   // We must test to see if 'subject' or 'data' is a defined property
    39   // of the event object, but also allow primitives to be passed in,
    40   // which the `in` operator breaks, yet `null` is an object, hence
    41   // the long conditional
    42   let subject = event && typeof event === 'object' && 'subject' in event ?
    43     Subject(event.subject) :
    44     null;
    45   let data = event && typeof event === 'object' ?
    46     // An object either returns its `data` property or null
    47     ('data' in event ? event.data : null) :
    48     // All other types return themselves (and cast to strings/null
    49     // via observer service)
    50     event;
    51   notifyObservers(subject, type, data);
    52 }
    53 exports.emit = emit;
    55 const Observer = Class({
    56   extends: Unknown,
    57   initialize: function initialize(listener) {
    58     this.listener = listener;
    59   },
    60   interfaces: [ 'nsIObserver', 'nsISupportsWeakReference' ],
    61   observe: function(subject, topic, data) {
    62     // Extract the wrapped object for subjects that are one of our
    63     // wrappers around a JS object.  This way we support both wrapped
    64     // subjects created using this module and those that are real
    65     // XPCOM components.
    66     if (subject && typeof(subject) == 'object' &&
    67         ('wrappedJSObject' in subject) &&
    68         ('observersModuleSubjectWrapper' in subject.wrappedJSObject))
    69       subject = subject.wrappedJSObject.object;
    71     try {
    72       this.listener({
    73         type: topic,
    74         subject: subject,
    75         data: data
    76       });
    77     }
    78     catch (error) {
    79       console.exception(error);
    80     }
    81   }
    82 });
    84 const subscribers = ns();
    86 function on(type, listener, strong) {
    87   // Unless last optional argument is `true` we use a weak reference to a
    88   // listener.
    89   let weak = !strong;
    90   // Take list of observers associated with given `listener` function.
    91   let observers = subscribers(listener);
    92   // If `observer` for the given `type` is not registered yet, then
    93   // associate an `observer` and register it.
    94   if (!(type in observers)) {
    95     let observer = Observer(listener);
    96     observers[type] = observer;
    97     addObserver(observer, type, weak);
    98     // WeakRef gymnastics to remove all alive observers on unload
    99     let ref = Cu.getWeakReference(observer);
   100     weakRefs.set(observer, ref);
   101     stillAlive.set(ref, type);
   102   }
   103 }
   104 exports.on = on;
   106 function once(type, listener) {
   107   // Note: this code assumes order in which listeners are called, which is fine
   108   // as long as dispatch happens in same order as listener registration which
   109   // is the case now. That being said we should be aware that this may break
   110   // in a future if order will change.
   111   on(type, listener);
   112   on(type, function cleanup() {
   113     off(type, listener);
   114     off(type, cleanup);
   115   }, true);
   116 }
   117 exports.once = once;
   119 function off(type, listener) {
   120   // Take list of observers as with the given `listener`.
   121   let observers = subscribers(listener);
   122   // If `observer` for the given `type` is registered, then
   123   // remove it & unregister.
   124   if (type in observers) {
   125     let observer = observers[type];
   126     delete observers[type];
   127     removeObserver(observer, type);
   128     stillAlive.delete(weakRefs.get(observer));
   129   }
   130 }
   131 exports.off = off;
   133 // must use WeakMap to keep reference to all the WeakRefs (!), see bug 986115
   134 let weakRefs = new WeakMap();
   136 // and we're out of beta, we're releasing on time!
   137 let stillAlive = new Map();   
   139 on('sdk:loader:destroy', function onunload({ subject, data: reason }) {
   140   // using logic from ./unload, to avoid a circular module reference
   141   if (subject.wrappedJSObject === unloadSubject) {
   142     off('sdk:loader:destroy', onunload);
   144     // don't bother
   145     if (reason === 'shutdown') 
   146       return;
   148     stillAlive.forEach( (type, ref) => {
   149       let observer = ref.get();
   150       if (observer) 
   151         removeObserver(observer, type);
   152     })
   153   }
   154   // a strong reference
   155 }, true);

mercurial