|
1 /* Copyright 2012 Mozilla Foundation and Mozilla contributors |
|
2 * |
|
3 * Licensed under the Apache License, Version 2.0 (the "License"); |
|
4 * you may not use this file except in compliance with the License. |
|
5 * You may obtain a copy of the License at |
|
6 * |
|
7 * http://www.apache.org/licenses/LICENSE-2.0 |
|
8 * |
|
9 * Unless required by applicable law or agreed to in writing, software |
|
10 * distributed under the License is distributed on an "AS IS" BASIS, |
|
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
12 * See the License for the specific language governing permissions and |
|
13 * limitations under the License. |
|
14 */ |
|
15 |
|
16 /* Copyright © 2013, Deutsche Telekom, Inc. */ |
|
17 |
|
18 "use strict"; |
|
19 |
|
20 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; |
|
21 |
|
22 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
23 Cu.import("resource://gre/modules/Services.jsm"); |
|
24 Cu.import("resource://gre/modules/DOMRequestHelper.jsm"); |
|
25 |
|
26 let NFC = {}; |
|
27 Cu.import("resource://gre/modules/nfc_consts.js", NFC); |
|
28 |
|
29 Cu.import("resource://gre/modules/systemlibs.js"); |
|
30 const NFC_ENABLED = libcutils.property_get("ro.moz.nfc.enabled", "false") === "true"; |
|
31 |
|
32 // set to true to in nfc_consts.js to see debug messages |
|
33 let DEBUG = NFC.DEBUG_CONTENT_HELPER; |
|
34 |
|
35 let debug; |
|
36 if (DEBUG) { |
|
37 debug = function (s) { |
|
38 dump("-*- NfcContentHelper: " + s + "\n"); |
|
39 }; |
|
40 } else { |
|
41 debug = function (s) {}; |
|
42 } |
|
43 |
|
44 const NFCCONTENTHELPER_CID = |
|
45 Components.ID("{4d72c120-da5f-11e1-9b23-0800200c9a66}"); |
|
46 |
|
47 const NFC_IPC_MSG_NAMES = [ |
|
48 "NFC:ReadNDEFResponse", |
|
49 "NFC:WriteNDEFResponse", |
|
50 "NFC:GetDetailsNDEFResponse", |
|
51 "NFC:MakeReadOnlyNDEFResponse", |
|
52 "NFC:ConnectResponse", |
|
53 "NFC:CloseResponse", |
|
54 "NFC:CheckP2PRegistrationResponse", |
|
55 "NFC:PeerEvent", |
|
56 "NFC:NotifySendFileStatusResponse", |
|
57 "NFC:ConfigResponse" |
|
58 ]; |
|
59 |
|
60 XPCOMUtils.defineLazyServiceGetter(this, "cpmm", |
|
61 "@mozilla.org/childprocessmessagemanager;1", |
|
62 "nsISyncMessageSender"); |
|
63 |
|
64 function GetDetailsNDEFResponse(details) { |
|
65 this.canBeMadeReadOnly = details.canBeMadeReadOnly; |
|
66 this.isReadOnly = details.isReadOnly; |
|
67 this.maxSupportedLength = details.maxSupportedLength; |
|
68 } |
|
69 GetDetailsNDEFResponse.prototype = { |
|
70 __exposedProps__ : {canBeMadeReadOnly: 'r', |
|
71 isReadOnly: 'r', |
|
72 maxSupportedLength: 'r'} |
|
73 }; |
|
74 |
|
75 function NfcContentHelper() { |
|
76 this.initDOMRequestHelper(/* aWindow */ null, NFC_IPC_MSG_NAMES); |
|
77 Services.obs.addObserver(this, "xpcom-shutdown", false); |
|
78 |
|
79 this._requestMap = []; |
|
80 |
|
81 // Maintains an array of PeerEvent related callbacks, mainly |
|
82 // one for 'peerReady' and another for 'peerLost'. |
|
83 this.peerEventsCallbackMap = {}; |
|
84 } |
|
85 |
|
86 NfcContentHelper.prototype = { |
|
87 __proto__: DOMRequestIpcHelper.prototype, |
|
88 |
|
89 QueryInterface: XPCOMUtils.generateQI([Ci.nsINfcContentHelper, |
|
90 Ci.nsISupportsWeakReference, |
|
91 Ci.nsIObserver]), |
|
92 classID: NFCCONTENTHELPER_CID, |
|
93 classInfo: XPCOMUtils.generateCI({ |
|
94 classID: NFCCONTENTHELPER_CID, |
|
95 classDescription: "NfcContentHelper", |
|
96 interfaces: [Ci.nsINfcContentHelper] |
|
97 }), |
|
98 |
|
99 _requestMap: null, |
|
100 peerEventsCallbackMap: null, |
|
101 |
|
102 encodeNDEFRecords: function encodeNDEFRecords(records) { |
|
103 let encodedRecords = []; |
|
104 for (let i = 0; i < records.length; i++) { |
|
105 let record = records[i]; |
|
106 encodedRecords.push({ |
|
107 tnf: record.tnf, |
|
108 type: record.type, |
|
109 id: record.id, |
|
110 payload: record.payload, |
|
111 }); |
|
112 } |
|
113 return encodedRecords; |
|
114 }, |
|
115 |
|
116 // NFC interface: |
|
117 setSessionToken: function setSessionToken(sessionToken) { |
|
118 if (sessionToken == null) { |
|
119 throw Components.Exception("No session token!", |
|
120 Cr.NS_ERROR_UNEXPECTED); |
|
121 return; |
|
122 } |
|
123 // Report session to Nfc.js only. |
|
124 cpmm.sendAsyncMessage("NFC:SetSessionToken", { |
|
125 sessionToken: sessionToken, |
|
126 }); |
|
127 }, |
|
128 |
|
129 // NFCTag interface |
|
130 getDetailsNDEF: function getDetailsNDEF(window, sessionToken) { |
|
131 if (window == null) { |
|
132 throw Components.Exception("Can't get window object", |
|
133 Cr.NS_ERROR_UNEXPECTED); |
|
134 } |
|
135 let request = Services.DOMRequest.createRequest(window); |
|
136 let requestId = btoa(this.getRequestId(request)); |
|
137 this._requestMap[requestId] = window; |
|
138 |
|
139 cpmm.sendAsyncMessage("NFC:GetDetailsNDEF", { |
|
140 requestId: requestId, |
|
141 sessionToken: sessionToken |
|
142 }); |
|
143 return request; |
|
144 }, |
|
145 |
|
146 readNDEF: function readNDEF(window, sessionToken) { |
|
147 if (window == null) { |
|
148 throw Components.Exception("Can't get window object", |
|
149 Cr.NS_ERROR_UNEXPECTED); |
|
150 } |
|
151 let request = Services.DOMRequest.createRequest(window); |
|
152 let requestId = btoa(this.getRequestId(request)); |
|
153 this._requestMap[requestId] = window; |
|
154 |
|
155 cpmm.sendAsyncMessage("NFC:ReadNDEF", { |
|
156 requestId: requestId, |
|
157 sessionToken: sessionToken |
|
158 }); |
|
159 return request; |
|
160 }, |
|
161 |
|
162 writeNDEF: function writeNDEF(window, records, sessionToken) { |
|
163 if (window == null) { |
|
164 throw Components.Exception("Can't get window object", |
|
165 Cr.NS_ERROR_UNEXPECTED); |
|
166 } |
|
167 let request = Services.DOMRequest.createRequest(window); |
|
168 let requestId = btoa(this.getRequestId(request)); |
|
169 this._requestMap[requestId] = window; |
|
170 |
|
171 let encodedRecords = this.encodeNDEFRecords(records); |
|
172 cpmm.sendAsyncMessage("NFC:WriteNDEF", { |
|
173 requestId: requestId, |
|
174 sessionToken: sessionToken, |
|
175 records: encodedRecords |
|
176 }); |
|
177 return request; |
|
178 }, |
|
179 |
|
180 makeReadOnlyNDEF: function makeReadOnlyNDEF(window, sessionToken) { |
|
181 if (window == null) { |
|
182 throw Components.Exception("Can't get window object", |
|
183 Cr.NS_ERROR_UNEXPECTED); |
|
184 } |
|
185 |
|
186 let request = Services.DOMRequest.createRequest(window); |
|
187 let requestId = btoa(this.getRequestId(request)); |
|
188 this._requestMap[requestId] = window; |
|
189 |
|
190 cpmm.sendAsyncMessage("NFC:MakeReadOnlyNDEF", { |
|
191 requestId: requestId, |
|
192 sessionToken: sessionToken |
|
193 }); |
|
194 return request; |
|
195 }, |
|
196 |
|
197 connect: function connect(window, techType, sessionToken) { |
|
198 if (window == null) { |
|
199 throw Components.Exception("Can't get window object", |
|
200 Cr.NS_ERROR_UNEXPECTED); |
|
201 } |
|
202 let request = Services.DOMRequest.createRequest(window); |
|
203 let requestId = btoa(this.getRequestId(request)); |
|
204 this._requestMap[requestId] = window; |
|
205 |
|
206 cpmm.sendAsyncMessage("NFC:Connect", { |
|
207 requestId: requestId, |
|
208 sessionToken: sessionToken, |
|
209 techType: techType |
|
210 }); |
|
211 return request; |
|
212 }, |
|
213 |
|
214 close: function close(window, sessionToken) { |
|
215 if (window == null) { |
|
216 throw Components.Exception("Can't get window object", |
|
217 Cr.NS_ERROR_UNEXPECTED); |
|
218 } |
|
219 let request = Services.DOMRequest.createRequest(window); |
|
220 let requestId = btoa(this.getRequestId(request)); |
|
221 this._requestMap[requestId] = window; |
|
222 |
|
223 cpmm.sendAsyncMessage("NFC:Close", { |
|
224 requestId: requestId, |
|
225 sessionToken: sessionToken |
|
226 }); |
|
227 return request; |
|
228 }, |
|
229 |
|
230 sendFile: function sendFile(window, data, sessionToken) { |
|
231 if (window == null) { |
|
232 throw Components.Exception("Can't get window object", |
|
233 Cr.NS_ERROR_UNEXPECTED); |
|
234 } |
|
235 let request = Services.DOMRequest.createRequest(window); |
|
236 let requestId = btoa(this.getRequestId(request)); |
|
237 this._requestMap[requestId] = window; |
|
238 |
|
239 cpmm.sendAsyncMessage("NFC:SendFile", { |
|
240 requestId: requestId, |
|
241 sessionToken: sessionToken, |
|
242 blob: data.blob |
|
243 }); |
|
244 return request; |
|
245 }, |
|
246 |
|
247 notifySendFileStatus: function notifySendFileStatus(window, status, |
|
248 requestId) { |
|
249 if (window == null) { |
|
250 throw Components.Exception("Can't get window object", |
|
251 Cr.NS_ERROR_UNEXPECTED); |
|
252 } |
|
253 |
|
254 cpmm.sendAsyncMessage("NFC:NotifySendFileStatus", { |
|
255 status: status, |
|
256 requestId: requestId |
|
257 }); |
|
258 }, |
|
259 |
|
260 registerTargetForPeerEvent: function registerTargetForPeerEvent(window, |
|
261 appId, event, callback) { |
|
262 if (window == null) { |
|
263 throw Components.Exception("Can't get window object", |
|
264 Cr.NS_ERROR_UNEXPECTED); |
|
265 } |
|
266 this.peerEventsCallbackMap[event] = callback; |
|
267 cpmm.sendAsyncMessage("NFC:RegisterPeerTarget", { |
|
268 appId: appId, |
|
269 event: event |
|
270 }); |
|
271 }, |
|
272 |
|
273 unregisterTargetForPeerEvent: function unregisterTargetForPeerEvent(window, |
|
274 appId, event) { |
|
275 if (window == null) { |
|
276 throw Components.Exception("Can't get window object", |
|
277 Cr.NS_ERROR_UNEXPECTED); |
|
278 } |
|
279 let callback = this.peerEventsCallbackMap[event]; |
|
280 if (callback != null) { |
|
281 delete this.peerEventsCallbackMap[event]; |
|
282 } |
|
283 |
|
284 cpmm.sendAsyncMessage("NFC:UnregisterPeerTarget", { |
|
285 appId: appId, |
|
286 event: event |
|
287 }); |
|
288 }, |
|
289 |
|
290 checkP2PRegistration: function checkP2PRegistration(window, appId) { |
|
291 if (window == null) { |
|
292 throw Components.Exception("Can't get window object", |
|
293 Cr.NS_ERROR_UNEXPECTED); |
|
294 } |
|
295 let request = Services.DOMRequest.createRequest(window); |
|
296 let requestId = btoa(this.getRequestId(request)); |
|
297 this._requestMap[requestId] = window; |
|
298 |
|
299 cpmm.sendAsyncMessage("NFC:CheckP2PRegistration", { |
|
300 appId: appId, |
|
301 requestId: requestId |
|
302 }); |
|
303 return request; |
|
304 }, |
|
305 |
|
306 notifyUserAcceptedP2P: function notifyUserAcceptedP2P(window, appId) { |
|
307 if (window == null) { |
|
308 throw Components.Exception("Can't get window object", |
|
309 Cr.NS_ERROR_UNEXPECTED); |
|
310 } |
|
311 |
|
312 cpmm.sendAsyncMessage("NFC:NotifyUserAcceptedP2P", { |
|
313 appId: appId |
|
314 }); |
|
315 }, |
|
316 |
|
317 startPoll: function startPoll(window) { |
|
318 if (window == null) { |
|
319 throw Components.Exception("Can't get window object", |
|
320 Cr.NS_ERROR_UNEXPECTED); |
|
321 } |
|
322 |
|
323 let request = Services.DOMRequest.createRequest(window); |
|
324 let requestId = btoa(this.getRequestId(request)); |
|
325 this._requestMap[requestId] = window; |
|
326 |
|
327 cpmm.sendAsyncMessage("NFC:StartPoll", |
|
328 {requestId: requestId}); |
|
329 return request; |
|
330 }, |
|
331 |
|
332 stopPoll: function stopPoll(window) { |
|
333 if (window == null) { |
|
334 throw Components.Exception("Can't get window object", |
|
335 Cr.NS_ERROR_UNEXPECTED); |
|
336 } |
|
337 |
|
338 let request = Services.DOMRequest.createRequest(window); |
|
339 let requestId = btoa(this.getRequestId(request)); |
|
340 this._requestMap[requestId] = window; |
|
341 |
|
342 cpmm.sendAsyncMessage("NFC:StopPoll", |
|
343 {requestId: requestId}); |
|
344 return request; |
|
345 }, |
|
346 |
|
347 powerOff: function powerOff(window) { |
|
348 if (window == null) { |
|
349 throw Components.Exception("Can't get window object", |
|
350 Cr.NS_ERROR_UNEXPECTED); |
|
351 } |
|
352 |
|
353 let request = Services.DOMRequest.createRequest(window); |
|
354 let requestId = btoa(this.getRequestId(request)); |
|
355 this._requestMap[requestId] = window; |
|
356 |
|
357 cpmm.sendAsyncMessage("NFC:PowerOff", |
|
358 {requestId: requestId}); |
|
359 return request; |
|
360 }, |
|
361 |
|
362 // nsIObserver |
|
363 observe: function observe(subject, topic, data) { |
|
364 if (topic == "xpcom-shutdown") { |
|
365 this.destroyDOMRequestHelper(); |
|
366 Services.obs.removeObserver(this, "xpcom-shutdown"); |
|
367 cpmm = null; |
|
368 } |
|
369 }, |
|
370 |
|
371 // nsIMessageListener |
|
372 |
|
373 fireRequestSuccess: function fireRequestSuccess(requestId, result) { |
|
374 let request = this.takeRequest(requestId); |
|
375 if (!request) { |
|
376 debug("not firing success for id: " + requestId + |
|
377 ", result: " + JSON.stringify(result)); |
|
378 return; |
|
379 } |
|
380 |
|
381 debug("fire request success, id: " + requestId + |
|
382 ", result: " + JSON.stringify(result)); |
|
383 Services.DOMRequest.fireSuccess(request, result); |
|
384 }, |
|
385 |
|
386 fireRequestError: function fireRequestError(requestId, error) { |
|
387 let request = this.takeRequest(requestId); |
|
388 if (!request) { |
|
389 debug("not firing error for id: " + requestId + |
|
390 ", error: " + JSON.stringify(error)); |
|
391 return; |
|
392 } |
|
393 |
|
394 debug("fire request error, id: " + requestId + |
|
395 ", result: " + JSON.stringify(error)); |
|
396 Services.DOMRequest.fireError(request, error); |
|
397 }, |
|
398 |
|
399 receiveMessage: function receiveMessage(message) { |
|
400 debug("Message received: " + JSON.stringify(message)); |
|
401 let result = message.json; |
|
402 |
|
403 switch (message.name) { |
|
404 case "NFC:ReadNDEFResponse": |
|
405 this.handleReadNDEFResponse(result); |
|
406 break; |
|
407 case "NFC:GetDetailsNDEFResponse": |
|
408 this.handleGetDetailsNDEFResponse(result); |
|
409 break; |
|
410 case "NFC:ConnectResponse": // Fall through. |
|
411 case "NFC:CloseResponse": |
|
412 case "NFC:WriteNDEFResponse": |
|
413 case "NFC:MakeReadOnlyNDEFResponse": |
|
414 case "NFC:CheckP2PRegistrationResponse": |
|
415 case "NFC:NotifySendFileStatusResponse": |
|
416 case "NFC:ConfigResponse": |
|
417 if (result.status !== NFC.GECKO_NFC_ERROR_SUCCESS) { |
|
418 this.fireRequestError(atob(result.requestId), result.status); |
|
419 } else { |
|
420 this.fireRequestSuccess(atob(result.requestId), result); |
|
421 } |
|
422 break; |
|
423 case "NFC:PeerEvent": |
|
424 let callback = this.peerEventsCallbackMap[result.event]; |
|
425 if (callback) { |
|
426 callback.peerNotification(result.event, result.sessionToken); |
|
427 } else { |
|
428 debug("PeerEvent: No valid callback registered for the event " + |
|
429 result.event); |
|
430 } |
|
431 break; |
|
432 } |
|
433 }, |
|
434 |
|
435 handleReadNDEFResponse: function handleReadNDEFResponse(result) { |
|
436 let requester = this._requestMap[result.requestId]; |
|
437 if (!requester) { |
|
438 debug("Response Invalid requestId=" + result.requestId); |
|
439 return; |
|
440 } |
|
441 delete this._requestMap[result.requestId]; |
|
442 |
|
443 if (result.status !== NFC.GECKO_NFC_ERROR_SUCCESS) { |
|
444 this.fireRequestError(atob(result.requestId), result.status); |
|
445 return; |
|
446 } |
|
447 |
|
448 let requestId = atob(result.requestId); |
|
449 let ndefMsg = []; |
|
450 let records = result.records; |
|
451 for (let i = 0; i < records.length; i++) { |
|
452 let record = records[i]; |
|
453 ndefMsg.push(new requester.MozNDEFRecord(record.tnf, |
|
454 record.type, |
|
455 record.id, |
|
456 record.payload)); |
|
457 } |
|
458 this.fireRequestSuccess(requestId, ndefMsg); |
|
459 }, |
|
460 |
|
461 handleGetDetailsNDEFResponse: function handleGetDetailsNDEFResponse(result) { |
|
462 if (result.status !== NFC.GECKO_NFC_ERROR_SUCCESS) { |
|
463 this.fireRequestError(atob(result.requestId), result.status); |
|
464 return; |
|
465 } |
|
466 |
|
467 let requestId = atob(result.requestId); |
|
468 let result = new GetDetailsNDEFResponse(result); |
|
469 this.fireRequestSuccess(requestId, result); |
|
470 }, |
|
471 }; |
|
472 |
|
473 if (NFC_ENABLED) { |
|
474 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NfcContentHelper]); |
|
475 } |