Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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 */
16 /* Copyright © 2013, Deutsche Telekom, Inc. */
18 "use strict";
20 importScripts("systemlibs.js", "nfc_consts.js");
21 importScripts("resource://gre/modules/workers/require.js");
23 // set to true in nfc_consts.js to see debug messages
24 let DEBUG = DEBUG_WORKER;
26 function getPaddingLen(len) {
27 return (len % 4) ? (4 - len % 4) : 0;
28 }
30 let Buf = {
31 __proto__: (function(){
32 return require("resource://gre/modules/workers/worker_buf.js").Buf;
33 })(),
35 init: function init() {
36 this._init();
37 },
39 /**
40 * Process one parcel.
41 */
42 processParcel: function processParcel() {
43 let pduType = this.readInt32();
44 if (DEBUG) debug("Number of bytes available in Parcel : " + this.readAvailable);
45 NfcWorker.handleParcel(pduType, this.mCallback);
46 },
48 /**
49 * Start a new outgoing parcel.
50 *
51 * @param type
52 * Integer specifying the request type.
53 * @param callback
54 */
55 newParcel: function newParcel(type, callback) {
56 if (DEBUG) debug("New outgoing parcel of type " + type);
57 this.mCallback = callback;
58 // We're going to leave room for the parcel size at the beginning.
59 this.outgoingIndex = this.PARCEL_SIZE_SIZE;
60 this.writeInt32(type);
61 },
63 simpleRequest: function simpleRequest(type) {
64 this.newParcel(type);
65 this.sendParcel();
66 },
68 onSendParcel: function onSendParcel(parcel) {
69 postNfcMessage(parcel);
70 },
72 /**
73 * TODO: Bug 933593. Callback map of NFC_RESPONSE_XXX and RequestID
74 * needs to be maintained
75 */
76 mCallback: null,
77 };
79 /**
80 * Provide a high-level API representing NFC capabilities.
81 * Rensponsible for converting NFC requests from Content process to binary data
82 * and NFC Responses from binary data to dictionary objects.
83 */
84 let NfcWorker = {
85 /**
86 * Handle incoming messages from the main UI thread.
87 *
88 * @param message
89 * Object containing the message. Messages are supposed
90 */
91 handleDOMMessage: function handleMessage(message) {
92 if (DEBUG) debug("Received DOM message " + JSON.stringify(message));
93 let method = this[message.type];
94 if (typeof method != "function") {
95 if (DEBUG) {
96 debug("Don't know what to do with message " + JSON.stringify(message));
97 }
98 return;
99 }
100 method.call(this, message);
101 },
103 /**
104 * Unmarshals a NDEF message
105 */
106 unMarshallNdefMessage: function unMarshallNdefMessage() {
107 let numOfRecords = Buf.readInt32();
108 debug("numOfRecords = " + numOfRecords);
109 if (numOfRecords <= 0) {
110 return null;
111 }
112 let records = [];
114 for (let i = 0; i < numOfRecords; i++) {
115 let tnf = Buf.readInt32() & 0xff;
116 let typeLength = Buf.readInt32();
117 let type = Buf.readUint8Array(typeLength);
118 let padding = getPaddingLen(typeLength);
119 for (let i = 0; i < padding; i++) {
120 Buf.readUint8();
121 }
123 let idLength = Buf.readInt32();
124 let id = Buf.readUint8Array(idLength);
125 padding = getPaddingLen(idLength);
126 for (let i = 0; i < padding; i++) {
127 Buf.readUint8();
128 }
130 let payloadLength = Buf.readInt32();
131 let payload = Buf.readUint8Array(payloadLength);
132 padding = getPaddingLen(payloadLength);
133 for (let i = 0; i < padding; i++) {
134 Buf.readUint8();
135 }
136 records.push({tnf: tnf,
137 type: type,
138 id: id,
139 payload: payload});
140 }
141 return records;
142 },
144 /**
145 * Read and return NDEF data, if present.
146 */
147 readNDEF: function readNDEF(message) {
148 let cb = function callback() {
149 let error = Buf.readInt32();
150 let sessionId = Buf.readInt32();
151 let records = this.unMarshallNdefMessage();
153 message.type = "ReadNDEFResponse";
154 message.sessionId = sessionId;
155 message.records = records;
156 message.status = (error === 0) ? GECKO_NFC_ERROR_SUCCESS :
157 GECKO_NFC_ERROR_GENERIC_FAILURE;
158 this.sendDOMMessage(message);
159 }
161 Buf.newParcel(NFC_REQUEST_READ_NDEF, cb);
162 Buf.writeInt32(message.sessionId);
163 Buf.sendParcel();
164 },
166 /**
167 * Write to a target that accepts NDEF formattable data
168 */
169 writeNDEF: function writeNDEF(message) {
170 let cb = function callback() {
171 let error = Buf.readInt32();
172 let sessionId = Buf.readInt32();
174 message.type = "WriteNDEFResponse";
175 message.sessionId = sessionId;
176 message.status = (error === 0) ? GECKO_NFC_ERROR_SUCCESS :
177 GECKO_NFC_ERROR_GENERIC_FAILURE;
178 this.sendDOMMessage(message);
179 };
181 Buf.newParcel(NFC_REQUEST_WRITE_NDEF, cb);
182 Buf.writeInt32(message.sessionId);
183 let records = message.records;
184 let numRecords = records.length;
185 Buf.writeInt32(numRecords);
186 for (let i = 0; i < numRecords; i++) {
187 let record = records[i];
188 Buf.writeInt32(record.tnf);
190 let typeLength = record.type ? record.type.length : 0;
191 Buf.writeInt32(typeLength);
192 for (let j = 0; j < typeLength; j++) {
193 Buf.writeUint8(record.type[j]);
194 }
195 let padding = getPaddingLen(typeLength);
196 for (let i = 0; i < padding; i++) {
197 Buf.writeUint8(0x00);
198 }
200 let idLength = record.id ? record.id.length : 0;
201 Buf.writeInt32(idLength);
202 for (let j = 0; j < idLength; j++) {
203 Buf.writeUint8(record.id[j]);
204 }
205 padding = getPaddingLen(idLength);
206 for (let i = 0; i < padding; i++) {
207 Buf.writeUint8(0x00);
208 }
210 let payloadLength = record.payload ? record.payload.length : 0;
211 Buf.writeInt32(payloadLength);
212 for (let j = 0; j < payloadLength; j++) {
213 Buf.writeUint8(record.payload[j]);
214 }
215 padding = getPaddingLen(payloadLength);
216 for (let i = 0; i < padding; i++) {
217 Buf.writeUint8(0x00);
218 }
219 }
221 Buf.sendParcel();
222 },
224 /**
225 * Make the NFC NDEF tag permanently read only
226 */
227 makeReadOnlyNDEF: function makeReadOnlyNDEF(message) {
228 let cb = function callback() {
229 let error = Buf.readInt32();
230 let sessionId = Buf.readInt32();
232 message.type = "MakeReadOnlyNDEFResponse";
233 message.sessionId = sessionId;
234 message.status = (error === 0) ? GECKO_NFC_ERROR_SUCCESS :
235 GECKO_NFC_ERROR_GENERIC_FAILURE;
236 this.sendDOMMessage(message);
237 };
239 Buf.newParcel(NFC_REQUEST_MAKE_NDEF_READ_ONLY, cb);
240 Buf.writeInt32(message.sessionId);
241 Buf.sendParcel();
242 },
244 /**
245 * Retrieve metadata describing the NDEF formatted data, if present.
246 */
247 getDetailsNDEF: function getDetailsNDEF(message) {
248 let cb = function callback() {
249 let error = Buf.readInt32();
250 let sessionId = Buf.readInt32();
251 let isReadOnly = Buf.readUint8();
252 let canBeMadeReadOnly = Buf.readUint8();
253 // Ensure that padding is taken care here after reading two successive uint8's
254 Buf.readUint8();
255 Buf.readUint8();
256 let maxSupportedLength = Buf.readInt32();
258 message.type = "GetDetailsNDEFResponse";
259 message.sessionId = sessionId;
260 message.isReadOnly = isReadOnly;
261 message.canBeMadeReadOnly = canBeMadeReadOnly;
262 message.maxSupportedLength = maxSupportedLength;
263 message.status = (error === 0) ? GECKO_NFC_ERROR_SUCCESS :
264 GECKO_NFC_ERROR_GENERIC_FAILURE;
265 this.sendDOMMessage(message);
266 };
267 Buf.newParcel(NFC_REQUEST_GET_DETAILS, cb);
268 Buf.writeInt32(message.sessionId);
269 Buf.sendParcel();
270 },
273 /**
274 * Open a connection to the NFC target.
275 */
276 connect: function connect(message) {
277 let cb = function callback() {
278 let error = Buf.readInt32();
279 let sessionId = Buf.readInt32();
281 message.type = "ConnectResponse";
282 message.sessionId = sessionId;
283 message.status = (error === 0) ? GECKO_NFC_ERROR_SUCCESS :
284 GECKO_NFC_ERROR_GENERIC_FAILURE;
285 this.sendDOMMessage(message);
286 };
288 Buf.newParcel(NFC_REQUEST_CONNECT, cb);
289 Buf.writeInt32(message.sessionId);
290 Buf.writeInt32(message.techType);
291 Buf.sendParcel();
292 },
294 /**
295 * NFC Configuration
296 */
297 config: function config(message) {
298 let cb = function callback() {
299 let error = Buf.readInt32();
301 message.type = "ConfigResponse";
302 message.status = (error === 0) ? GECKO_NFC_ERROR_SUCCESS :
303 GECKO_NFC_ERROR_GENERIC_FAILURE;
304 this.sendDOMMessage(message);
305 };
307 Buf.newParcel(NFC_REQUEST_CONFIG , cb);
308 Buf.writeInt32(message.powerLevel);
309 Buf.sendParcel();
310 },
312 /**
313 * Close connection to the NFC target.
314 */
315 close: function close(message) {
316 let cb = function callback() {
317 let error = Buf.readInt32();
318 let sessionId = Buf.readInt32();
320 message.type = "CloseResponse";
321 message.sessionId = sessionId;
322 message.status = (error === 0) ? GECKO_NFC_ERROR_SUCCESS :
323 GECKO_NFC_ERROR_GENERIC_FAILURE;
324 this.sendDOMMessage(message);
325 };
327 Buf.newParcel(NFC_REQUEST_CLOSE , cb);
328 Buf.writeInt32(message.sessionId);
329 Buf.sendParcel();
330 },
332 handleParcel: function handleParcel(request_type, callback) {
333 let method = this[request_type];
334 if (typeof method == "function") {
335 if (DEBUG) debug("Handling parcel as " + method.name);
336 method.call(this);
337 } else if (typeof callback == "function") {
338 callback.call(this, request_type);
339 this.mCallback = null;
340 } else {
341 debug("Unable to handle ReqType:"+request_type);
342 }
343 },
345 /**
346 * Send messages to the main UI thread.
347 */
348 sendDOMMessage: function sendDOMMessage(message) {
349 postMessage(message);
350 }
351 };
353 /**
354 * Notification Handlers
355 */
356 NfcWorker[NFC_NOTIFICATION_INITIALIZED] = function NFC_NOTIFICATION_INITIALIZED () {
357 let status = Buf.readInt32();
358 let majorVersion = Buf.readInt32();
359 let minorVersion = Buf.readInt32();
360 debug("NFC_NOTIFICATION_INITIALIZED status:" + status);
361 if ((majorVersion != NFC_MAJOR_VERSION) || (minorVersion != NFC_MINOR_VERSION)) {
362 debug("Version Mismatch! Current Supported Version : " +
363 NFC_MAJOR_VERSION + "." + NFC_MINOR_VERSION +
364 " Received Version : " + majorVersion + "." + minorVersion);
365 }
366 };
368 NfcWorker[NFC_NOTIFICATION_TECH_DISCOVERED] = function NFC_NOTIFICATION_TECH_DISCOVERED() {
369 debug("NFC_NOTIFICATION_TECH_DISCOVERED");
370 let techList = [];
371 let records = null;
373 let sessionId = Buf.readInt32();
374 let techCount = Buf.readInt32();
375 for (let count = 0; count < techCount; count++) {
376 let tech = NFC_TECHS[Buf.readUint8()];
377 if (tech) {
378 techList.push(tech);
379 }
380 }
382 let padding = getPaddingLen(techCount);
383 for (let i = 0; i < padding; i++) {
384 Buf.readUint8();
385 }
387 let ndefMsgCount = Buf.readInt32();
388 if (ndefMsgCount > 0) {
389 records = this.unMarshallNdefMessage();
390 }
391 this.sendDOMMessage({type: "techDiscovered",
392 sessionId: sessionId,
393 techList: techList,
394 records: records});
395 };
397 NfcWorker[NFC_NOTIFICATION_TECH_LOST] = function NFC_NOTIFICATION_TECH_LOST() {
398 debug("NFC_NOTIFICATION_TECH_LOST");
399 let sessionId = Buf.readInt32();
400 debug("sessionId = " + sessionId);
401 this.sendDOMMessage({type: "techLost",
402 sessionId: sessionId,
403 });
404 };
406 /**
407 * Global stuff.
408 */
410 if (!this.debug) {
411 // Debugging stub that goes nowhere.
412 this.debug = function debug(message) {
413 dump("Nfc Worker: " + message + "\n");
414 };
415 }
417 // Initialize buffers. This is a separate function so that unit tests can
418 // re-initialize the buffers at will.
419 Buf.init();
421 function onNfcMessage(data) {
422 Buf.processIncoming(data);
423 };
425 onmessage = function onmessage(event) {
426 NfcWorker.handleDOMMessage(event.data);
427 };
429 onerror = function onerror(event) {
430 debug("OnError: event: " + JSON.stringify(event));
431 debug("NFC Worker error " + event.message + " " + event.filename + ":" +
432 event.lineno + ":\n");
433 };