|
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 const events = require("sdk/system/events"); |
|
6 const self = require("sdk/self"); |
|
7 const { Cc, Ci, Cu } = require("chrome"); |
|
8 const { setTimeout } = require("sdk/timers"); |
|
9 const { Loader, LoaderWithHookedConsole2 } = require("sdk/test/loader"); |
|
10 const nsIObserverService = Cc["@mozilla.org/observer-service;1"]. |
|
11 getService(Ci.nsIObserverService); |
|
12 |
|
13 let isConsoleEvent = (topic) => |
|
14 !!~["console-api-log-event", "console-storage-cache-event"].indexOf(topic) |
|
15 |
|
16 exports["test basic"] = function(assert) { |
|
17 let type = Date.now().toString(32); |
|
18 |
|
19 let timesCalled = 0; |
|
20 function handler({subject, data}) { timesCalled++; }; |
|
21 |
|
22 events.on(type, handler); |
|
23 events.emit(type, { data: "yo yo" }); |
|
24 |
|
25 assert.equal(timesCalled, 1, "event handler was called"); |
|
26 |
|
27 events.off(type, handler); |
|
28 events.emit(type, { data: "no way" }); |
|
29 |
|
30 assert.equal(timesCalled, 1, "removed handler is no longer called"); |
|
31 |
|
32 events.once(type, handler); |
|
33 events.emit(type, { data: "and we meet again" }); |
|
34 events.emit(type, { data: "it's always hard to say bye" }); |
|
35 |
|
36 assert.equal(timesCalled, 2, "handlers added via once are triggered once"); |
|
37 } |
|
38 |
|
39 exports["test simple argument passing"] = function (assert) { |
|
40 let type = Date.now().toString(32); |
|
41 |
|
42 let lastArg; |
|
43 function handler({data}) { lastArg = data; } |
|
44 events.on(type, handler); |
|
45 |
|
46 [true, false, 100, 0, 'a string', ''].forEach(arg => { |
|
47 events.emit(type, arg); |
|
48 assert.strictEqual(lastArg, arg + '', |
|
49 'event emitted for ' + arg + ' has correct data value'); |
|
50 |
|
51 events.emit(type, { data: arg }); |
|
52 assert.strictEqual(lastArg, arg + '', |
|
53 'event emitted for ' + arg + ' has correct data value when a property on an object'); |
|
54 }); |
|
55 |
|
56 [null, undefined, {}].forEach(arg => { |
|
57 events.emit(type, arg); |
|
58 assert.strictEqual(lastArg, null, |
|
59 'emitting ' + arg + ' gets null data'); |
|
60 }); |
|
61 |
|
62 events.off(type, handler); |
|
63 }; |
|
64 |
|
65 exports["test error reporting"] = function(assert) { |
|
66 let { loader, messages } = LoaderWithHookedConsole2(module); |
|
67 |
|
68 let events = loader.require("sdk/system/events"); |
|
69 function brokenHandler(subject, data) { throw new Error("foo"); }; |
|
70 |
|
71 let lineNumber; |
|
72 try { brokenHandler() } catch (error) { lineNumber = error.lineNumber } |
|
73 |
|
74 let errorType = Date.now().toString(32); |
|
75 |
|
76 events.on(errorType, brokenHandler); |
|
77 events.emit(errorType, { data: "yo yo" }); |
|
78 |
|
79 assert.equal(messages.length, 2, "Got an exception"); |
|
80 assert.equal(messages[0], "console.error: " + self.name + ": \n", |
|
81 "error is logged"); |
|
82 let text = messages[1]; |
|
83 assert.ok(text.indexOf("Error: foo") >= 0, "error message is logged"); |
|
84 assert.ok(text.indexOf(module.uri) >= 0, "module uri is logged"); |
|
85 assert.ok(text.indexOf(lineNumber) >= 0, "error line is logged"); |
|
86 |
|
87 events.off(errorType, brokenHandler); |
|
88 |
|
89 loader.unload(); |
|
90 }; |
|
91 |
|
92 exports["test listeners are GC-ed"] = function(assert, done) { |
|
93 let receivedFromWeak = []; |
|
94 let receivedFromStrong = []; |
|
95 let loader = Loader(module); |
|
96 let events = loader.require('sdk/system/events'); |
|
97 |
|
98 let type = 'test-listeners-are-garbage-collected'; |
|
99 function handler(event) { receivedFromStrong.push(event); } |
|
100 function weakHandler(event) { receivedFromWeak.push(event); } |
|
101 |
|
102 events.on(type, handler, true); |
|
103 events.on(type, weakHandler); |
|
104 |
|
105 events.emit(type, { data: 1 }); |
|
106 assert.equal(receivedFromStrong.length, 1, "strong listener invoked"); |
|
107 assert.equal(receivedFromWeak.length, 1, "weak listener invoked"); |
|
108 |
|
109 handler = weakHandler = null; |
|
110 |
|
111 Cu.schedulePreciseGC(function() { |
|
112 events.emit(type, { data: 2 }); |
|
113 |
|
114 assert.equal(receivedFromWeak.length, 1, "weak listener was GC-ed"); |
|
115 assert.equal(receivedFromStrong.length, 2, "strong listener was invoked"); |
|
116 |
|
117 loader.unload(); |
|
118 done(); |
|
119 }); |
|
120 }; |
|
121 |
|
122 exports["test alive listeners are removed on unload"] = function(assert) { |
|
123 let receivedFromWeak = []; |
|
124 let receivedFromStrong = []; |
|
125 let loader = Loader(module); |
|
126 let events = loader.require('sdk/system/events'); |
|
127 |
|
128 let type = 'test-alive-listeners-are-removed'; |
|
129 const handler = (event) => receivedFromStrong.push(event); |
|
130 const weakHandler = (event) => receivedFromWeak.push(event); |
|
131 |
|
132 events.on(type, handler, true); |
|
133 events.on(type, weakHandler); |
|
134 |
|
135 events.emit(type, { data: 1 }); |
|
136 assert.equal(receivedFromStrong.length, 1, "strong listener invoked"); |
|
137 assert.equal(receivedFromWeak.length, 1, "weak listener invoked"); |
|
138 |
|
139 loader.unload(); |
|
140 events.emit(type, { data: 2 }); |
|
141 |
|
142 assert.equal(receivedFromWeak.length, 1, "weak listener was removed"); |
|
143 assert.equal(receivedFromStrong.length, 1, "strong listener was removed"); |
|
144 }; |
|
145 |
|
146 exports["test handle nsIObserverService notifications"] = function(assert) { |
|
147 let ios = Cc['@mozilla.org/network/io-service;1'] |
|
148 .getService(Ci.nsIIOService); |
|
149 |
|
150 let uri = ios.newURI("http://www.foo.com", null, null); |
|
151 |
|
152 let type = Date.now().toString(32); |
|
153 let timesCalled = 0; |
|
154 let lastSubject = null; |
|
155 let lastData = null; |
|
156 let lastType = null; |
|
157 |
|
158 function handler({ subject, data, type }) { |
|
159 // Ignores internal console events |
|
160 if (isConsoleEvent(type)) |
|
161 return; |
|
162 timesCalled++; |
|
163 lastSubject = subject; |
|
164 lastData = data; |
|
165 lastType = type; |
|
166 }; |
|
167 |
|
168 events.on(type, handler); |
|
169 nsIObserverService.notifyObservers(uri, type, "some data"); |
|
170 |
|
171 assert.equal(timesCalled, 1, "notification invokes handler"); |
|
172 assert.equal(lastType, type, "event.type is notification topic"); |
|
173 assert.equal(lastSubject, uri, "event.subject is notification subject"); |
|
174 assert.equal(lastData, "some data", "event.data is notification data"); |
|
175 |
|
176 function customSubject() {} |
|
177 function customData() {} |
|
178 |
|
179 events.emit(type, { data: customData, subject: customSubject }); |
|
180 |
|
181 assert.equal(timesCalled, 2, "notification invokes handler"); |
|
182 assert.equal(lastType, type, "event.type is notification topic"); |
|
183 assert.equal(lastSubject, customSubject, |
|
184 "event.subject is wrapped & unwrapped"); |
|
185 assert.equal(lastData, customData, "event.data is wrapped & unwrapped"); |
|
186 |
|
187 events.off(type, handler); |
|
188 |
|
189 nsIObserverService.notifyObservers(null, type, "some data"); |
|
190 |
|
191 assert.equal(timesCalled, 2, "event handler is removed"); |
|
192 |
|
193 events.on("*", handler); |
|
194 |
|
195 nsIObserverService.notifyObservers(null, type, "more data"); |
|
196 |
|
197 assert.equal(timesCalled, 3, "notification invokes * handler"); |
|
198 assert.equal(lastType, type, "event.type is notification topic"); |
|
199 assert.equal(lastSubject, null, |
|
200 "event.subject is notification subject"); |
|
201 assert.equal(lastData, "more data", "event.data is notification data"); |
|
202 |
|
203 events.off("*", handler); |
|
204 |
|
205 nsIObserverService.notifyObservers(null, type, "last data"); |
|
206 |
|
207 assert.equal(timesCalled, 3, "* event handler is removed"); |
|
208 }; |
|
209 |
|
210 exports["test emit to nsIObserverService observers"] = function(assert) { |
|
211 let ios = Cc['@mozilla.org/network/io-service;1'] |
|
212 .getService(Ci.nsIIOService); |
|
213 |
|
214 let uri = ios.newURI("http://www.foo.com", null, null); |
|
215 let timesCalled = 0; |
|
216 let lastSubject = null; |
|
217 let lastData = null; |
|
218 let lastTopic = null; |
|
219 |
|
220 var topic = Date.now().toString(32) |
|
221 let nsIObserver = { |
|
222 QueryInterface: function() { |
|
223 return nsIObserver; |
|
224 }, |
|
225 observe: function(subject, topic, data) { |
|
226 // Ignores internal console events |
|
227 if (isConsoleEvent(topic)) |
|
228 return; |
|
229 timesCalled = timesCalled + 1; |
|
230 lastSubject = subject; |
|
231 lastData = data; |
|
232 lastTopic = topic; |
|
233 } |
|
234 }; |
|
235 |
|
236 nsIObserverService.addObserver(nsIObserver, topic, false); |
|
237 |
|
238 events.emit(topic, { subject: uri, data: "some data" }); |
|
239 |
|
240 assert.equal(timesCalled, 1, "emit notifies observers"); |
|
241 assert.equal(lastTopic, topic, "event type is notification topic"); |
|
242 assert.equal(lastSubject.wrappedJSObject.object, uri, |
|
243 "event.subject is notification subject"); |
|
244 assert.equal(lastData, "some data", "event.data is notification data"); |
|
245 function customSubject() {} |
|
246 function customData() {} |
|
247 events.emit(topic, { subject: customSubject, data: customData }); |
|
248 |
|
249 assert.equal(timesCalled, 2, "emit notifies observers"); |
|
250 assert.equal(lastTopic, topic, "event.type is notification"); |
|
251 assert.equal(lastSubject.wrappedJSObject.object, customSubject, |
|
252 "event.subject is notification subject"); |
|
253 assert.equal(lastData, customData, "event.data is notification data"); |
|
254 |
|
255 nsIObserverService.removeObserver(nsIObserver, topic); |
|
256 |
|
257 events.emit(topic, { data: "more data" }); |
|
258 |
|
259 assert.equal(timesCalled, 2, "removed observers no longer invoked"); |
|
260 |
|
261 nsIObserverService.addObserver(nsIObserver, "*", false); |
|
262 |
|
263 events.emit(topic, { data: "data again" }); |
|
264 |
|
265 assert.equal(timesCalled, 3, "emit notifies * observers"); |
|
266 |
|
267 assert.equal(lastTopic, topic, "event.type is notification"); |
|
268 assert.equal(lastSubject, null, |
|
269 "event.subject is notification subject"); |
|
270 assert.equal(lastData, "data again", "event.data is notification data"); |
|
271 |
|
272 nsIObserverService.removeObserver(nsIObserver, "*"); |
|
273 |
|
274 events.emit(topic, { data: "last data" }); |
|
275 assert.equal(timesCalled, 3, "removed observers no longer invoked"); |
|
276 } |
|
277 |
|
278 require("test").run(exports); |