From 22579845a9ef6edbef2a3a71537824cbc14a70be Mon Sep 17 00:00:00 2001
From: Zoltan Kis
An NFC tag is a passive NFC device. @@ -608,20 +608,18 @@ this specification.
- navigator.nfc.requestAdapter().then((adapter) => { - adapter.watch({}, (message) => { - console.log('NDEF message received from URL ' + message.url); - if (message[0].recordType == 'empty') { - adapter.pushMessage([ - { - recordType: "text", - mediaType: "", - data: "Initializing tag" - } - ]); - } - processMessage(message); - }); + navigator.nfc.watch({}, (message) => { + console.log('NDEF message received from URL ' + message.url); + if (message[0].recordType == 'empty') { + navigator.nfc.push([ + { + recordType: "text", + mediaType: "", + data: "Initializing tag" + } + ]); + } + processMessage(message); }); function processMessage(message) { @@ -648,8 +646,7 @@ };
- navigator.nfc.requestAdapter().then((adapter) => { - adapter.pushMessage([ + navigator.nfc.push([ { recordType: "text", mediaType: "", @@ -660,23 +657,18 @@ }).catch(() => { console.log("Send failed :-( try again."); }); - });
- navigator.nfc.requestAdapter().then((adapter) => { - console.log("Awaiting game state"); - - adapter.watch({ url: "*://mydomain/mygame/*" }, (message) => { - console.log("Game state received from: " + message.url); - console.log("Stored state: " + message.data); - - // Now do some calculations and then update the state. - // ... - adapter.pushMessage([ { data: { level: 3, points: 4500, lives: 3 }} ]) - .then(() => { console.log('We have stored your current game state.'); }) - .catch(() => { console.log('Failed updating game state, try again.'); }); - }); + navigator.nfc.watch({ url: "*://mydomain/mygame/*" }, (message) => { + console.log("Game state received from: " + message.url); + console.log("Stored state: " + message.data); + + // Now do some calculations and then update the state. + // ... + navigator.nfc.push([ { data: { level: 3, points: 4500, lives: 3 }} ]) + .then(() => { console.log('We have stored your current game state.'); }) + .catch(() => { console.log('Failed updating game state, try again.'); }); });@@ -775,17 +767,22 @@ This use case is not supported in this version of the specification. +
+ Users may attach one or more external NFC adapters to their + devices, in addition to a built-in adapter. Users may use either + NFC adapter. +
+High level features for the Web NFC specification include the following:
The main threats are summarized in the - Security and Privacy document. + + Security and Privacy document.
In this specification the following threats are handled with the highest @@ -941,9 +939,9 @@ For details see the Writing or pushing content section.
interface NFC { - Promise<NFCAdapter> requestAdapter(); + Promise<void> push(sequence<NFCRecord> message, NFCPushOptions options); + void cancelPush(NFCPushTarget target); + Promise<long> watch(NFCWatchOptions options, MessageCallback callback); + Promise<void> unwatch(long id); }; + + callback MessageCallback = void (NFCMessage message);-
+ In later versions cancelPush()
and unwatch()
+ may be obsoleted by
+ cancelable Promises used with push()
and
+ watch()
, respectively.
+
- Instances of NFCAdapter are created with the internal slots + The NFC object is created with the internal slots described in the following table:
[[\suspended]] |
- A boolean flag indicating whether the adapter is suspended or not,
- initially false .
+ A boolean flag indicating whether NFC functionality is
+ suspended or not, initially
+ false .
|
- The NFC.requestAdapter() method, when invoked MUST return a new - promise promise and run the following steps in parallel: -
"SecurityError"
,
- and abort these steps.
- "NotSupportedError"
, and abort these steps.
- "NotFoundError"
, and abort these steps.
- null
.
- null
, reject promise
- with "NotFoundError"
, and abort these steps.
- NFCAdapter
instance
- oldAdapter as a result of a previous invocation of this
- algorithm, run the following sub-steps:
- NFCAdapter
- object bound to adapter.
-
Each Window
object connected to the
- script execution environment has a separate
- NFCAdapter
instance. The
+ script execution environment has a separate NFC
+ instance. The
visibility and
focus state of the
Window
object determines the
suspended state of the associated
- NFCAdapter
instance.
+ NFC
instance.
The term suspended in this specification
@@ -1349,7 +1300,7 @@
pushed, and no received NFC content is presented via watches while
suspended.
However, platform level timers for the
- NFCAdapter.pushMessage()
method continue running,
+ NFC.push()
method continue running,
and if they expire, the event should be recorded and handled
when execution next resumes, i.e. when the focus
event is fired on the Window
object.
@@ -1364,7 +1315,7 @@
false
.
+ Set NFC@[[\suspended]] to false
.
@@ -1375,73 +1326,49 @@
true
.
+ Set NFC@[[\suspended]] to true
.
If a user agent is to
make disappear
- an NFCAdapter
object adapter,
- run the following NFC adapter release steps, given
- adapter:
+ an NFC
object nfc, run the following
+ NFC release steps, given nfc:
true
.
+ Set nfc@[[\suspended]] to true
.
"tag"
as parameter.
"peer"
as parameter.
- The UA should run the NFC adapter release steps, given - adapter, as additional steps to the + The UA should run the NFC release steps, given nfc, as + additional steps to the unloading document cleanup steps.
-- interface NFCAdapter { - Promise<void> pushMessage(sequence<NFCRecord> message, NFCPushOptions options); - void cancelPush(NFCPushTarget target); - Promise<long> watch(NFCWatchOptions options, MessageCallback callback); - Promise<void> unwatch(long id); - }; - - callback MessageCallback = void (NFCMessage message); --
- In later versions cancelPush()
and unwatch()
- may be obsoleted by
- cancelable Promises used with pushMessage()
and
- watch()
, respectively.
-
enum NFCPushTarget { @@ -1456,17 +1383,17 @@
The NFCPushOptions.target property
- denotes the intended target for the pending pushMessage()
+ denotes the intended target for the pending push()
operation.
The NFCPushOptions.timeout property
- denotes the timeout for the pending pushMessage()
+ denotes the timeout for the pending push()
operation expressed in milliseconds. The default value is
implementation-dependent. The value Infinity
means there is
- no timeout. After the timeout expires, the
- message set for pushing is cleared, an error is returned, and a new
- Web NFC message can be set for pushing.
+ no timeout, i.e. no timer is started. After the timeout
+ expires, the message set for pushing is cleared, an error is returned,
+ and a new Web NFC message can be set for pushing.
cancelPush()
method.
-
+
The
- NFCAdapter.pushMessage(message, options)
+ NFC.push(message, options)
method, when invoked, MUST run the
push a message algorithm:
- In other words, the current invocation of
+ There is very small likelihood that a simultaneous tap
+ will happen on two or multiple different
+ NFC adapters.
+ If it happens, the user will likely need to repeat the
+ tap until full success. The error here gives an indication
+ that the operation needs to be repeated. Otherwise the
+ user may think the operation succeeded on all
+ NFC adapters.
+
If this@[[\suspended]] is The push() method
+
@@ -1573,12 +1500,12 @@
+ "AbortError"
.
pushMessage()
+ In other words, the current invocation of push()
rejects and replaces existing running invocations handling the same
options.target. At any given moment there may be
maximum two instances of this algorithm running: one targeting
@@ -1666,18 +1593,34 @@
"NetworkError"
and terminate these
steps.
+ true
,
@@ -2128,12 +2071,12 @@
The
- NFCAdapter.cancelPush(target)
+ NFC.cancelPush(target)
method, when invoked, MUST run cancel push algorithm:
"tag"
and
there is an instance of the
- NFCAdapter.pushMessage()
algorithm running with
+ NFC.push()
algorithm running with
options.target "tag"
, or if
target is "peer"
and there is an instance of
- the NFCAdapter.pushMessage()
algorithm running
+ the NFC.push()
algorithm running
with options.target "peer"
, then
In order to receive NFC content, the client needs to call the - NFCAdapter.watch() to opt into content of interest. + NFC.watch() to opt into content of interest.
An NFC watch is referring to a
NFCWatchOptions
filter saved together with the
- NFCAdapter
instance it belongs
+ NFC
instance it belongs
to, and a locally unique identifier which is used for cancellation.
The section
Receiving and parsing content uses NFC watches to match
@@ -2254,12 +2197,9 @@
Multiple consecutive calls to the watch()
method from the
same origin create filters which are in OR relationship.
- Watch filters are grouped by NFC adapter. -
When the
- NFCAdapter.watch(options, callback)
+ NFC.watch(options, callback)
method is invoked, the UA MUST run the following
NFC watch algorithm:
"SecurityError"
, and abort these steps.
- When the NFCAdapter.unwatch(id)
method is
+ When the NFC.unwatch(id)
method is
invoked, the UA MUST return a Promise
promise and run the following steps in parallel.
undefined
, then
remove all watches and filters set by successive calls of the
- NFC watch()
method on the current
- NFC adapter.
+ NFC watch()
method on all NFC adapters.
"NotFoundError"
, and abort these steps.
- If there are any NFC watches set up for any NFC adapter in - their NFCAdapter@[[\watchList]], then - UAs MUST listen to NDEF messages, according to step 9 + If there are any NFC watches set up in NFC@[[\watchList]], + then UAs MUST listen to NDEF messages, according to step 9 of the NFC watch algorithm.
true
, abort
+ If NFC@[[\suspended]] is true
, abort
these steps.
false
and
+ If NFC@[[\suspended]] is false
and
message.data is not empty, run the
dispatch NFC content steps given message.
NFCMessage
as a dictionary.NFCAdapter
.NFC
.
The Web NFC message origin is an ASCII serialized origin
with "https"
scheme, stored in the Web NFC record.
+ For NFC content that is not a Web NFC message, it is
+ null
.
The Web NFC Id is a URL according to @@ -1229,9 +1231,11 @@ to handle incoming Web NFC messages either from an NFC tag or an NFC peer.
+ typedef (DOMString or ArrayBuffer or NFCMessage) NFCPushMessage; + interface NFC { - Promise<void> push(sequence<NFCRecord> message, NFCPushOptions options); - void cancelPush(NFCPushTarget target); + Promise<void> push(NFCPushMessage message, optional NFCPushOptions options); + Promise<void> cancelPush(NFCPushTarget target); Promise<long> watch(NFCWatchOptions options, MessageCallback callback); Promise<void> unwatch(long id); }; @@ -1281,6 +1285,7 @@ The NFC object represents all NFC hardware devices that can read and write NFC content. +
@@ -1373,12 +1378,14 @@
enum NFCPushTarget { "tag", - "peer" + "peer", + "any" }; dictionary NFCPushOptions { - NFCPushTarget target; - unsigned long timeout; + NFCPushTarget target = "any"; + unrestricted double timeout = Infinity; + boolean ignoreRead = true; };
@@ -1395,7 +1402,15 @@ expires, the message set for pushing is cleared, an error is returned, and a new Web NFC message can be set for pushing.
+
+ The NFCPushOptions.ignoreRead property if true
,
+ will make the push algorithm skip invoking the
+ receiving and parsing steps on the
+ read NFC content if the NFC device in communication range
+ is an NFC tag.
+
To describe which messages an application is interested in, the @@ -1492,33 +1507,6 @@ If any exception occurs while running these steps, reject promise with that exception, and abort these steps. -
"SyntaxError"
, and abort these steps.
- Otherwise let target be options.target, and
- timeout be options.timeout.
- "AbortError"
.
-
- In other words, the current invocation of push()
- rejects and replaces existing running invocations handling the same
- options.target. At any given moment there may be
- maximum two instances of this algorithm running: one targeting
- NFC tags, and another targeting NFC peers.
-
- Implementations are expected to clean up state on aborting these - steps, e.g. stop the related timer, clear the related push - message, as well as release any resources bound to NFC - functionality, so that new invocations of this algorithm do not - depend on previous invocations. -
-false
, then reject
- promise with "SecurityError"
, and abort
- these steps.
+ Let target be options.target or the default
+ "any"
.
"SyntaxError"
,
- and abort these steps.
+ Let timeout be options.timeout or the
+ default Infinity
.
+ DOMString
or ArrayBuffer
or instance of
+ NFCPushMessage
, reject promise with
+ "SyntaxError"
, and abort these steps.
NFCPushMessage
, and message.data is an
+ empty sequence, reject promise with
+ "SyntaxError"
, and abort these steps.
+ "SyntaxError"
, and abort these steps.
Infinity
,
+ If target is "any"
, run the following
+ steps twice, once with slot with value
+ "tag"
, and once with the value "peer"
;
+ otherwise run the following step once, with
+ slot given the value of target.
+ "AbortError"
.
+
+ In other words, the current invocation of push()
+ rejects and replaces existing running invocations handling the
+ same slot. At any given moment there may be
+ maximum two instances of this algorithm running: one targeting
+ NFC tags, and another targeting NFC peers.
+
+ Implementations are expected to clean up state on aborting these + steps, e.g. stop the related timer, clear the related push + message, as well as release any resources bound to NFC + functionality, so that new invocations of this algorithm do not + depend on previous invocations. +
+Infinity
,
start a timer timer with the timeout value set to
- options.timeout.
+ timeout.
cancelPush()
- method is called while timer is active, reject
- promise with "AbortError"
, and
+ method is called while timer is active with
+ target or "any"
, then reject
+ promise with "AbortError"
and
terminate these steps, as described in the
- cancelPush()
algorithm.
+ cancelPush()
steps.
false
, and if device is an
- NFC tag and target is "tag"
or if
- device is an NFC peer and target
- is "peer"
, then
+ If an NFC device device comes within
+ communication range, which an NFC tag and
+ target is "tag"
or "any"
,
+ or if device is an NFC peer and
+ target is "peer"
or "any"
,
+ and this@[[\suspended]] is false
, then
+ run the following sub-steps:
true
,
+ run the receiving steps.
+ null
, or it is
+ different than the ASCII serialized origin of the
+ incumbent settings object, and if the
+ obtain push permission steps return
+ false
, then reject promise with
+ "SecurityError"
, and abort these steps.
+ There is very small likelihood that a simultaneous tap - will happen on two or multiple different + will happen on two or multiple different and connected NFC adapters. If it happens, the user will likely need to repeat the - tap until full success. The error here gives an indication - that the operation needs to be repeated. Otherwise the - user may think the operation succeeded on all - NFC adapters. + taps until full success, or one device at a time. + The error here gives an indication that the operation + needs to be repeated. Otherwise the user may think the + operation succeeded on all connected NFC adapters.
If this@[[\suspended]] is
- To obtain push permission given target of type
-
- In other words, updating NFC tags written by the same
- origin does not require expressed permission. All
- other writes, including that of empty tags, require
- expressed permission.
-
The
@@ -1688,7 +1719,7 @@
origin and global object, store the result
in the
- permission store, and return the status.
+ permission store, and on success return true
,
@@ -1635,32 +1682,14 @@
Obtaining push permission
NFCPushTarget
, run these steps:
+ To obtain push permission, run these steps:
true
.
- "peer"
,
+ If there is a prearranged trust relationship,
return true
.
"tag"
, read that
- NFC tag. If it contains a Web NFC message with its
- Web NFC message origin equal to the
- ASCII serialized origin of the
- incumbent settings object, then return true
.
-
- "prompt"
, then request permission from the user
- for the Web NFC permission name. If that is granted,
- return true
.
+ "prompt", then optionally
+
+ request permission from the user for the
+ Web NFC permission name.
+ If that is granted, return true
.
true
.
Promise
promise, and
+ then continue running this algorithm in parallel.
+ "tag"
and
- there is an instance of the
- NFC.push()
algorithm running with
- options.target "tag"
, or if
- target is "peer"
and there is an instance of
- the NFC.push()
algorithm running
- with options.target "peer"
, then
+ If there is an instance of the NFC.push()
+ algorithm running with its target equal to
+ target or "any"
, then
"AbortError"
and terminate the steps of the
+ "AbortError"
and abort the steps of the
instance.
From f06aa125e18830edea2747277e0aaa36669b0623 Mon Sep 17 00:00:00 2001
From: Zoltan Kis
- In later versions
- The NFCPushOptions.ignoreRead property if cancelPush()
and unwatch()
+ In later versions cancelPush()
and cancelWatch()
may be obsoleted by
cancelable Promises used with push()
and
watch()
, respectively.
@@ -1403,11 +1403,10 @@
and a new Web NFC message can be set for pushing.
true
,
- will make the push algorithm skip invoking the
- receiving and parsing steps on the
- read NFC content if the NFC device in communication range
- is an NFC tag.
+ When the value of the NFCPushOptions.ignoreRead property is
+ true
, the push algorithm
+ will skip invoking the
+ receiving and parsing steps for an NFC tag.
"SecurityError"
, and abort these steps.
+ "SecurityError"
and abort these steps.
- "NotSupportedError"
, and abort these steps.
+ "NotSupportedError" and abort these steps.
"any"
.
+ Let target be options.target.
Infinity
.
+ Let timeout be options.timeout.
DOMString
or ArrayBuffer
or instance of
- NFCPushMessage
, reject promise with
- "SyntaxError"
, and abort these steps.
+ If the type of the message parameter is not
+ DOMString
or ArrayBuffer
, and it is not an
+ instance of NFCPushMessage
, reject promise
+ with "SyntaxError"
, and abort these steps.
NFCPushMessage
, and message.data is an
empty sequence, reject promise with
- "SyntaxError"
, and abort these steps.
+ "SyntaxError"
and abort these steps.
"SyntaxError"
, and abort these steps.
+ "SyntaxError"
and abort these steps.
"any"
, run the following
- steps twice, once with slot with value
- "tag"
, and once with the value "peer"
;
+ steps twice, once with slot set to the value
+ "tag"
, and once set to the value "peer"
;
otherwise run the following step once, with
- slot given the value of target.
+ slot set to the value of target.
Infinity
,
+ If timeout value is not equal to Infinity
,
start a timer timer with the timeout value set to
timeout.
"any"
, then reject
promise with "AbortError"
and
- terminate these steps, as described in the
+ abort these steps, as described in the
cancelPush()
steps.
"tag"
or "any"
,
- or if device is an NFC peer and
- target is "peer"
or "any"
,
- and this@[[\suspended]] is false
, then
- run the following sub-steps:
+ communication range, verify the following conditions:
+ "tag"
or "any"
+ "peer"
or
+ "any"
+ false
.
+ true
,
- run the receiving steps.
+ If options.ignoreRead is not equal to
+ true
, run the
+ receiving steps.
null
, or it is
different than the ASCII serialized origin of the
- incumbent settings object, and if the
+ incumbent settings object, and the
obtain push permission steps return
false
, then reject promise with
- "SecurityError"
, and abort these steps.
+ "SecurityError"
and abort these steps.
"NetworkError"
and terminate these
+ "NetworkError"
and abort these
steps.
+ Multiple adapters should be used sequentially by users. There is very small likelihood that a simultaneous tap will happen on two or multiple different and connected NFC adapters. If it happens, the user will likely need to repeat the - taps until full success, or one device at a time. + taps until success, preferably one device at a time. The error here gives an indication that the operation needs to be repeated. Otherwise the user may think the operation succeeded on all connected NFC adapters.
"SyntaxError"
, and abort these steps.
+ "SyntaxError"
and abort these steps.
"SyntaxError"
exception, and abort these steps.
+ throw a "SyntaxError"
exception and abort these steps.
@@ -1884,7 +1893,7 @@
for
text.
If not, then the UA MAY throw a "SyntaxError"
- exception, and abort these steps.
+ exception and abort these steps.
"en"
.
@@ -1898,7 +1907,7 @@
in the
IANA language registry
(or [[ISO-639.2]]), then UAs MAY throw a "SyntaxError"
- exception, and abort these steps.
+ exception and abort these steps.
Note that lang=
is not standard parameter
@@ -2041,7 +2050,7 @@
ArrayBuffer
, reject promise with
- "SyntaxError"
, and abort these steps.
+ "SyntaxError"
and abort these steps.
Promise
promise, and
+ Return a new Promise
promise and
then continue running this algorithm in parallel.
"SecurityError"
, and abort these steps.
+ "SecurityError"
and abort these steps.
NFC.push()
- algorithm running with its target equal to
+ algorithm running with its options.target equal to
target or "any"
, then
"SecurityError"
, and abort these steps.
+ "SecurityError"
and abort these steps.
"NotSupportedError"
, and
- terminate this algorithm.
+ promise with "NotSupportedError"
and
+ abort these steps.
false
, then reject promise with
- "SecurityError"
, and abort these steps.
+ "SecurityError"
and abort these steps.
"NotSupportedError"
, and abort these steps.
+ "NotSupportedError"
and abort these steps.
- When the NFC.unwatch(id)
method is
+ When the NFC.cancelWatch(id)
method is
invoked, the UA MUST return a Promise
promise and run the following steps in parallel.
-
"SecurityError"
, and abort these steps.
+ "SecurityError"
and abort these steps.
"NotFoundError"
, and abort these steps.
+ "NotFoundError"
and abort these steps.
+ // Specifying an option for pushing to peers only, + // and thus, do nothing when the user taps a tag. + navigator.nfc.push("Text meant for peers only", { target: "peer" }) + .then(() => { + console.log("Message pushed."); + }).catch((error) => { + console.log("Push failed :-( try again."); + }); ++
+ // Push a text string (here: serialized JSON), with no options specified + // (target defaults to any device). + navigator.nfc.push('{ prop1: "value1"; prop2: "value2" }') + .then(() => { + console.log("Message pushed."); + }).catch((error) => { + console.log("Push failed :-( try again."); + }); ++
+ // To push an NDEF record of URL type, use NFCMessage + navigator.nfc.push({ + data: [{ recordType: "url", data: "/path/resource/" }]; + }).then(() => { + console.log("Message pushed."); + }).catch((error) => { + console.log("Push failed :-( try again."); + }); +
- navigator.nfc.watch({}, (message) => { + // Use default options for watch. + navigator.nfc.watch((message) => { console.log('NDEF message received from URL ' + message.url); - if (message[0].recordType == 'empty') { - navigator.nfc.push([ - { - recordType: "text", - mediaType: "", - data: "Initializing tag" - } - ]); + if (message.data[0].recordType == 'empty') { + navigator.nfc.push({ + url: "/mypath/myapp", // path set by the application + data: [ + { + recordType: "url", + mediaType: "", // this line could be omitted + data: "Initial data" + } + ]}); } processMessage(message); - }); + }).then(() => { console.log("Watch added.");}) + .catch((error) => { console.log("Adding watch failed: " + error.name )}); function processMessage(message) { message.data.forEach((record) => { @@ -647,31 +682,32 @@ console.log('JSON data: ' + obj.myProperty.toString()); };-
- navigator.nfc.push([ - { - recordType: "text", - mediaType: "", - data: "Data meant for peers" - } - ]).then(() => { - console.log("Message sent."); - }).catch(() => { - console.log("Send failed :-( try again."); - }); -
- navigator.nfc.watch({ url: "*://mydomain/mygame/*" }, (message) => { + // Watching for specific content; here only own game data. + navigator.nfc.watch(reader, { url: "*/mypath/mygame/*" }); + + function reader(message) { console.log("Game state received from: " + message.url); console.log("Stored state: " + message.data); // Now do some calculations and then update the state. - // ... - navigator.nfc.push([ { data: { level: 3, points: 4500, lives: 3 }} ]) + + // Specify custom format, to control NDEF data layout. + var message = { + url: "https://company.com/path/game/update", // app specific path + data: [{ + recordType: "json", + mediaType: "application/json", + data: { level: 3, points: 4500, lives: 3 } + }] + }; + + // Push the message to tag or peer, whichever is tapped first. + navigator.nfc.push(message) .then(() => { console.log('We have stored your current game state.'); }) - .catch(() => { console.log('Failed updating game state, try again.'); }); - }); + .catch((error) => { console.log('Failed updating game state, try again.'); }); + };
The NFCMessage.url
- property represents the Web NFC Id of the Web NFC message.
+ property represents the Web NFC Id of a received
+ Web NFC message. When used in the NFC.push()
+ method, it represents a URL path used for constructing the
+ Web NFC Id of the pushed Web NFC content.
The NFCMessage.data @@ -1827,7 +1866,9 @@
- "text/"
, then throw a "SyntaxError"
exception,
+ "text/", then throw a "SyntaxError"
exception
and abort these steps.
In addition, UAs MAY check that
@@ -2080,7 +2121,8 @@
- To create a Web NFC record, run these steps: + To create a Web NFC record given url, + run these steps:
"urn:nfc:ext:w3.org:webnfc"
.
+ To create a Web NFC Id given url, + run these steps: +
"SyntaxError"
exception and abort these steps.
+ + This means url should be a valid URL path. +
+