michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: module.metadata = { michael@0: "stability": "experimental" michael@0: }; michael@0: michael@0: michael@0: const { Cc, Ci, Cr } = require("chrome"); michael@0: const { Class } = require("./heritage"); michael@0: const { isWeak } = require("./reference"); michael@0: const method = require("../../method/core"); michael@0: michael@0: const { addObserver, removeObserver } = Cc['@mozilla.org/observer-service;1']. michael@0: getService(Ci.nsIObserverService); michael@0: michael@0: michael@0: // This is a method that will be invoked when notification observer michael@0: // subscribed to occurs. michael@0: const observe = method("observer/observe"); michael@0: exports.observe = observe; michael@0: michael@0: // Method to subscribe to the observer notification. michael@0: const subscribe = method("observe/subscribe"); michael@0: exports.subscribe = subscribe; michael@0: michael@0: michael@0: // Method to unsubscribe from the observer notifications. michael@0: const unsubscribe = method("observer/unsubscribe"); michael@0: exports.unsubscribe = unsubscribe; michael@0: michael@0: michael@0: // This is wrapper class that takes a `delegate` and produces michael@0: // instance of `nsIObserver` which will delegate to a given michael@0: // object when observer notification occurs. michael@0: const ObserverDelegee = Class({ michael@0: initialize: function(delegate) { michael@0: this.delegate = delegate; michael@0: }, michael@0: QueryInterface: function(iid) { michael@0: const isObserver = iid.equals(Ci.nsIObserver); michael@0: const isWeakReference = iid.equals(Ci.nsISupportsWeakReference); michael@0: michael@0: if (!isObserver && !isWeakReference) michael@0: throw Cr.NS_ERROR_NO_INTERFACE; michael@0: michael@0: return this; michael@0: }, michael@0: observe: function(subject, topic, data) { michael@0: observe(this.delegate, subject, topic, data); michael@0: } michael@0: }); michael@0: michael@0: michael@0: // Class that can be either mixed in or inherited from in michael@0: // order to subscribe / unsubscribe for observer notifications. michael@0: const Observer = Class({}); michael@0: exports.Observer = Observer; michael@0: michael@0: // Weak maps that associates instance of `ObserverDelegee` with michael@0: // an actual observer. It ensures that `ObserverDelegee` instance michael@0: // won't be GC-ed until given `observer` is. michael@0: const subscribers = new WeakMap(); michael@0: michael@0: // Implementation of `subscribe` for `Observer` type just registers michael@0: // observer for an observer service. If `isWeak(observer)` is `true` michael@0: // observer service won't hold strong reference to a given `observer`. michael@0: subscribe.define(Observer, (observer, topic) => { michael@0: if (!subscribers.has(observer)) { michael@0: const delegee = new ObserverDelegee(observer); michael@0: subscribers.set(observer, delegee); michael@0: addObserver(delegee, topic, isWeak(observer)); michael@0: } michael@0: }); michael@0: michael@0: // Unsubscribes `observer` from observer notifications for the michael@0: // given `topic`. michael@0: unsubscribe.define(Observer, (observer, topic) => { michael@0: const delegee = subscribers.get(observer); michael@0: if (delegee) { michael@0: subscribers.delete(observer); michael@0: removeObserver(delegee, topic); michael@0: } michael@0: });