forked from mirrors/gecko-dev
		
	 81cb5e57b9
			
		
	
	
		81cb5e57b9
		
	
	
	
	
		
			
			MozReview-Commit-ID: EjyAssqiQk8 --HG-- extra : rebase_source : d783829bc7fced3044d0d076c4786a6957d29bb6
		
			
				
	
	
		
			149 lines
		
	
	
	
		
			5.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			149 lines
		
	
	
	
		
			5.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| this.EXPORTED_SYMBOLS = ["Observers"];
 | |
| 
 | |
| var Cc = Components.classes;
 | |
| var Ci = Components.interfaces;
 | |
| var Cr = Components.results;
 | |
| var Cu = Components.utils;
 | |
| 
 | |
| Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 | |
| 
 | |
| /**
 | |
|  * A service for adding, removing and notifying observers of notifications.
 | |
|  * Wraps the nsIObserverService interface.
 | |
|  *
 | |
|  * @version 0.2
 | |
|  */
 | |
| this.Observers = {
 | |
|   /**
 | |
|    * Register the given callback as an observer of the given topic.
 | |
|    *
 | |
|    * @param   topic       {String}
 | |
|    *          the topic to observe
 | |
|    *
 | |
|    * @param   callback    {Object}
 | |
|    *          the callback; an Object that implements nsIObserver or a Function
 | |
|    *          that gets called when the notification occurs
 | |
|    *
 | |
|    * @param   thisObject  {Object}  [optional]
 | |
|    *          the object to use as |this| when calling a Function callback
 | |
|    *
 | |
|    * @returns the observer
 | |
|    */
 | |
|   add(topic, callback, thisObject) {
 | |
|     let observer = new Observer(topic, callback, thisObject);
 | |
|     this._cache.push(observer);
 | |
|     this._service.addObserver(observer, topic, true);
 | |
| 
 | |
|     return observer;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Unregister the given callback as an observer of the given topic.
 | |
|    *
 | |
|    * @param topic       {String}
 | |
|    *        the topic being observed
 | |
|    *
 | |
|    * @param callback    {Object}
 | |
|    *        the callback doing the observing
 | |
|    *
 | |
|    * @param thisObject  {Object}  [optional]
 | |
|    *        the object being used as |this| when calling a Function callback
 | |
|    */
 | |
|   remove(topic, callback, thisObject) {
 | |
|     // This seems fairly inefficient, but I'm not sure how much better
 | |
|     // we can make it.  We could index by topic, but we can't index by callback
 | |
|     // or thisObject, as far as I know, since the keys to JavaScript hashes
 | |
|     // (a.k.a. objects) can apparently only be primitive values.
 | |
|     let [observer] = this._cache.filter(v => v.topic == topic &&
 | |
|                                              v.callback == callback &&
 | |
|                                              v.thisObject == thisObject);
 | |
|     if (observer) {
 | |
|       this._service.removeObserver(observer, topic);
 | |
|       this._cache.splice(this._cache.indexOf(observer), 1);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Notify observers about something.
 | |
|    *
 | |
|    * @param topic   {String}
 | |
|    *        the topic to notify observers about
 | |
|    *
 | |
|    * @param subject {Object}  [optional]
 | |
|    *        some information about the topic; can be any JS object or primitive
 | |
|    *
 | |
|    * @param data    {String}  [optional] [deprecated]
 | |
|    *        some more information about the topic; deprecated as the subject
 | |
|    *        is sufficient to pass all needed information to the JS observers
 | |
|    *        that this module targets; if you have multiple values to pass to
 | |
|    *        the observer, wrap them in an object and pass them via the subject
 | |
|    *        parameter (i.e.: { foo: 1, bar: "some string", baz: myObject })
 | |
|    */
 | |
|   notify(topic, subject, data) {
 | |
|     subject = (typeof subject == "undefined") ? null : new Subject(subject);
 | |
|        data = (typeof data == "undefined") ? null : data;
 | |
|     this._service.notifyObservers(subject, topic, data);
 | |
|   },
 | |
| 
 | |
|   _service: Cc["@mozilla.org/observer-service;1"].
 | |
|             getService(Ci.nsIObserverService),
 | |
| 
 | |
|   /**
 | |
|    * A cache of observers that have been added.
 | |
|    *
 | |
|    * We use this to remove observers when a caller calls |remove|.
 | |
|    *
 | |
|    * XXX This might result in reference cycles, causing memory leaks,
 | |
|    * if we hold a reference to an observer that holds a reference to us.
 | |
|    * Could we fix that by making this an independent top-level object
 | |
|    * rather than a property of this object?
 | |
|    */
 | |
|   _cache: []
 | |
| };
 | |
| 
 | |
| 
 | |
| function Observer(topic, callback, thisObject) {
 | |
|   this.topic = topic;
 | |
|   this.callback = callback;
 | |
|   this.thisObject = thisObject;
 | |
| }
 | |
| 
 | |
| Observer.prototype = {
 | |
|   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
 | |
|   observe(subject, topic, data) {
 | |
|     // Extract the wrapped object for subjects that are one of our wrappers
 | |
|     // around a JS object.  This way we support both wrapped subjects created
 | |
|     // using this module and those that are real XPCOM components.
 | |
|     if (subject && typeof subject == "object" &&
 | |
|         ("wrappedJSObject" in subject) &&
 | |
|         ("observersModuleSubjectWrapper" in subject.wrappedJSObject))
 | |
|       subject = subject.wrappedJSObject.object;
 | |
| 
 | |
|     if (typeof this.callback == "function") {
 | |
|       if (this.thisObject)
 | |
|         this.callback.call(this.thisObject, subject, data);
 | |
|       else
 | |
|         this.callback(subject, data);
 | |
|     } else // typeof this.callback == "object" (nsIObserver)
 | |
|       this.callback.observe(subject, topic, data);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| function Subject(object) {
 | |
|   // Double-wrap the object and set a property identifying the wrappedJSObject
 | |
|   // as one of our wrappers to distinguish between subjects that are one of our
 | |
|   // wrappers (which we should unwrap when notifying our observers) and those
 | |
|   // that are real JS XPCOM components (which we should pass through unaltered).
 | |
|   this.wrappedJSObject = { observersModuleSubjectWrapper: true, object };
 | |
| }
 | |
| 
 | |
| Subject.prototype = {
 | |
|   QueryInterface: XPCOMUtils.generateQI([]),
 | |
|   getScriptableHelper() {},
 | |
|   getInterfaces() {}
 | |
| };
 |