addon-sdk/source/lib/sdk/system/events.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/addon-sdk/source/lib/sdk/system/events.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,155 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +'use strict';
     1.9 +
    1.10 +module.metadata = {
    1.11 +  'stability': 'unstable'
    1.12 +};
    1.13 +
    1.14 +const { Cc, Ci, Cu } = require('chrome');
    1.15 +const { Unknown } = require('../platform/xpcom');
    1.16 +const { Class } = require('../core/heritage');
    1.17 +const { ns } = require('../core/namespace');
    1.18 +const { addObserver, removeObserver, notifyObservers } = 
    1.19 +  Cc['@mozilla.org/observer-service;1'].getService(Ci.nsIObserverService);
    1.20 +const unloadSubject = require('@loader/unload');
    1.21 +
    1.22 +const Subject = Class({
    1.23 +  extends: Unknown,
    1.24 +  initialize: function initialize(object) {
    1.25 +    // Double-wrap the object and set a property identifying the
    1.26 +    // wrappedJSObject as one of our wrappers to distinguish between
    1.27 +    // subjects that are one of our wrappers (which we should unwrap
    1.28 +    // when notifying our observers) and those that are real JS XPCOM
    1.29 +    // components (which we should pass through unaltered).
    1.30 +    this.wrappedJSObject = {
    1.31 +      observersModuleSubjectWrapper: true,
    1.32 +      object: object
    1.33 +    };
    1.34 +  },
    1.35 +  getHelperForLanguage: function() {},
    1.36 +  getInterfaces: function() {}
    1.37 +});
    1.38 +
    1.39 +function emit(type, event) {
    1.40 +  // From bug 910599
    1.41 +  // We must test to see if 'subject' or 'data' is a defined property
    1.42 +  // of the event object, but also allow primitives to be passed in,
    1.43 +  // which the `in` operator breaks, yet `null` is an object, hence
    1.44 +  // the long conditional
    1.45 +  let subject = event && typeof event === 'object' && 'subject' in event ?
    1.46 +    Subject(event.subject) :
    1.47 +    null;
    1.48 +  let data = event && typeof event === 'object' ?
    1.49 +    // An object either returns its `data` property or null
    1.50 +    ('data' in event ? event.data : null) :
    1.51 +    // All other types return themselves (and cast to strings/null
    1.52 +    // via observer service)
    1.53 +    event;
    1.54 +  notifyObservers(subject, type, data);
    1.55 +}
    1.56 +exports.emit = emit;
    1.57 +
    1.58 +const Observer = Class({
    1.59 +  extends: Unknown,
    1.60 +  initialize: function initialize(listener) {
    1.61 +    this.listener = listener;
    1.62 +  },
    1.63 +  interfaces: [ 'nsIObserver', 'nsISupportsWeakReference' ],
    1.64 +  observe: function(subject, topic, data) {
    1.65 +    // Extract the wrapped object for subjects that are one of our
    1.66 +    // wrappers around a JS object.  This way we support both wrapped
    1.67 +    // subjects created using this module and those that are real
    1.68 +    // XPCOM components.
    1.69 +    if (subject && typeof(subject) == 'object' &&
    1.70 +        ('wrappedJSObject' in subject) &&
    1.71 +        ('observersModuleSubjectWrapper' in subject.wrappedJSObject))
    1.72 +      subject = subject.wrappedJSObject.object;
    1.73 +
    1.74 +    try {
    1.75 +      this.listener({
    1.76 +        type: topic,
    1.77 +        subject: subject,
    1.78 +        data: data
    1.79 +      });
    1.80 +    }
    1.81 +    catch (error) {
    1.82 +      console.exception(error);
    1.83 +    }
    1.84 +  }
    1.85 +});
    1.86 +
    1.87 +const subscribers = ns();
    1.88 +
    1.89 +function on(type, listener, strong) {
    1.90 +  // Unless last optional argument is `true` we use a weak reference to a
    1.91 +  // listener.
    1.92 +  let weak = !strong;
    1.93 +  // Take list of observers associated with given `listener` function.
    1.94 +  let observers = subscribers(listener);
    1.95 +  // If `observer` for the given `type` is not registered yet, then
    1.96 +  // associate an `observer` and register it.
    1.97 +  if (!(type in observers)) {
    1.98 +    let observer = Observer(listener);
    1.99 +    observers[type] = observer;
   1.100 +    addObserver(observer, type, weak);
   1.101 +    // WeakRef gymnastics to remove all alive observers on unload
   1.102 +    let ref = Cu.getWeakReference(observer);
   1.103 +    weakRefs.set(observer, ref);
   1.104 +    stillAlive.set(ref, type);
   1.105 +  }
   1.106 +}
   1.107 +exports.on = on;
   1.108 +
   1.109 +function once(type, listener) {
   1.110 +  // Note: this code assumes order in which listeners are called, which is fine
   1.111 +  // as long as dispatch happens in same order as listener registration which
   1.112 +  // is the case now. That being said we should be aware that this may break
   1.113 +  // in a future if order will change.
   1.114 +  on(type, listener);
   1.115 +  on(type, function cleanup() {
   1.116 +    off(type, listener);
   1.117 +    off(type, cleanup);
   1.118 +  }, true);
   1.119 +}
   1.120 +exports.once = once;
   1.121 +
   1.122 +function off(type, listener) {
   1.123 +  // Take list of observers as with the given `listener`.
   1.124 +  let observers = subscribers(listener);
   1.125 +  // If `observer` for the given `type` is registered, then
   1.126 +  // remove it & unregister.
   1.127 +  if (type in observers) {
   1.128 +    let observer = observers[type];
   1.129 +    delete observers[type];
   1.130 +    removeObserver(observer, type);
   1.131 +    stillAlive.delete(weakRefs.get(observer));
   1.132 +  }
   1.133 +}
   1.134 +exports.off = off;
   1.135 +
   1.136 +// must use WeakMap to keep reference to all the WeakRefs (!), see bug 986115
   1.137 +let weakRefs = new WeakMap();
   1.138 +
   1.139 +// and we're out of beta, we're releasing on time!
   1.140 +let stillAlive = new Map();   
   1.141 +
   1.142 +on('sdk:loader:destroy', function onunload({ subject, data: reason }) {
   1.143 +  // using logic from ./unload, to avoid a circular module reference
   1.144 +  if (subject.wrappedJSObject === unloadSubject) {
   1.145 +    off('sdk:loader:destroy', onunload);
   1.146 +
   1.147 +    // don't bother
   1.148 +    if (reason === 'shutdown') 
   1.149 +      return;
   1.150 +
   1.151 +    stillAlive.forEach( (type, ref) => {
   1.152 +      let observer = ref.get();
   1.153 +      if (observer) 
   1.154 +        removeObserver(observer, type);
   1.155 +    })
   1.156 +  }
   1.157 +  // a strong reference
   1.158 +}, true);

mercurial