|
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 "use strict"; |
|
6 |
|
7 module.metadata = { |
|
8 "stability": "experimental" |
|
9 }; |
|
10 |
|
11 |
|
12 const { Cc, Ci, Cr } = require("chrome"); |
|
13 const { Class } = require("./heritage"); |
|
14 const { isWeak } = require("./reference"); |
|
15 const method = require("../../method/core"); |
|
16 |
|
17 const { addObserver, removeObserver } = Cc['@mozilla.org/observer-service;1']. |
|
18 getService(Ci.nsIObserverService); |
|
19 |
|
20 |
|
21 // This is a method that will be invoked when notification observer |
|
22 // subscribed to occurs. |
|
23 const observe = method("observer/observe"); |
|
24 exports.observe = observe; |
|
25 |
|
26 // Method to subscribe to the observer notification. |
|
27 const subscribe = method("observe/subscribe"); |
|
28 exports.subscribe = subscribe; |
|
29 |
|
30 |
|
31 // Method to unsubscribe from the observer notifications. |
|
32 const unsubscribe = method("observer/unsubscribe"); |
|
33 exports.unsubscribe = unsubscribe; |
|
34 |
|
35 |
|
36 // This is wrapper class that takes a `delegate` and produces |
|
37 // instance of `nsIObserver` which will delegate to a given |
|
38 // object when observer notification occurs. |
|
39 const ObserverDelegee = Class({ |
|
40 initialize: function(delegate) { |
|
41 this.delegate = delegate; |
|
42 }, |
|
43 QueryInterface: function(iid) { |
|
44 const isObserver = iid.equals(Ci.nsIObserver); |
|
45 const isWeakReference = iid.equals(Ci.nsISupportsWeakReference); |
|
46 |
|
47 if (!isObserver && !isWeakReference) |
|
48 throw Cr.NS_ERROR_NO_INTERFACE; |
|
49 |
|
50 return this; |
|
51 }, |
|
52 observe: function(subject, topic, data) { |
|
53 observe(this.delegate, subject, topic, data); |
|
54 } |
|
55 }); |
|
56 |
|
57 |
|
58 // Class that can be either mixed in or inherited from in |
|
59 // order to subscribe / unsubscribe for observer notifications. |
|
60 const Observer = Class({}); |
|
61 exports.Observer = Observer; |
|
62 |
|
63 // Weak maps that associates instance of `ObserverDelegee` with |
|
64 // an actual observer. It ensures that `ObserverDelegee` instance |
|
65 // won't be GC-ed until given `observer` is. |
|
66 const subscribers = new WeakMap(); |
|
67 |
|
68 // Implementation of `subscribe` for `Observer` type just registers |
|
69 // observer for an observer service. If `isWeak(observer)` is `true` |
|
70 // observer service won't hold strong reference to a given `observer`. |
|
71 subscribe.define(Observer, (observer, topic) => { |
|
72 if (!subscribers.has(observer)) { |
|
73 const delegee = new ObserverDelegee(observer); |
|
74 subscribers.set(observer, delegee); |
|
75 addObserver(delegee, topic, isWeak(observer)); |
|
76 } |
|
77 }); |
|
78 |
|
79 // Unsubscribes `observer` from observer notifications for the |
|
80 // given `topic`. |
|
81 unsubscribe.define(Observer, (observer, topic) => { |
|
82 const delegee = subscribers.get(observer); |
|
83 if (delegee) { |
|
84 subscribers.delete(observer); |
|
85 removeObserver(delegee, topic); |
|
86 } |
|
87 }); |