1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
/* 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/. */
// Parts of this module were taken from narwhal:
//
// http://narwhaljs.org
module.metadata = {
"stability": "experimental"
};
const { Cu } = require('chrome');
const { on, off } = require('./events');
const unloadSubject = require('@loader/unload');
const observers = [];
const unloaders = [];
function WeakObserver(inner) {
this._inner = Cu.getWeakReference(inner);
}
Object.defineProperty(WeakObserver.prototype, 'value', {
get: function() { this._inner.get() }
});
var when = exports.when = function when(observer, opts) {
opts = opts || {};
for (var i = 0; i < observers.length; ++i) {
if (observers[i] === observer || observers[i].value === observer) {
return;
}
}
if (opts.weak) {
observers.unshift(new WeakObserver(observer));
} else {
observers.unshift(observer);
}
};
var ensure = exports.ensure = function ensure(obj, destructorName) {
if (!destructorName)
destructorName = "unload";
if (!(destructorName in obj))
throw new Error("object has no '" + destructorName + "' property");
let called = false;
let originalDestructor = obj[destructorName];
function unloadWrapper(reason) {
if (!called) {
called = true;
let index = unloaders.indexOf(unloadWrapper);
if (index == -1)
throw new Error("internal error: unloader not found");
unloaders.splice(index, 1);
originalDestructor.call(obj, reason);
originalDestructor = null;
destructorName = null;
obj = null;
}
};
// TODO: Find out why the order is inverted here. It seems that
// it may be causing issues!
unloaders.push(unloadWrapper);
obj[destructorName] = unloadWrapper;
};
function unload(reason) {
observers.forEach(function(observer) {
try {
if (observer instanceof WeakObserver) {
observer = observer.value;
}
if (typeof observer === 'function') {
observer(reason);
}
}
catch (error) {
console.exception(error);
}
});
}
when(function(reason) {
unloaders.slice().forEach(function(unloadWrapper) {
unloadWrapper(reason);
});
});
on('sdk:loader:destroy', function onunload({ subject, data: reason }) {
// If this loader is unload then `subject.wrappedJSObject` will be
// `destructor`.
if (subject.wrappedJSObject === unloadSubject) {
off('sdk:loader:destroy', onunload);
unload(reason);
}
// Note that we use strong reference to listener here to make sure it's not
// GC-ed, which may happen otherwise since nothing keeps reference to `onunolad`
// function.
}, true);
|