michael@0: /* Copyright 2012 Mozilla Foundation and Mozilla contributors michael@0: * michael@0: * Licensed under the Apache License, Version 2.0 (the "License"); michael@0: * you may not use this file except in compliance with the License. michael@0: * You may obtain a copy of the License at michael@0: * michael@0: * http://www.apache.org/licenses/LICENSE-2.0 michael@0: * michael@0: * Unless required by applicable law or agreed to in writing, software michael@0: * distributed under the License is distributed on an "AS IS" BASIS, michael@0: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. michael@0: * See the License for the specific language governing permissions and michael@0: * limitations under the License. michael@0: */ michael@0: michael@0: /* Copyright © 2013, Deutsche Telekom, Inc. */ michael@0: michael@0: "use strict"; michael@0: michael@0: const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; michael@0: michael@0: Cu.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: Cu.import("resource://gre/modules/DOMRequestHelper.jsm"); michael@0: michael@0: let NFC = {}; michael@0: Cu.import("resource://gre/modules/nfc_consts.js", NFC); michael@0: michael@0: Cu.import("resource://gre/modules/systemlibs.js"); michael@0: const NFC_ENABLED = libcutils.property_get("ro.moz.nfc.enabled", "false") === "true"; michael@0: michael@0: // set to true to in nfc_consts.js to see debug messages michael@0: let DEBUG = NFC.DEBUG_CONTENT_HELPER; michael@0: michael@0: let debug; michael@0: if (DEBUG) { michael@0: debug = function (s) { michael@0: dump("-*- NfcContentHelper: " + s + "\n"); michael@0: }; michael@0: } else { michael@0: debug = function (s) {}; michael@0: } michael@0: michael@0: const NFCCONTENTHELPER_CID = michael@0: Components.ID("{4d72c120-da5f-11e1-9b23-0800200c9a66}"); michael@0: michael@0: const NFC_IPC_MSG_NAMES = [ michael@0: "NFC:ReadNDEFResponse", michael@0: "NFC:WriteNDEFResponse", michael@0: "NFC:GetDetailsNDEFResponse", michael@0: "NFC:MakeReadOnlyNDEFResponse", michael@0: "NFC:ConnectResponse", michael@0: "NFC:CloseResponse", michael@0: "NFC:CheckP2PRegistrationResponse", michael@0: "NFC:PeerEvent", michael@0: "NFC:NotifySendFileStatusResponse", michael@0: "NFC:ConfigResponse" michael@0: ]; michael@0: michael@0: XPCOMUtils.defineLazyServiceGetter(this, "cpmm", michael@0: "@mozilla.org/childprocessmessagemanager;1", michael@0: "nsISyncMessageSender"); michael@0: michael@0: function GetDetailsNDEFResponse(details) { michael@0: this.canBeMadeReadOnly = details.canBeMadeReadOnly; michael@0: this.isReadOnly = details.isReadOnly; michael@0: this.maxSupportedLength = details.maxSupportedLength; michael@0: } michael@0: GetDetailsNDEFResponse.prototype = { michael@0: __exposedProps__ : {canBeMadeReadOnly: 'r', michael@0: isReadOnly: 'r', michael@0: maxSupportedLength: 'r'} michael@0: }; michael@0: michael@0: function NfcContentHelper() { michael@0: this.initDOMRequestHelper(/* aWindow */ null, NFC_IPC_MSG_NAMES); michael@0: Services.obs.addObserver(this, "xpcom-shutdown", false); michael@0: michael@0: this._requestMap = []; michael@0: michael@0: // Maintains an array of PeerEvent related callbacks, mainly michael@0: // one for 'peerReady' and another for 'peerLost'. michael@0: this.peerEventsCallbackMap = {}; michael@0: } michael@0: michael@0: NfcContentHelper.prototype = { michael@0: __proto__: DOMRequestIpcHelper.prototype, michael@0: michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsINfcContentHelper, michael@0: Ci.nsISupportsWeakReference, michael@0: Ci.nsIObserver]), michael@0: classID: NFCCONTENTHELPER_CID, michael@0: classInfo: XPCOMUtils.generateCI({ michael@0: classID: NFCCONTENTHELPER_CID, michael@0: classDescription: "NfcContentHelper", michael@0: interfaces: [Ci.nsINfcContentHelper] michael@0: }), michael@0: michael@0: _requestMap: null, michael@0: peerEventsCallbackMap: null, michael@0: michael@0: encodeNDEFRecords: function encodeNDEFRecords(records) { michael@0: let encodedRecords = []; michael@0: for (let i = 0; i < records.length; i++) { michael@0: let record = records[i]; michael@0: encodedRecords.push({ michael@0: tnf: record.tnf, michael@0: type: record.type, michael@0: id: record.id, michael@0: payload: record.payload, michael@0: }); michael@0: } michael@0: return encodedRecords; michael@0: }, michael@0: michael@0: // NFC interface: michael@0: setSessionToken: function setSessionToken(sessionToken) { michael@0: if (sessionToken == null) { michael@0: throw Components.Exception("No session token!", michael@0: Cr.NS_ERROR_UNEXPECTED); michael@0: return; michael@0: } michael@0: // Report session to Nfc.js only. michael@0: cpmm.sendAsyncMessage("NFC:SetSessionToken", { michael@0: sessionToken: sessionToken, michael@0: }); michael@0: }, michael@0: michael@0: // NFCTag interface michael@0: getDetailsNDEF: function getDetailsNDEF(window, sessionToken) { michael@0: if (window == null) { michael@0: throw Components.Exception("Can't get window object", michael@0: Cr.NS_ERROR_UNEXPECTED); michael@0: } michael@0: let request = Services.DOMRequest.createRequest(window); michael@0: let requestId = btoa(this.getRequestId(request)); michael@0: this._requestMap[requestId] = window; michael@0: michael@0: cpmm.sendAsyncMessage("NFC:GetDetailsNDEF", { michael@0: requestId: requestId, michael@0: sessionToken: sessionToken michael@0: }); michael@0: return request; michael@0: }, michael@0: michael@0: readNDEF: function readNDEF(window, sessionToken) { michael@0: if (window == null) { michael@0: throw Components.Exception("Can't get window object", michael@0: Cr.NS_ERROR_UNEXPECTED); michael@0: } michael@0: let request = Services.DOMRequest.createRequest(window); michael@0: let requestId = btoa(this.getRequestId(request)); michael@0: this._requestMap[requestId] = window; michael@0: michael@0: cpmm.sendAsyncMessage("NFC:ReadNDEF", { michael@0: requestId: requestId, michael@0: sessionToken: sessionToken michael@0: }); michael@0: return request; michael@0: }, michael@0: michael@0: writeNDEF: function writeNDEF(window, records, sessionToken) { michael@0: if (window == null) { michael@0: throw Components.Exception("Can't get window object", michael@0: Cr.NS_ERROR_UNEXPECTED); michael@0: } michael@0: let request = Services.DOMRequest.createRequest(window); michael@0: let requestId = btoa(this.getRequestId(request)); michael@0: this._requestMap[requestId] = window; michael@0: michael@0: let encodedRecords = this.encodeNDEFRecords(records); michael@0: cpmm.sendAsyncMessage("NFC:WriteNDEF", { michael@0: requestId: requestId, michael@0: sessionToken: sessionToken, michael@0: records: encodedRecords michael@0: }); michael@0: return request; michael@0: }, michael@0: michael@0: makeReadOnlyNDEF: function makeReadOnlyNDEF(window, sessionToken) { michael@0: if (window == null) { michael@0: throw Components.Exception("Can't get window object", michael@0: Cr.NS_ERROR_UNEXPECTED); michael@0: } michael@0: michael@0: let request = Services.DOMRequest.createRequest(window); michael@0: let requestId = btoa(this.getRequestId(request)); michael@0: this._requestMap[requestId] = window; michael@0: michael@0: cpmm.sendAsyncMessage("NFC:MakeReadOnlyNDEF", { michael@0: requestId: requestId, michael@0: sessionToken: sessionToken michael@0: }); michael@0: return request; michael@0: }, michael@0: michael@0: connect: function connect(window, techType, sessionToken) { michael@0: if (window == null) { michael@0: throw Components.Exception("Can't get window object", michael@0: Cr.NS_ERROR_UNEXPECTED); michael@0: } michael@0: let request = Services.DOMRequest.createRequest(window); michael@0: let requestId = btoa(this.getRequestId(request)); michael@0: this._requestMap[requestId] = window; michael@0: michael@0: cpmm.sendAsyncMessage("NFC:Connect", { michael@0: requestId: requestId, michael@0: sessionToken: sessionToken, michael@0: techType: techType michael@0: }); michael@0: return request; michael@0: }, michael@0: michael@0: close: function close(window, sessionToken) { michael@0: if (window == null) { michael@0: throw Components.Exception("Can't get window object", michael@0: Cr.NS_ERROR_UNEXPECTED); michael@0: } michael@0: let request = Services.DOMRequest.createRequest(window); michael@0: let requestId = btoa(this.getRequestId(request)); michael@0: this._requestMap[requestId] = window; michael@0: michael@0: cpmm.sendAsyncMessage("NFC:Close", { michael@0: requestId: requestId, michael@0: sessionToken: sessionToken michael@0: }); michael@0: return request; michael@0: }, michael@0: michael@0: sendFile: function sendFile(window, data, sessionToken) { michael@0: if (window == null) { michael@0: throw Components.Exception("Can't get window object", michael@0: Cr.NS_ERROR_UNEXPECTED); michael@0: } michael@0: let request = Services.DOMRequest.createRequest(window); michael@0: let requestId = btoa(this.getRequestId(request)); michael@0: this._requestMap[requestId] = window; michael@0: michael@0: cpmm.sendAsyncMessage("NFC:SendFile", { michael@0: requestId: requestId, michael@0: sessionToken: sessionToken, michael@0: blob: data.blob michael@0: }); michael@0: return request; michael@0: }, michael@0: michael@0: notifySendFileStatus: function notifySendFileStatus(window, status, michael@0: requestId) { michael@0: if (window == null) { michael@0: throw Components.Exception("Can't get window object", michael@0: Cr.NS_ERROR_UNEXPECTED); michael@0: } michael@0: michael@0: cpmm.sendAsyncMessage("NFC:NotifySendFileStatus", { michael@0: status: status, michael@0: requestId: requestId michael@0: }); michael@0: }, michael@0: michael@0: registerTargetForPeerEvent: function registerTargetForPeerEvent(window, michael@0: appId, event, callback) { michael@0: if (window == null) { michael@0: throw Components.Exception("Can't get window object", michael@0: Cr.NS_ERROR_UNEXPECTED); michael@0: } michael@0: this.peerEventsCallbackMap[event] = callback; michael@0: cpmm.sendAsyncMessage("NFC:RegisterPeerTarget", { michael@0: appId: appId, michael@0: event: event michael@0: }); michael@0: }, michael@0: michael@0: unregisterTargetForPeerEvent: function unregisterTargetForPeerEvent(window, michael@0: appId, event) { michael@0: if (window == null) { michael@0: throw Components.Exception("Can't get window object", michael@0: Cr.NS_ERROR_UNEXPECTED); michael@0: } michael@0: let callback = this.peerEventsCallbackMap[event]; michael@0: if (callback != null) { michael@0: delete this.peerEventsCallbackMap[event]; michael@0: } michael@0: michael@0: cpmm.sendAsyncMessage("NFC:UnregisterPeerTarget", { michael@0: appId: appId, michael@0: event: event michael@0: }); michael@0: }, michael@0: michael@0: checkP2PRegistration: function checkP2PRegistration(window, appId) { michael@0: if (window == null) { michael@0: throw Components.Exception("Can't get window object", michael@0: Cr.NS_ERROR_UNEXPECTED); michael@0: } michael@0: let request = Services.DOMRequest.createRequest(window); michael@0: let requestId = btoa(this.getRequestId(request)); michael@0: this._requestMap[requestId] = window; michael@0: michael@0: cpmm.sendAsyncMessage("NFC:CheckP2PRegistration", { michael@0: appId: appId, michael@0: requestId: requestId michael@0: }); michael@0: return request; michael@0: }, michael@0: michael@0: notifyUserAcceptedP2P: function notifyUserAcceptedP2P(window, appId) { michael@0: if (window == null) { michael@0: throw Components.Exception("Can't get window object", michael@0: Cr.NS_ERROR_UNEXPECTED); michael@0: } michael@0: michael@0: cpmm.sendAsyncMessage("NFC:NotifyUserAcceptedP2P", { michael@0: appId: appId michael@0: }); michael@0: }, michael@0: michael@0: startPoll: function startPoll(window) { michael@0: if (window == null) { michael@0: throw Components.Exception("Can't get window object", michael@0: Cr.NS_ERROR_UNEXPECTED); michael@0: } michael@0: michael@0: let request = Services.DOMRequest.createRequest(window); michael@0: let requestId = btoa(this.getRequestId(request)); michael@0: this._requestMap[requestId] = window; michael@0: michael@0: cpmm.sendAsyncMessage("NFC:StartPoll", michael@0: {requestId: requestId}); michael@0: return request; michael@0: }, michael@0: michael@0: stopPoll: function stopPoll(window) { michael@0: if (window == null) { michael@0: throw Components.Exception("Can't get window object", michael@0: Cr.NS_ERROR_UNEXPECTED); michael@0: } michael@0: michael@0: let request = Services.DOMRequest.createRequest(window); michael@0: let requestId = btoa(this.getRequestId(request)); michael@0: this._requestMap[requestId] = window; michael@0: michael@0: cpmm.sendAsyncMessage("NFC:StopPoll", michael@0: {requestId: requestId}); michael@0: return request; michael@0: }, michael@0: michael@0: powerOff: function powerOff(window) { michael@0: if (window == null) { michael@0: throw Components.Exception("Can't get window object", michael@0: Cr.NS_ERROR_UNEXPECTED); michael@0: } michael@0: michael@0: let request = Services.DOMRequest.createRequest(window); michael@0: let requestId = btoa(this.getRequestId(request)); michael@0: this._requestMap[requestId] = window; michael@0: michael@0: cpmm.sendAsyncMessage("NFC:PowerOff", michael@0: {requestId: requestId}); michael@0: return request; michael@0: }, michael@0: michael@0: // nsIObserver michael@0: observe: function observe(subject, topic, data) { michael@0: if (topic == "xpcom-shutdown") { michael@0: this.destroyDOMRequestHelper(); michael@0: Services.obs.removeObserver(this, "xpcom-shutdown"); michael@0: cpmm = null; michael@0: } michael@0: }, michael@0: michael@0: // nsIMessageListener michael@0: michael@0: fireRequestSuccess: function fireRequestSuccess(requestId, result) { michael@0: let request = this.takeRequest(requestId); michael@0: if (!request) { michael@0: debug("not firing success for id: " + requestId + michael@0: ", result: " + JSON.stringify(result)); michael@0: return; michael@0: } michael@0: michael@0: debug("fire request success, id: " + requestId + michael@0: ", result: " + JSON.stringify(result)); michael@0: Services.DOMRequest.fireSuccess(request, result); michael@0: }, michael@0: michael@0: fireRequestError: function fireRequestError(requestId, error) { michael@0: let request = this.takeRequest(requestId); michael@0: if (!request) { michael@0: debug("not firing error for id: " + requestId + michael@0: ", error: " + JSON.stringify(error)); michael@0: return; michael@0: } michael@0: michael@0: debug("fire request error, id: " + requestId + michael@0: ", result: " + JSON.stringify(error)); michael@0: Services.DOMRequest.fireError(request, error); michael@0: }, michael@0: michael@0: receiveMessage: function receiveMessage(message) { michael@0: debug("Message received: " + JSON.stringify(message)); michael@0: let result = message.json; michael@0: michael@0: switch (message.name) { michael@0: case "NFC:ReadNDEFResponse": michael@0: this.handleReadNDEFResponse(result); michael@0: break; michael@0: case "NFC:GetDetailsNDEFResponse": michael@0: this.handleGetDetailsNDEFResponse(result); michael@0: break; michael@0: case "NFC:ConnectResponse": // Fall through. michael@0: case "NFC:CloseResponse": michael@0: case "NFC:WriteNDEFResponse": michael@0: case "NFC:MakeReadOnlyNDEFResponse": michael@0: case "NFC:CheckP2PRegistrationResponse": michael@0: case "NFC:NotifySendFileStatusResponse": michael@0: case "NFC:ConfigResponse": michael@0: if (result.status !== NFC.GECKO_NFC_ERROR_SUCCESS) { michael@0: this.fireRequestError(atob(result.requestId), result.status); michael@0: } else { michael@0: this.fireRequestSuccess(atob(result.requestId), result); michael@0: } michael@0: break; michael@0: case "NFC:PeerEvent": michael@0: let callback = this.peerEventsCallbackMap[result.event]; michael@0: if (callback) { michael@0: callback.peerNotification(result.event, result.sessionToken); michael@0: } else { michael@0: debug("PeerEvent: No valid callback registered for the event " + michael@0: result.event); michael@0: } michael@0: break; michael@0: } michael@0: }, michael@0: michael@0: handleReadNDEFResponse: function handleReadNDEFResponse(result) { michael@0: let requester = this._requestMap[result.requestId]; michael@0: if (!requester) { michael@0: debug("Response Invalid requestId=" + result.requestId); michael@0: return; michael@0: } michael@0: delete this._requestMap[result.requestId]; michael@0: michael@0: if (result.status !== NFC.GECKO_NFC_ERROR_SUCCESS) { michael@0: this.fireRequestError(atob(result.requestId), result.status); michael@0: return; michael@0: } michael@0: michael@0: let requestId = atob(result.requestId); michael@0: let ndefMsg = []; michael@0: let records = result.records; michael@0: for (let i = 0; i < records.length; i++) { michael@0: let record = records[i]; michael@0: ndefMsg.push(new requester.MozNDEFRecord(record.tnf, michael@0: record.type, michael@0: record.id, michael@0: record.payload)); michael@0: } michael@0: this.fireRequestSuccess(requestId, ndefMsg); michael@0: }, michael@0: michael@0: handleGetDetailsNDEFResponse: function handleGetDetailsNDEFResponse(result) { michael@0: if (result.status !== NFC.GECKO_NFC_ERROR_SUCCESS) { michael@0: this.fireRequestError(atob(result.requestId), result.status); michael@0: return; michael@0: } michael@0: michael@0: let requestId = atob(result.requestId); michael@0: let result = new GetDetailsNDEFResponse(result); michael@0: this.fireRequestSuccess(requestId, result); michael@0: }, michael@0: }; michael@0: michael@0: if (NFC_ENABLED) { michael@0: this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NfcContentHelper]); michael@0: }