Bumping manifests a=b2g-bump
[gecko.git] / dom / nfc / nsNfc.js
blobc0d7591ae4a2cb317b59799862506f47e50a1913
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 file,
3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 /* Copyright © 2013, Deutsche Telekom, Inc. */
7 "use strict";
9 const DEBUG = false;
10 function debug(s) {
11   if (DEBUG) dump("-*- Nfc DOM: " + s + "\n");
14 const Cc = Components.classes;
15 const Ci = Components.interfaces;
16 const Cu = Components.utils;
18 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
19 Cu.import("resource://gre/modules/Services.jsm");
20 Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
22 XPCOMUtils.defineLazyServiceGetter(this,
23                                    "appsService",
24                                    "@mozilla.org/AppsService;1",
25                                    "nsIAppsService");
27 function NfcCallback(aWindow) {
28   this._window = aWindow;
29   this.initDOMRequestHelper(aWindow, null);
30   this._createPromise();
32 NfcCallback.prototype = {
33   __proto__: DOMRequestIpcHelper.prototype,
35   _window: null,
36   promise: null,
37   _requestId: null,
39   _createPromise: function _createPromise() {
40     this.promise = this.createPromise((aResolve, aReject) => {
41       this._requestId = btoa(this.getPromiseResolverId({
42         resolve: aResolve,
43         reject: aReject
44       }));
45     });
46   },
48   getCallbackId: function getCallbackId() {
49     return this._requestId;
50   },
52   notifySuccess: function notifySuccess() {
53     let resolver = this.takePromiseResolver(atob(this._requestId));
54     if (!resolver) {
55       debug("can not find promise resolver for id: " + this._requestId);
56       return;
57     }
58     resolver.resolve();
59   },
61   notifySuccessWithBoolean: function notifySuccessWithBoolean(aResult) {
62     let resolver = this.takePromiseResolver(atob(this._requestId));
63     if (!resolver) {
64       debug("can not find promise resolver for id: " + this._requestId);
65       return;
66     }
67     resolver.resolve(aResult);
68   },
70   notifySuccessWithNDEFRecords: function notifySuccessWithNDEFRecords(aRecords) {
71     let resolver = this.takePromiseResolver(atob(this._requestId));
72     if (!resolver) {
73       debug("can not find promise resolver for id: " + this._requestId);
74       return;
75     }
77     let records = new this._window.Array();
78     for (let i = 0; i < aRecords.length; i++) {
79       let record = aRecords[i];
80       records.push(new this._window.MozNDEFRecord({tnf: record.tnf,
81                                                    type: record.type,
82                                                    id: record.id,
83                                                    payload: record.payload}));
84     }
85     resolver.resolve(records);
86   },
88   notifySuccessWithByteArray: function notifySuccessWithByteArray(aArray) {
89     let resolver = this.takePromiseResolver(atob(this._requestId));
90     if (!resolver) {
91       debug("can not find promise resolver for id: " + this._requestId);
92       return;
93     }
94     resolver.resolve(Cu.cloneInto(aArray, this._window));
95   },
97   notifyError: function notifyError(aErrorMsg) {
98     let resolver = this.takePromiseResolver(atob(this._requestId));
99     if (!resolver) {
100       debug("can not find promise resolver for id: " + this._requestId +
101            ", errormsg: " + aErrorMsg);
102       return;
103     }
104     resolver.reject(new this._window.Error(aErrorMsg));
105   },
107   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference,
108                                          Ci.nsIObserver,
109                                          Ci.nsINfcRequestCallback]),
112 // Should be mapped to the NFCTagType defined in MozNFCTag.webidl.
113 let TagType = {
114   TYPE1: "Type1",
115   TYPE2: "Type2",
116   TYPE3: "Type3",
117   TYPE4: "Type4",
118   MIFARE_CLASSIC: "MIFARE-Classic"
122  * Implementation of NFCTag.
124  * @param window        global window object.
125  * @param sessionToken  session token received from parent process.
126  * @param tagInfo       type of nsITagInfo received from parent process.
127  * @parem ndefInfo      type of nsITagNDEFInfo received from parent process.
128  */
129 function MozNFCTagImpl(window, sessionToken, tagInfo, ndefInfo) {
130   debug("In MozNFCTagImpl Constructor");
131   this._nfcContentHelper = Cc["@mozilla.org/nfc/content-helper;1"]
132                              .getService(Ci.nsINfcContentHelper);
133   this._window = window;
134   this.session = sessionToken;
135   this.techList = tagInfo.techList;
136   this.id = Cu.cloneInto(tagInfo.tagId, window);
138   if (ndefInfo) {
139     this.type = ndefInfo.tagType;
140     this.maxNDEFSize = ndefInfo.maxNDEFSize;
141     this.isReadOnly = ndefInfo.isReadOnly;
142     this.isFormatable = ndefInfo.isFormatable;
143     this.canBeMadeReadOnly = this.type == TagType.TYPE1 ||
144                              this.type == TagType.TYPE2 ||
145                              this.type == TagType.MIFARE_CLASSIC;
146   }
148 MozNFCTagImpl.prototype = {
149   _nfcContentHelper: null,
150   _window: null,
151   session: null,
152   techList: null,
153   id: null,
154   type: null,
155   maxNDEFSize: null,
156   isReadOnly: null,
157   isFormatable: null,
158   canBeMadeReadOnly: null,
159   isLost: false,
161   createTech: { "ISO-DEP": (win, tag) => { return new win.MozIsoDepTech(tag); }
162               },
164   // NFCTag interface:
165   readNDEF: function readNDEF() {
166     if (this.isLost) {
167       throw new this._window.DOMError("InvalidStateError", "NFCTag object is invalid");
168     }
170     let callback = new NfcCallback(this._window);
171     this._nfcContentHelper.readNDEF(this.session, callback);
172     return callback.promise;
173   },
175   writeNDEF: function writeNDEF(records) {
176     if (this.isLost) {
177       throw new this._window.DOMError("InvalidStateError", "NFCTag object is invalid");
178     }
180     if (this.isReadOnly) {
181       throw new this._window.DOMError("InvalidAccessError", "NFCTag object is read-only");
182     }
184     let ndefLen = 0;
185     for (let record of records) {
186       ndefLen += record.size;
187     }
189     if (ndefLen > this.maxNDEFSize) {
190       throw new this._window.DOMError("NotSupportedError", "Exceed max NDEF size");
191     }
193     let callback = new NfcCallback(this._window);
194     this._nfcContentHelper.writeNDEF(records, this.session, callback);
195     return callback.promise;
196   },
198   makeReadOnly: function makeReadOnly() {
199     if (this.isLost) {
200       throw new this._window.DOMError("InvalidStateError", "NFCTag object is invalid");
201     }
203     if (!this.canBeMadeReadOnly) {
204       throw new this._window.DOMError("InvalidAccessError",
205                                       "NFCTag object cannot be made read-only");
206     }
208     let callback = new NfcCallback(this._window);
209     this._nfcContentHelper.makeReadOnly(this.session, callback);
210     return callback.promise;
211   },
213   format: function format() {
214     if (this.isLost) {
215       throw new this._window.DOMError("InvalidStateError", "NFCTag object is invalid");
216     }
218     if (!this.isFormatable) {
219       throw new this._window.DOMError("InvalidAccessError",
220                                       "NFCTag object is not formatable");
221     }
223     let callback = new NfcCallback(this._window);
224     this._nfcContentHelper.format(this.session, callback);
225     return callback.promise;
226   },
228   selectTech: function selectTech(tech) {
229     if (this.isLost) {
230       throw new this._window.DOMError("InvalidStateError", "NFCTag object is invalid");
231     }
233     if (this.techList.indexOf(tech) == -1) {
234       throw new this._window.DOMError("InvalidAccessError",
235         "NFCTag does not contain selected tag technology");
236     }
238     if (this.createTech[tech] === undefined) {
239       throw new this._window.DOMError("InvalidAccessError",
240         "Technology is not supported now");
241     }
243     return this.createTech[tech](this._window, this._contentObj);
244   },
246   transceive: function transceive(tech, cmd) {
247     if (this.isLost) {
248       throw new this._window.DOMError("InvalidStateError", "NFCTag object is invalid");
249     }
251     let callback = new NfcCallback(this._window);
252     this._nfcContentHelper.transceive(this.session, tech, cmd, callback);
253     return callback.promise;
254   },
256   notifyLost: function notifyLost() {
257     this.isLost = true;
258   },
260   classID: Components.ID("{4e1e2e90-3137-11e3-aa6e-0800200c9a66}"),
261   contractID: "@mozilla.org/nfc/tag;1",
262   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
263                                          Ci.nsIDOMGlobalPropertyInitializer]),
267  * Implementation of NFCPeer.
269  * @param window  global window object.
270  * @param sessionToken  session token received from parent process.
271  */
272 function MozNFCPeerImpl(aWindow, aSessionToken) {
273   debug("In MozNFCPeerImpl Constructor");
274   this._nfcContentHelper = Cc["@mozilla.org/nfc/content-helper;1"]
275                              .getService(Ci.nsINfcContentHelper);
277   this._window = aWindow;
278   this.session = aSessionToken;
280 MozNFCPeerImpl.prototype = {
281   _nfcContentHelper: null,
282   _window: null,
283   isLost: false,
285   // NFCPeer interface:
286   sendNDEF: function sendNDEF(records) {
287     if (this.isLost) {
288       throw new this._window.DOMError("InvalidStateError", "NFCPeer object is invalid");
289     }
291     // Just forward sendNDEF to writeNDEF
292     let callback = new NfcCallback(this._window);
293     this._nfcContentHelper.writeNDEF(records, this.session, callback);
294     return callback.promise;
295   },
297   sendFile: function sendFile(blob) {
298     if (this.isLost) {
299       throw new this._window.DOMError("InvalidStateError", "NFCPeer object is invalid");
300     }
302     let data = {
303       "blob": blob
304     };
306     let callback = new NfcCallback(this._window);
307     this._nfcContentHelper.sendFile(Cu.cloneInto(data, this._window),
308                                     this.session, callback);
309     return callback.promise;
310   },
312   notifyLost: function notifyLost() {
313     this.isLost = true;
314   },
316   classID: Components.ID("{c1b2bcf0-35eb-11e3-aa6e-0800200c9a66}"),
317   contractID: "@mozilla.org/nfc/peer;1",
318   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
319                                          Ci.nsIDOMGlobalPropertyInitializer]),
322 // Should be mapped to the RFState defined in WebIDL.
323 let RFState = {
324   IDLE: "idle",
325   LISTEN: "listen",
326   DISCOVERY: "discovery"
330  * Implementation of navigator NFC object.
331  */
332 function MozNFCImpl() {
333   debug("In MozNFCImpl Constructor");
334   try {
335     this._nfcContentHelper = Cc["@mozilla.org/nfc/content-helper;1"]
336                                .getService(Ci.nsINfcContentHelper);
337   } catch(e) {
338     debug("No NFC support.");
339   }
341   this.eventService = Cc["@mozilla.org/eventlistenerservice;1"]
342                         .getService(Ci.nsIEventListenerService);
344 MozNFCImpl.prototype = {
345   _nfcContentHelper: null,
346   window: null,
347   _tabId: null,
348   _innerWindowId: null,
349   _rfState: null,
350   _contentObj: null,
351   nfcPeer: null,
352   nfcTag: null,
353   eventService: null,
355   init: function init(aWindow) {
356     debug("MozNFCImpl init called");
357     this.window = aWindow;
358     let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
359                       .getInterface(Ci.nsIDOMWindowUtils);
360     this._innerWindowId = util.currentInnerWindowID;
362     this.defineEventHandlerGetterSetter("ontagfound");
363     this.defineEventHandlerGetterSetter("ontaglost");
364     this.defineEventHandlerGetterSetter("onpeerready");
365     this.defineEventHandlerGetterSetter("onpeerfound");
366     this.defineEventHandlerGetterSetter("onpeerlost");
368     Services.obs.addObserver(this, "inner-window-destroyed",
369                              /* weak-ref */ false);
371     if (this._nfcContentHelper) {
372       this._tabId = this.getTabId(aWindow);
373       this._nfcContentHelper.addEventListener(this, this._tabId);
374       this._rfState = this._nfcContentHelper.queryRFState();
375     }
376   },
378   getTabId: function getTabId(aWindow) {
379     let tabId;
380     // For now, we assume app will run in oop mode so we can get
381     // tab id for each app. Fix bug 1116449 if we are going to
382     // support in-process mode.
383     let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
384                           .getInterface(Ci.nsIWebNavigation)
385                           .QueryInterface(Ci.nsIDocShell);
386     try {
387       tabId = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
388                       .getInterface(Ci.nsITabChild)
389                       .tabId;
390     } catch(e) {
391       // Only parent process does not have tab id, so in this case
392       // NfcContentHelper is used by system app. Use -1(tabId) to
393       // indicate its system app.
394       let inParent = Cc["@mozilla.org/xre/app-info;1"]
395                        .getService(Ci.nsIXULRuntime)
396                        .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
397       if (inParent) {
398         tabId = Ci.nsINfcBrowserAPI.SYSTEM_APP_ID;
399       } else {
400         throw Components.Exception("Can't get tab id in child process",
401                                    Cr.NS_ERROR_UNEXPECTED);
402       }
403     }
405     return tabId;
406   },
408   // Only apps which have nfc-manager permission can call the following interfaces
409   // 'checkP2PRegistration' , 'notifyUserAcceptedP2P' , 'notifySendFileStatus',
410   // 'startPoll', 'stopPoll', and 'powerOff'.
411   checkP2PRegistration: function checkP2PRegistration(manifestUrl) {
412     // Get the AppID and pass it to ContentHelper
413     let appID = appsService.getAppLocalIdByManifestURL(manifestUrl);
415     let callback = new NfcCallback(this.window);
416     this._nfcContentHelper.checkP2PRegistration(appID, callback);
417     return callback.promise;
418   },
420   notifyUserAcceptedP2P: function notifyUserAcceptedP2P(manifestUrl) {
421     let appID = appsService.getAppLocalIdByManifestURL(manifestUrl);
422     // Notify chrome process of user's acknowledgement
423     this._nfcContentHelper.notifyUserAcceptedP2P(appID);
424   },
426   notifySendFileStatus: function notifySendFileStatus(status, requestId) {
427     this._nfcContentHelper.notifySendFileStatus(status, requestId);
428   },
430   startPoll: function startPoll() {
431     let callback = new NfcCallback(this.window);
432     this._nfcContentHelper.changeRFState(RFState.DISCOVERY, callback);
433     return callback.promise;
434   },
436   stopPoll: function stopPoll() {
437     let callback = new NfcCallback(this.window);
438     this._nfcContentHelper.changeRFState(RFState.LISTEN, callback);
439     return callback.promise;
440   },
442   powerOff: function powerOff() {
443     let callback = new NfcCallback(this.window);
444     this._nfcContentHelper.changeRFState(RFState.IDLE, callback);
445     return callback.promise;
446   },
448   get enabled() {
449     return this._rfState != RFState.IDLE;
450   },
452   observe: function observe(subject, topic, data) {
453     if (topic !== "inner-window-destroyed") {
454       return;
455     }
457     let wId = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
458     if (wId != this._innerWindowId) {
459       return;
460     }
462     this._nfcContentHelper.removeEventListener(this._tabId);
463   },
465   defineEventHandlerGetterSetter: function defineEventHandlerGetterSetter(name) {
466     Object.defineProperty(this, name, {
467       get: function get() {
468         return this.__DOM_IMPL__.getEventHandler(name);
469       },
470       set: function set(handler) {
471         this.__DOM_IMPL__.setEventHandler(name, handler);
472       }
473     });
474   },
476   eventListenerWasAdded: function(eventType) {
477     if (eventType !== "peerready") {
478       return;
479     }
481     let appId = this.window.document.nodePrincipal.appId;
482     this._nfcContentHelper.registerTargetForPeerReady(appId);
483   },
485   eventListenerWasRemoved: function(eventType) {
486     if (eventType !== "peerready") {
487       return;
488     }
490     let appId = this.window.document.nodePrincipal.appId;
491     this._nfcContentHelper.unregisterTargetForPeerReady(appId);
492   },
494   notifyTagFound: function notifyTagFound(sessionToken, tagInfo, ndefInfo, records) {
495     if (!this.handleTagFound(sessionToken, tagInfo, ndefInfo, records)) {
496       this._nfcContentHelper.callDefaultFoundHandler(sessionToken, false, records);
497     };
498   },
500   /**
501    * Handles Tag Found event.
502    *
503    * returns true if the app could process this event, false otherwise.
504    */
505   handleTagFound: function handleTagFound(sessionToken, tagInfo, ndefInfo, records) {
506     if (this.hasDeadWrapper()) {
507       dump("this.window or this.__DOM_IMPL__ is a dead wrapper.");
508       return false;
509     }
511     if (!this.eventService.hasListenersFor(this.__DOM_IMPL__, "tagfound")) {
512       debug("ontagfound is not registered.");
513       return false;
514     }
516     if (!this.checkPermissions(["nfc"])) {
517       return false;
518     }
520     let tagImpl = new MozNFCTagImpl(this.window, sessionToken, tagInfo, ndefInfo);
521     let tag = this.window.MozNFCTag._create(this.window, tagImpl);
523     tagImpl._contentObj = tag;
524     this.nfcTag = tag;
526     let length = records ? records.length : 0;
527     let ndefRecords = records ? [] : null;
528     for (let i = 0; i < length; i++) {
529       let record = records[i];
530       ndefRecords.push(new this.window.MozNDEFRecord({tnf: record.tnf,
531                                                        type: record.type,
532                                                        id: record.id,
533                                                        payload: record.payload}));
534     }
536     let eventData = {
537       "cancelable": true,
538       "tag": tag,
539       "ndefRecords": ndefRecords
540     };
542     debug("fire ontagfound " + sessionToken);
543     let tagEvent = new this.window.MozNFCTagEvent("tagfound", eventData);
544     this.__DOM_IMPL__.dispatchEvent(tagEvent);
546     // If defaultPrevented is false, means we need to take the default action
547     // for this event - redirect this event to System app. Before redirecting to
548     // System app, we need revoke the tag object first.
549     if (!tagEvent.defaultPrevented) {
550       this.notifyTagLost(sessionToken);
551     }
553     return tagEvent.defaultPrevented;
554   },
556   notifyTagLost: function notifyTagLost(sessionToken) {
557     if (!this.handleTagLost(sessionToken)) {
558       this._nfcContentHelper.callDefaultLostHandler(sessionToken, false);
559     }
560   },
562   handleTagLost: function handleTagLost(sessionToken) {
563     if (this.hasDeadWrapper()) {
564       dump("this.window or this.__DOM_IMPL__ is a dead wrapper.");
565       return false;
566     }
568     if (!this.checkPermissions(["nfc"])) {
569       return false;
570     }
572     if (!this.nfcTag) {
573       debug("No NFCTag object existing.");
574       return false;
575     }
577     this.nfcTag.notifyLost();
578     this.nfcTag = null;
580     debug("fire ontaglost " + sessionToken);
581     let event = new this.window.Event("taglost");
582     this.__DOM_IMPL__.dispatchEvent(event);
584     return true;
585   },
587   notifyPeerFound: function notifyPeerFound(sessionToken, isPeerReady) {
588     if (!this.handlePeerFound(sessionToken, isPeerReady)) {
589       this._nfcContentHelper.callDefaultFoundHandler(sessionToken, true, null);
590     }
591   },
593   /**
594    * Handles Peer Found/Peer Ready event.
595    *
596    * returns true if the app could process this event, false otherwise.
597    */
598   handlePeerFound: function handlePeerFound(sessionToken, isPeerReady) {
599     if (this.hasDeadWrapper()) {
600       dump("this.window or this.__DOM_IMPL__ is a dead wrapper.");
601       return false;
602     }
604     if (!isPeerReady &&
605         !this.eventService.hasListenersFor(this.__DOM_IMPL__, "peerfound")) {
606       debug("onpeerfound is not registered.");
607       return false;
608     }
610     let perm = isPeerReady ? ["nfc-share"] : ["nfc"];
611     if (!this.checkPermissions(perm)) {
612       return false;
613     }
615     let peerImpl = new MozNFCPeerImpl(this.window, sessionToken);
616     this.nfcPeer = this.window.MozNFCPeer._create(this.window, peerImpl);
618     let eventType;
619     let eventData = {
620       "peer": this.nfcPeer
621     };
623     if (isPeerReady) {
624       eventType = "peerready";
625     } else {
626       eventData.cancelable = true;
627       eventType = "peerfound";
628     }
630     debug("fire on" + eventType + " " + sessionToken);
631     let event = new this.window.MozNFCPeerEvent(eventType, eventData);
632     this.__DOM_IMPL__.dispatchEvent(event);
634     // For peerready we don't take the default action.
635     if (isPeerReady) {
636       return true;
637     }
639     // If defaultPrevented is false, means we need to take the default action
640     // for this event - redirect this event to System app. Before redirecting to
641     // System app, we need revoke the peer object first.
642     if (!event.defaultPrevented) {
643       this.notifyPeerLost(sessionToken);
644     }
646     return event.defaultPrevented;
647   },
649   notifyPeerLost: function notifyPeerLost(sessionToken) {
650     if (!this.handlePeerLost(sessionToken)) {
651       this._nfcContentHelper.callDefaultLostHandler(sessionToken, true);
652     }
653   },
655   handlePeerLost: function handlePeerLost(sessionToken) {
656     if (this.hasDeadWrapper()) {
657       dump("this.window or this.__DOM_IMPL__ is a dead wrapper.");
658       return false;
659     }
661     if (!this.checkPermissions(["nfc", "nfc-share"])) {
662       return false;
663     }
665     if (!this.nfcPeer) {
666       debug("No NFCPeer object existing.");
667       return false;
668     }
670     this.nfcPeer.notifyLost();
671     this.nfcPeer = null;
673     debug("fire onpeerlost");
674     let event = new this.window.Event("peerlost");
675     this.__DOM_IMPL__.dispatchEvent(event);
677     return true;
678   },
680   notifyRFStateChanged: function notifyRFStateChanged(rfState) {
681     this._rfState = rfState;
682   },
684   notifyFocusChanged: function notifyFocusChanged(focus) {
685     if (focus) {
686       return;
687     }
689     if (this.nfcTag) {
690       debug("losing focus, call taglost.");
691       this.notifyTagLost(this.nfcTag.session);
692     }
694     if (this.nfcPeer) {
695       debug("losing focus, call peerlost.");
696       this.notifyPeerLost(this.nfcPeer.session);
697     }
698   },
700   checkPermissions: function checkPermissions(perms) {
701     let principal = this.window.document.nodePrincipal;
702     for (let perm of perms) {
703       let permValue =
704         Services.perms.testExactPermissionFromPrincipal(principal, perm);
705       if (permValue == Ci.nsIPermissionManager.ALLOW_ACTION) {
706         return true;
707       } else {
708         debug("doesn't have " + perm + " permission.");
709       }
710     }
712     return false;
713   },
715   hasDeadWrapper: function hasDeadWrapper() {
716     return Cu.isDeadWrapper(this.window) || Cu.isDeadWrapper(this.__DOM_IMPL__);
717   },
719   classID: Components.ID("{6ff2b290-2573-11e3-8224-0800200c9a66}"),
720   contractID: "@mozilla.org/nfc/manager;1",
721   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
722                                          Ci.nsIDOMGlobalPropertyInitializer,
723                                          Ci.nsINfcEventListener,
724                                          Ci.nsIObserver]),
727 function NFCSendFileWrapper() {
729 NFCSendFileWrapper.prototype = {
730   // nsISystemMessagesWrapper implementation.
731   wrapMessage: function wrapMessage(aMessage, aWindow) {
732     let peerImpl = new MozNFCPeerImpl(aWindow, aMessage.sessionToken);
733     let peer = aWindow.MozNFCPeer._create(aWindow, peerImpl);
735     delete aMessage.sessionToken;
736     aMessage = Cu.cloneInto(aMessage, aWindow);
737     aMessage.peer = peer;
738     return aMessage;
739   },
741   classDescription: "NFCSendFileWrapper",
742   classID: Components.ID("{c5063a5c-8cb9-41d2-baf5-56062a2e30e9}"),
743   contractID: "@mozilla.org/dom/system-messages/wrapper/nfc-manager-send-file;1",
744   QueryInterface: XPCOMUtils.generateQI([Ci.nsISystemMessagesWrapper])
747 function NFCTechDiscoveredWrapper() {
749 NFCTechDiscoveredWrapper.prototype = {
750   // nsISystemMessagesWrapper implementation.
751   wrapMessage: function wrapMessage(aMessage, aWindow) {
752     aMessage = Cu.cloneInto(aMessage, aWindow);
753     if (aMessage.isP2P) {
754       let peerImpl = new MozNFCPeerImpl(aWindow, aMessage.sessionToken);
755       let peer = aWindow.MozNFCPeer._create(aWindow, peerImpl);
756       aMessage.peer = peer;
757     }
759     delete aMessage.isP2P;
760     delete aMessage.sessionToken;
762     return aMessage;
763   },
765   classDescription: "NFCTechDiscoveredWrapper",
766   classID: Components.ID("{2e7f9285-3c72-4e1f-b985-141a00a23a75}"),
767   contractID: "@mozilla.org/dom/system-messages/wrapper/nfc-manager-tech-discovered;1",
768   QueryInterface: XPCOMUtils.generateQI([Ci.nsISystemMessagesWrapper])
771 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([MozNFCTagImpl,
772   MozNFCPeerImpl, MozNFCImpl, NFCSendFileWrapper, NFCTechDiscoveredWrapper]);