1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/addon-sdk/source/lib/sdk/core/disposable.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,132 @@ 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": "experimental" 1.12 +}; 1.13 + 1.14 + 1.15 +const { Class } = require("./heritage"); 1.16 +const { Observer, subscribe, unsubscribe, observe } = require("./observer"); 1.17 +const { isWeak, WeakReference } = require("./reference"); 1.18 +const method = require("../../method/core"); 1.19 + 1.20 +const unloadSubject = require('@loader/unload'); 1.21 +const addonUnloadTopic = "sdk:loader:destroy"; 1.22 + 1.23 + 1.24 + 1.25 +const uninstall = method("disposable/uninstall"); 1.26 +exports.uninstall = uninstall; 1.27 + 1.28 + 1.29 +const shutdown = method("disposable/shutdown"); 1.30 +exports.shutdown = shutdown; 1.31 + 1.32 +const disable = method("disposable/disable"); 1.33 +exports.disable = disable; 1.34 + 1.35 +const upgrade = method("disposable/upgrade"); 1.36 +exports.upgrade = upgrade; 1.37 + 1.38 +const downgrade = method("disposable/downgrade"); 1.39 +exports.downgrade = downgrade; 1.40 + 1.41 +const unload = method("disposable/unload"); 1.42 +exports.unload = unload; 1.43 + 1.44 +const dispose = method("disposable/dispose"); 1.45 +exports.dispose = dispose; 1.46 +dispose.define(Object, object => object.dispose()); 1.47 + 1.48 + 1.49 +const setup = method("disposable/setup"); 1.50 +exports.setup = setup; 1.51 +setup.define(Object, (object, ...args) => object.setup(...args)); 1.52 + 1.53 + 1.54 +// Set's up disposable instance. 1.55 +const setupDisposable = disposable => { 1.56 + subscribe(disposable, addonUnloadTopic, isWeak(disposable)); 1.57 +}; 1.58 + 1.59 +// Tears down disposable instance. 1.60 +const disposeDisposable = disposable => { 1.61 + unsubscribe(disposable, addonUnloadTopic); 1.62 +}; 1.63 + 1.64 +// Base type that takes care of disposing it's instances on add-on unload. 1.65 +// Also makes sure to remove unload listener if it's already being disposed. 1.66 +const Disposable = Class({ 1.67 + implements: [Observer], 1.68 + initialize: function(...args) { 1.69 + // First setup instance before initializing it's disposal. If instance 1.70 + // fails to initialize then there is no instance to be disposed at the 1.71 + // unload. 1.72 + setup(this, ...args); 1.73 + setupDisposable(this); 1.74 + }, 1.75 + destroy: function(reason) { 1.76 + // Destroying disposable removes unload handler so that attempt to dispose 1.77 + // won't be made at unload & delegates to dispose. 1.78 + disposeDisposable(this); 1.79 + unload(this, reason); 1.80 + }, 1.81 + setup: function() { 1.82 + // Implement your initialize logic here. 1.83 + }, 1.84 + dispose: function() { 1.85 + // Implement your cleanup logic here. 1.86 + } 1.87 +}); 1.88 +exports.Disposable = Disposable; 1.89 + 1.90 +// Disposable instances observe add-on unload notifications in 1.91 +// order to trigger `unload` on them. 1.92 +observe.define(Disposable, (disposable, subject, topic, data) => { 1.93 + const isUnloadTopic = topic === addonUnloadTopic; 1.94 + const isUnloadSubject = subject.wrappedJSObject === unloadSubject; 1.95 + if (isUnloadTopic && isUnloadSubject) { 1.96 + unsubscribe(disposable, topic); 1.97 + unload(disposable); 1.98 + } 1.99 +}); 1.100 + 1.101 +const unloaders = { 1.102 + destroy: dispose, 1.103 + uninstall: uninstall, 1.104 + shutdown: shutdown, 1.105 + disable: disable, 1.106 + upgrade: upgrade, 1.107 + downgrade: downgrade 1.108 +} 1.109 +const unloaded = new WeakMap(); 1.110 +unload.define(Disposable, (disposable, reason) => { 1.111 + if (!unloaded.get(disposable)) { 1.112 + unloaded.set(disposable, true); 1.113 + // Pick an unload handler associated with an unload 1.114 + // reason (falling back to destroy if not found) and 1.115 + // delegate unloading to it. 1.116 + const unload = unloaders[reason] || unloaders.destroy; 1.117 + unload(disposable); 1.118 + } 1.119 +}); 1.120 + 1.121 + 1.122 +// If add-on is disabled munally, it's being upgraded, downgraded 1.123 +// or uniststalled `dispose` is invoked to undo any changes that 1.124 +// has being done by it in this session. 1.125 +disable.define(Disposable, dispose); 1.126 +downgrade.define(Disposable, dispose); 1.127 +upgrade.define(Disposable, dispose); 1.128 +uninstall.define(Disposable, dispose); 1.129 + 1.130 +// If application is shut down no dispose is invoked as undo-ing 1.131 +// changes made by instance is likely to just waste of resources & 1.132 +// increase shutdown time. Although specefic components may choose 1.133 +// to implement shutdown handler that does something better. 1.134 +shutdown.define(Disposable, disposable => {}); 1.135 +