|
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 { Class } = require("./heritage"); |
|
13 const { Observer, subscribe, unsubscribe, observe } = require("./observer"); |
|
14 const { isWeak, WeakReference } = require("./reference"); |
|
15 const method = require("../../method/core"); |
|
16 |
|
17 const unloadSubject = require('@loader/unload'); |
|
18 const addonUnloadTopic = "sdk:loader:destroy"; |
|
19 |
|
20 |
|
21 |
|
22 const uninstall = method("disposable/uninstall"); |
|
23 exports.uninstall = uninstall; |
|
24 |
|
25 |
|
26 const shutdown = method("disposable/shutdown"); |
|
27 exports.shutdown = shutdown; |
|
28 |
|
29 const disable = method("disposable/disable"); |
|
30 exports.disable = disable; |
|
31 |
|
32 const upgrade = method("disposable/upgrade"); |
|
33 exports.upgrade = upgrade; |
|
34 |
|
35 const downgrade = method("disposable/downgrade"); |
|
36 exports.downgrade = downgrade; |
|
37 |
|
38 const unload = method("disposable/unload"); |
|
39 exports.unload = unload; |
|
40 |
|
41 const dispose = method("disposable/dispose"); |
|
42 exports.dispose = dispose; |
|
43 dispose.define(Object, object => object.dispose()); |
|
44 |
|
45 |
|
46 const setup = method("disposable/setup"); |
|
47 exports.setup = setup; |
|
48 setup.define(Object, (object, ...args) => object.setup(...args)); |
|
49 |
|
50 |
|
51 // Set's up disposable instance. |
|
52 const setupDisposable = disposable => { |
|
53 subscribe(disposable, addonUnloadTopic, isWeak(disposable)); |
|
54 }; |
|
55 |
|
56 // Tears down disposable instance. |
|
57 const disposeDisposable = disposable => { |
|
58 unsubscribe(disposable, addonUnloadTopic); |
|
59 }; |
|
60 |
|
61 // Base type that takes care of disposing it's instances on add-on unload. |
|
62 // Also makes sure to remove unload listener if it's already being disposed. |
|
63 const Disposable = Class({ |
|
64 implements: [Observer], |
|
65 initialize: function(...args) { |
|
66 // First setup instance before initializing it's disposal. If instance |
|
67 // fails to initialize then there is no instance to be disposed at the |
|
68 // unload. |
|
69 setup(this, ...args); |
|
70 setupDisposable(this); |
|
71 }, |
|
72 destroy: function(reason) { |
|
73 // Destroying disposable removes unload handler so that attempt to dispose |
|
74 // won't be made at unload & delegates to dispose. |
|
75 disposeDisposable(this); |
|
76 unload(this, reason); |
|
77 }, |
|
78 setup: function() { |
|
79 // Implement your initialize logic here. |
|
80 }, |
|
81 dispose: function() { |
|
82 // Implement your cleanup logic here. |
|
83 } |
|
84 }); |
|
85 exports.Disposable = Disposable; |
|
86 |
|
87 // Disposable instances observe add-on unload notifications in |
|
88 // order to trigger `unload` on them. |
|
89 observe.define(Disposable, (disposable, subject, topic, data) => { |
|
90 const isUnloadTopic = topic === addonUnloadTopic; |
|
91 const isUnloadSubject = subject.wrappedJSObject === unloadSubject; |
|
92 if (isUnloadTopic && isUnloadSubject) { |
|
93 unsubscribe(disposable, topic); |
|
94 unload(disposable); |
|
95 } |
|
96 }); |
|
97 |
|
98 const unloaders = { |
|
99 destroy: dispose, |
|
100 uninstall: uninstall, |
|
101 shutdown: shutdown, |
|
102 disable: disable, |
|
103 upgrade: upgrade, |
|
104 downgrade: downgrade |
|
105 } |
|
106 const unloaded = new WeakMap(); |
|
107 unload.define(Disposable, (disposable, reason) => { |
|
108 if (!unloaded.get(disposable)) { |
|
109 unloaded.set(disposable, true); |
|
110 // Pick an unload handler associated with an unload |
|
111 // reason (falling back to destroy if not found) and |
|
112 // delegate unloading to it. |
|
113 const unload = unloaders[reason] || unloaders.destroy; |
|
114 unload(disposable); |
|
115 } |
|
116 }); |
|
117 |
|
118 |
|
119 // If add-on is disabled munally, it's being upgraded, downgraded |
|
120 // or uniststalled `dispose` is invoked to undo any changes that |
|
121 // has being done by it in this session. |
|
122 disable.define(Disposable, dispose); |
|
123 downgrade.define(Disposable, dispose); |
|
124 upgrade.define(Disposable, dispose); |
|
125 uninstall.define(Disposable, dispose); |
|
126 |
|
127 // If application is shut down no dispose is invoked as undo-ing |
|
128 // changes made by instance is likely to just waste of resources & |
|
129 // increase shutdown time. Although specefic components may choose |
|
130 // to implement shutdown handler that does something better. |
|
131 shutdown.define(Disposable, disposable => {}); |
|
132 |