Bug 916428 - [NFC] APIs for ISO 14443-4 tags (IsoDep Support). r=smaug, r=yoshi
authorDimi Lee <dlee@mozilla.com>
Tue, 13 Jan 2015 14:50:24 +0800
changeset 223631 7d7a31553aa1aad3467420c5c63c7fee0ce9f7db
parent 223537 0500f1032ea1f51c42b940cc6b25696cb9064c3e
child 223632 a48c2bd869f4d1a644f29f33dbd026dfe3b761cd
push id28099
push userkwierso@gmail.com
push dateWed, 14 Jan 2015 01:38:51 +0000
treeherdermozilla-central@3100ceecc1bb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, yoshi
bugs916428, 14443
milestone38.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 916428 - [NFC] APIs for ISO 14443-4 tags (IsoDep Support). r=smaug, r=yoshi
dom/nfc/MozIsoDepTech.cpp
dom/nfc/MozIsoDepTech.h
dom/nfc/moz.build
dom/nfc/nsNfc.js
dom/webidl/MozIsoDepTech.webidl
dom/webidl/MozNFCTag.webidl
dom/webidl/moz.build
new file mode 100644
--- /dev/null
+++ b/dom/nfc/MozIsoDepTech.cpp
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=2 et sw=2 tw=80:
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MozIsoDepTech.h"
+#include "mozilla/dom/Promise.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(MozIsoDepTech)
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(MozIsoDepTech)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(MozIsoDepTech)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTag)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MozIsoDepTech)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTag)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(MozIsoDepTech)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(MozIsoDepTech)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MozIsoDepTech)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+/* static */
+already_AddRefed<MozIsoDepTech>
+MozIsoDepTech::Constructor(const GlobalObject& aGlobal,
+                           MozNFCTag& aNFCTag,
+                           ErrorResult& aRv)
+{
+  ErrorResult rv;
+  nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aGlobal.GetAsSupports());
+  if (!win) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  Nullable<nsTArray<NFCTechType>> techList;
+  aNFCTag.GetTechList(techList, rv);
+  if (rv.Failed()) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  if (techList.IsNull() || !(techList.Value().Contains(mTechnology))) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return nullptr;
+  }
+
+  nsRefPtr<MozIsoDepTech> isoDep = new MozIsoDepTech(win, aNFCTag);
+
+  return isoDep.forget();
+}
+
+MozIsoDepTech::MozIsoDepTech(nsPIDOMWindow* aWindow, MozNFCTag& aNFCTag)
+ : mWindow(aWindow)
+ , mTag(&aNFCTag)
+{
+}
+
+MozIsoDepTech::~MozIsoDepTech()
+{
+}
+
+already_AddRefed<Promise>
+MozIsoDepTech::Transceive(const Uint8Array& aCommand, ErrorResult& aRv)
+{
+  ErrorResult rv;
+
+  aCommand.ComputeLengthAndData();
+  nsRefPtr<Promise> promise = mTag->Transceive(mTechnology, aCommand, rv);
+  if (rv.Failed()) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  return promise.forget();
+}
+
+JSObject*
+MozIsoDepTech::WrapObject(JSContext* aCx)
+{
+  return MozIsoDepTechBinding::Wrap(aCx, this);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/nfc/MozIsoDepTech.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=2 et sw=2 tw=80:
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_nfc_MozIsoDepTech_h__
+#define mozilla_dom_nfc_MozIsoDepTech_h__
+
+#include "mozilla/dom/MozNFCTagBinding.h"
+#include "mozilla/dom/MozIsoDepTechBinding.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsWrapperCache.h"
+#include "nsISupportsImpl.h"
+#include "nsPIDOMWindow.h"
+
+namespace mozilla {
+namespace dom {
+
+class Promise;
+
+class MozIsoDepTech : public nsISupports,
+                      public nsWrapperCache
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MozIsoDepTech)
+
+  already_AddRefed<Promise> Transceive(const Uint8Array& aCommand,
+                                       ErrorResult& aRv);
+
+  nsPIDOMWindow* GetParentObject() const { return mWindow; }
+
+  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+  static already_AddRefed<MozIsoDepTech>
+  Constructor(const GlobalObject& aGlobal, MozNFCTag& aNFCTag,
+              ErrorResult& aRv);
+
+private:
+  MozIsoDepTech(nsPIDOMWindow* aWindow, MozNFCTag& aNFCTag);
+  virtual ~MozIsoDepTech();
+
+  nsRefPtr<nsPIDOMWindow> mWindow;
+  nsRefPtr<MozNFCTag> mTag;
+
+  static const NFCTechType mTechnology = NFCTechType::ISO_DEP;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif  // mozilla_dom_nfc_MozIsoDepTech_h__
--- a/dom/nfc/moz.build
+++ b/dom/nfc/moz.build
@@ -7,19 +7,21 @@
 
 XPIDL_MODULE = 'dom_nfc'
 
 if CONFIG['MOZ_NFC']:
     XPIDL_SOURCES += [
         'nsINfcContentHelper.idl',
     ]
     EXPORTS.mozilla.dom += [
+        'MozIsoDepTech.h',
         'MozNDEFRecord.h',
     ]
     UNIFIED_SOURCES += [
+        'MozIsoDepTech.cpp',
         'MozNDEFRecord.cpp',
     ]
     EXTRA_COMPONENTS += [
         'NfcContentHelper.js',
         'NfcContentHelper.manifest',
         'nsNfc.js',
         'nsNfc.manifest',
     ]
--- a/dom/nfc/nsNfc.js
+++ b/dom/nfc/nsNfc.js
@@ -132,16 +132,19 @@ MozNFCTagImpl.prototype = {
   id: null,
   type: null,
   maxNDEFSize: null,
   isReadOnly: null,
   isFormatable: null,
   canBeMadeReadOnly: null,
   isLost: false,
 
+  createTech: { "ISO-DEP": (win, tag) => { return new win.MozIsoDepTech(tag); }
+              },
+
   // NFCTag interface:
   readNDEF: function readNDEF() {
     if (this.isLost) {
       throw new this._window.DOMError("InvalidStateError", "NFCTag object is invalid");
     }
 
     let callback = new NfcCallback(this._window);
     this._nfcContentHelper.readNDEF(this.session, callback);
@@ -196,16 +199,34 @@ MozNFCTagImpl.prototype = {
                                       "NFCTag object is not formatable");
     }
 
     let callback = new NfcCallback(this._window);
     this._nfcContentHelper.format(this.session, callback);
     return callback.promise;
   },
 
+  selectTech: function selectTech(tech) {
+    if (this.isLost) {
+      throw new this._window.DOMError("InvalidStateError", "NFCTag object is invalid");
+    }
+
+    if (this.techList.indexOf(tech) == -1) {
+      throw new this._window.DOMError("InvalidAccessError",
+        "NFCTag does not contain selected tag technology");
+    }
+
+    if (this.createTech[tech] === undefined) {
+      throw new this._window.DOMError("InvalidAccessError",
+        "Technology is not supported now");
+    }
+
+    return this.createTech[tech](this._window, this._contentObj);
+  },
+
   transceive: function transceive(tech, cmd) {
     if (this.isLost) {
       throw new this._window.DOMError("InvalidStateError", "NFCTag object is invalid");
     }
 
     let callback = new NfcCallback(this._window);
     this._nfcContentHelper.transceive(this.session, tech, cmd, callback);
     return callback.promise;
@@ -288,27 +309,28 @@ let RFState = {
  * Implementation of navigator NFC object.
  */
 function MozNFCImpl() {
   debug("In MozNFCImpl Constructor");
   try {
     this._nfcContentHelper = Cc["@mozilla.org/nfc/content-helper;1"]
                                .getService(Ci.nsINfcContentHelper);
   } catch(e) {
-    debug("No NFC support.")
+    debug("No NFC support.");
   }
 
   this.eventService = Cc["@mozilla.org/eventlistenerservice;1"]
                         .getService(Ci.nsIEventListenerService);
   this._nfcContentHelper.addEventListener(this);
 }
 MozNFCImpl.prototype = {
   _nfcContentHelper: null,
   _window: null,
   _rfState: null,
+  _contentObj: null,
   nfcPeer: null,
   nfcTag: null,
   eventService: null,
 
   init: function init(aWindow) {
     debug("MozNFCImpl init called");
     this._window = aWindow;
     this.defineEventHandlerGetterSetter("ontagfound");
@@ -411,16 +433,18 @@ MozNFCImpl.prototype = {
       return;
     }
 
     this.eventService.addSystemEventListener(this._window, "visibilitychange",
       this, /* useCapture */false);
 
     let tagImpl = new MozNFCTagImpl(this._window, sessionToken, tagInfo, ndefInfo);
     let tag = this._window.MozNFCTag._create(this._window, tagImpl);
+
+    tagImpl._contentObj = tag;
     this.nfcTag = tag;
 
     let length = records ? records.length : 0;
     let ndefRecords = records ? [] : null;
     for (let i = 0; i < length; i++) {
       let record = records[i];
       ndefRecords.push(new this._window.MozNDEFRecord({tnf: record.tnf,
                                                        type: record.type,
@@ -483,17 +507,17 @@ MozNFCImpl.prototype = {
     if (!this.checkPermissions(perm)) {
       return;
     }
 
     this.eventService.addSystemEventListener(this._window, "visibilitychange",
       this, /* useCapture */false);
 
     let peerImpl = new MozNFCPeerImpl(this._window, sessionToken);
-    this.nfcPeer = this._window.MozNFCPeer._create(this._window, peerImpl)
+    this.nfcPeer = this._window.MozNFCPeer._create(this._window, peerImpl);
     let eventData = { "peer": this.nfcPeer };
     let type = (isPeerReady) ? "peerready" : "peerfound";
 
     debug("fire on" + type + " " + sessionToken);
     let event = new this._window.MozNFCPeerEvent(type, eventData);
     this.__DOM_IMPL__.dispatchEvent(event);
   },
 
new file mode 100644
--- /dev/null
+++ b/dom/webidl/MozIsoDepTech.webidl
@@ -0,0 +1,13 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+[Func="Navigator::HasNFCSupport", AvailableIn="PrivilegedApps",
+ ChromeConstructor(MozNFCTag tag)]
+interface MozIsoDepTech {
+  /**
+   * Send raw command to tag and receive the response.
+   */
+  [Throws]
+  Promise<Uint8Array> transceive(Uint8Array command);
+};
--- a/dom/webidl/MozNFCTag.webidl
+++ b/dom/webidl/MozNFCTag.webidl
@@ -30,16 +30,18 @@ enum NFCTechType {
 enum NFCTagType {
   "Type1",
   "Type2",
   "Type3",
   "Type4",
   "MIFARE-Classic"
 };
 
+typedef MozIsoDepTech MozTagTech;
+
 [JSImplementation="@mozilla.org/nfc/tag;1", AvailableIn="PrivilegedApps"]
 interface MozNFCTag {
   /**
    * The supported technologies of this tag, null if unknown.
    */
   [Cached, Pure] readonly attribute sequence<NFCTechType>? techList;
 
   /**
@@ -95,24 +97,24 @@ interface MozNFCTag {
   [Throws]
   Promise<void> makeReadOnly();
 
   /**
    * Format a tag as NDEF.
    */
   [Throws]
   Promise<void> format();
+
+  [NewObject, Throws]
+  MozTagTech selectTech(NFCTechType tech);
 };
 
 // Mozilla Only
 partial interface MozNFCTag {
   [ChromeOnly]
   attribute DOMString session;
 
   [ChromeOnly]
   void notifyLost();
 
-  /**
-   * Send raw command to tag and receive the response.
-   */
   [ChromeOnly, Throws]
   Promise<Uint8Array> transceive(NFCTechType tech, Uint8Array command);
 };
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -643,16 +643,17 @@ if CONFIG['MOZ_B2G_RIL']:
         'IccCardLockError.webidl',
         'MozIcc.webidl',
         'MozIccInfo.webidl',
         'MozIccManager.webidl',
     ]
 
 if CONFIG['MOZ_NFC']:
     WEBIDL_FILES += [
+         'MozIsoDepTech.webidl',
          'MozNDEFRecord.webidl',
          'MozNFC.webidl',
          'MozNFCPeer.webidl',
          'MozNFCTag.webidl',
          'NfcOptions.webidl',
     ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':