Index: third_party/WebKit/Source/modules/nfc/NFC.cpp |
diff --git a/third_party/WebKit/Source/modules/nfc/NFC.cpp b/third_party/WebKit/Source/modules/nfc/NFC.cpp |
index a986a5840cfe1362de9143f2dc7eb3d0b983ec0f..6c75a76ac609afc87c6a7b06ece18d4660b422b0 100644 |
--- a/third_party/WebKit/Source/modules/nfc/NFC.cpp |
+++ b/third_party/WebKit/Source/modules/nfc/NFC.cpp |
@@ -15,6 +15,7 @@ |
#include "modules/nfc/NFCError.h" |
#include "modules/nfc/NFCMessage.h" |
#include "modules/nfc/NFCPushOptions.h" |
+#include "modules/nfc/NFCWatchOptions.h" |
#include "platform/mojo/MojoHelper.h" |
#include "public/platform/InterfaceProvider.h" |
#include "public/platform/Platform.h" |
@@ -36,9 +37,13 @@ using device::nfc::mojom::blink::NFCMessagePtr; |
using device::nfc::mojom::blink::NFCRecord; |
using device::nfc::mojom::blink::NFCRecordPtr; |
using device::nfc::mojom::blink::NFCRecordType; |
+using device::nfc::mojom::blink::NFCRecordTypeFilter; |
using device::nfc::mojom::blink::NFCPushOptions; |
using device::nfc::mojom::blink::NFCPushOptionsPtr; |
using device::nfc::mojom::blink::NFCPushTarget; |
+using device::nfc::mojom::blink::NFCWatchMode; |
+using device::nfc::mojom::blink::NFCWatchOptions; |
+using device::nfc::mojom::blink::NFCWatchOptionsPtr; |
NFCPushTarget toNFCPushTarget(const WTF::String& target) { |
if (target == "tag") |
@@ -70,6 +75,17 @@ NFCRecordType toNFCRecordType(const WTF::String& recordType) { |
return NFCRecordType::EMPTY; |
} |
+NFCWatchMode toNFCWatchMode(const WTF::String& watchMode) { |
+ if (watchMode == "web-nfc-only") |
+ return NFCWatchMode::WEBNFC_ONLY; |
+ |
+ if (watchMode == "any") |
+ return NFCWatchMode::ANY; |
+ |
+ NOTREACHED(); |
+ return NFCWatchMode::WEBNFC_ONLY; |
+} |
+ |
// https://w3c.github.io/web-nfc/#creating-web-nfc-message Step 2.1 |
// If NFCRecord type is not provided, deduce NFCRecord type from JS data type: |
// String or Number => 'text' record |
@@ -302,6 +318,32 @@ struct TypeConverter<NFCPushOptionsPtr, blink::NFCPushOptions> { |
} |
}; |
+template <> |
+struct TypeConverter<NFCWatchOptionsPtr, blink::NFCWatchOptions> { |
+ static NFCWatchOptionsPtr Convert( |
+ const blink::NFCWatchOptions& watchOptions) { |
+ // https://w3c.github.io/web-nfc/#the-nfcwatchoptions-dictionary |
+ // Default values for NFCWatchOptions dictionary are: |
+ // url = "", recordType = null, mediaType = "", mode = "web-nfc-only" |
+ NFCWatchOptionsPtr watchOptionsPtr = NFCWatchOptions::New(); |
+ watchOptionsPtr->url = watchOptions.url(); |
+ watchOptionsPtr->media_type = watchOptions.mediaType(); |
+ |
+ if (watchOptions.hasMode()) |
+ watchOptionsPtr->mode = toNFCWatchMode(watchOptions.mode()); |
+ else |
+ watchOptionsPtr->mode = NFCWatchMode::WEBNFC_ONLY; |
+ |
+ if (watchOptions.hasRecordType()) { |
+ watchOptionsPtr->record_filter = NFCRecordTypeFilter::New(); |
+ watchOptionsPtr->record_filter->record_type = |
+ toNFCRecordType(watchOptions.recordType()); |
+ } |
+ |
+ return watchOptionsPtr; |
+ } |
+}; |
+ |
} // namespace mojo |
namespace blink { |
@@ -426,6 +468,96 @@ bool setURL(const String& origin, |
return originURL.isValid(); |
} |
+String toNFCRecordType(const device::nfc::mojom::blink::NFCRecordType& type) { |
+ switch (type) { |
+ case device::nfc::mojom::blink::NFCRecordType::TEXT: |
+ return "text"; |
+ case device::nfc::mojom::blink::NFCRecordType::URL: |
+ return "url"; |
+ case device::nfc::mojom::blink::NFCRecordType::JSON: |
+ return "json"; |
+ case device::nfc::mojom::blink::NFCRecordType::OPAQUE_RECORD: |
+ return "opaque"; |
+ case device::nfc::mojom::blink::NFCRecordType::EMPTY: |
+ return "empty"; |
+ } |
+ |
+ NOTREACHED(); |
+ return String(); |
+} |
+ |
+v8::Local<v8::Value> toV8( |
+ ScriptState* scriptState, |
+ const device::nfc::mojom::blink::NFCRecordPtr& record) { |
+ switch (record->record_type) { |
+ case device::nfc::mojom::blink::NFCRecordType::TEXT: |
+ case device::nfc::mojom::blink::NFCRecordType::URL: |
+ case device::nfc::mojom::blink::NFCRecordType::JSON: { |
+ String stringData; |
+ if (!record->data.isEmpty()) { |
+ stringData = String::fromUTF8WithLatin1Fallback( |
+ static_cast<unsigned char*>(&record->data.first()), |
+ record->data.size()); |
+ } |
+ |
+ v8::Isolate* isolate = scriptState->isolate(); |
+ v8::Local<v8::String> string = v8String(isolate, stringData); |
+ |
+ // Stringified JSON must be converted back to an Object. |
+ if (record->record_type == |
+ device::nfc::mojom::blink::NFCRecordType::JSON) { |
+ v8::Local<v8::Value> jsonObject; |
+ v8::TryCatch tryCatch(isolate); |
+ if (!v8Call(v8::JSON::Parse(isolate, string), jsonObject, tryCatch)) { |
+ return v8::Null(isolate); |
+ } |
+ |
+ return jsonObject; |
+ } |
+ |
+ return string; |
+ } |
+ |
+ case device::nfc::mojom::blink::NFCRecordType::OPAQUE_RECORD: { |
+ if (!record->data.isEmpty()) { |
+ DOMArrayBuffer* buffer = DOMArrayBuffer::create( |
+ static_cast<void*>(&record->data.first()), record->data.size()); |
+ return toV8(buffer, scriptState->context()->Global(), |
+ scriptState->isolate()); |
+ } |
+ |
+ return v8::Null(scriptState->isolate()); |
+ } |
+ |
+ case device::nfc::mojom::blink::NFCRecordType::EMPTY: |
+ return v8::Null(scriptState->isolate()); |
+ } |
+ |
+ NOTREACHED(); |
+ return v8::Local<v8::Value>(); |
+} |
+ |
+NFCRecord toNFCRecord(ScriptState* scriptState, |
+ const device::nfc::mojom::blink::NFCRecordPtr& record) { |
+ NFCRecord nfcRecord; |
+ nfcRecord.setMediaType(record->media_type); |
+ nfcRecord.setRecordType(toNFCRecordType(record->record_type)); |
+ nfcRecord.setData(ScriptValue(scriptState, toV8(scriptState, record))); |
+ return nfcRecord; |
+} |
+ |
+NFCMessage toNFCMessage( |
+ ScriptState* scriptState, |
+ const device::nfc::mojom::blink::NFCMessagePtr& message) { |
+ NFCMessage nfcMessage; |
+ nfcMessage.setURL(message->url); |
+ blink::HeapVector<NFCRecord> records; |
+ for (size_t i = 0; i < message->data.size(); ++i) |
+ records.append(toNFCRecord(scriptState, message->data[i])); |
+ nfcMessage.setData(records); |
+ return nfcMessage; |
+} |
+ |
size_t getNFCMessageSize( |
const device::nfc::mojom::blink::NFCMessagePtr& message) { |
size_t messageSize = message->url.charactersSizeInBytes(); |
@@ -467,25 +599,22 @@ void NFC::dispose() { |
void NFC::contextDestroyed() { |
m_nfc.reset(); |
m_requests.clear(); |
+ m_callbacks.clear(); |
} |
// https://w3c.github.io/web-nfc/#writing-or-pushing-content |
+// https://w3c.github.io/web-nfc/#dom-nfc-push |
ScriptPromise NFC::push(ScriptState* scriptState, |
const NFCPushMessage& pushMessage, |
const NFCPushOptions& options) { |
- String errorMessage; |
- if (!scriptState->getExecutionContext()->isSecureContext(errorMessage)) |
- return ScriptPromise::rejectWithDOMException( |
- scriptState, DOMException::create(SecurityError, errorMessage)); |
+ ScriptPromise promise = rejectIfNotSupported(scriptState); |
+ if (!promise.isEmpty()) |
+ return promise; |
DOMException* exception = isValidNFCPushMessage(pushMessage); |
if (exception) |
return ScriptPromise::rejectWithDOMException(scriptState, exception); |
- if (!m_nfc) |
- return ScriptPromise::rejectWithDOMException( |
- scriptState, DOMException::create(NotSupportedError)); |
- |
device::nfc::mojom::blink::NFCMessagePtr message = |
device::nfc::mojom::blink::NFCMessage::From(pushMessage); |
if (!message) |
@@ -516,15 +645,11 @@ ScriptPromise NFC::push(ScriptState* scriptState, |
return resolver->promise(); |
} |
+// https://w3c.github.io/web-nfc/#dom-nfc-cancelpush |
ScriptPromise NFC::cancelPush(ScriptState* scriptState, const String& target) { |
- String errorMessage; |
- if (!scriptState->getExecutionContext()->isSecureContext(errorMessage)) |
- return ScriptPromise::rejectWithDOMException( |
- scriptState, DOMException::create(SecurityError, errorMessage)); |
- |
- if (!m_nfc) |
- return ScriptPromise::rejectWithDOMException( |
- scriptState, DOMException::create(NotSupportedError)); |
+ ScriptPromise promise = rejectIfNotSupported(scriptState); |
+ if (!promise.isEmpty()) |
+ return promise; |
ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); |
m_requests.add(resolver); |
@@ -536,24 +661,61 @@ ScriptPromise NFC::cancelPush(ScriptState* scriptState, const String& target) { |
return resolver->promise(); |
} |
+// https://w3c.github.io/web-nfc/#watching-for-content |
+// https://w3c.github.io/web-nfc/#dom-nfc-watch |
ScriptPromise NFC::watch(ScriptState* scriptState, |
MessageCallback* callback, |
const NFCWatchOptions& options) { |
- // TODO(shalamov): To be implemented. |
- return ScriptPromise::rejectWithDOMException( |
- scriptState, DOMException::create(NotSupportedError)); |
+ ScriptPromise promise = rejectIfNotSupported(scriptState); |
+ if (!promise.isEmpty()) |
+ return promise; |
+ |
+ callback->setScriptState(scriptState); |
+ ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); |
+ m_requests.add(resolver); |
+ auto watchCallback = convertToBaseCallback( |
+ WTF::bind(&NFC::OnWatchRegistered, wrapPersistent(this), |
+ wrapPersistent(callback), wrapPersistent(resolver))); |
+ m_nfc->Watch(device::nfc::mojom::blink::NFCWatchOptions::From(options), |
+ watchCallback); |
+ return resolver->promise(); |
} |
+// https://w3c.github.io/web-nfc/#dom-nfc-cancelwatch |
ScriptPromise NFC::cancelWatch(ScriptState* scriptState, long id) { |
- // TODO(shalamov): To be implemented. |
- return ScriptPromise::rejectWithDOMException( |
- scriptState, DOMException::create(NotSupportedError)); |
+ ScriptPromise promise = rejectIfNotSupported(scriptState); |
+ if (!promise.isEmpty()) |
+ return promise; |
+ |
+ if (id) { |
+ m_callbacks.remove(id); |
+ } else { |
+ return ScriptPromise::rejectWithDOMException( |
+ scriptState, DOMException::create(NotFoundError)); |
+ } |
+ |
+ ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); |
+ m_requests.add(resolver); |
+ m_nfc->CancelWatch(id, convertToBaseCallback(WTF::bind( |
+ &NFC::OnRequestCompleted, wrapPersistent(this), |
+ wrapPersistent(resolver)))); |
+ return resolver->promise(); |
} |
+// https://w3c.github.io/web-nfc/#dom-nfc-cancelwatch |
+// If watchId is not provided to nfc.cancelWatch, cancel all watch operations. |
ScriptPromise NFC::cancelWatch(ScriptState* scriptState) { |
- // TODO(shalamov): To be implemented. |
- return ScriptPromise::rejectWithDOMException( |
- scriptState, DOMException::create(NotSupportedError)); |
+ ScriptPromise promise = rejectIfNotSupported(scriptState); |
+ if (!promise.isEmpty()) |
+ return promise; |
+ |
+ m_callbacks.clear(); |
+ ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); |
+ m_requests.add(resolver); |
+ m_nfc->CancelAllWatches(convertToBaseCallback( |
+ WTF::bind(&NFC::OnRequestCompleted, wrapPersistent(this), |
+ wrapPersistent(resolver)))); |
+ return resolver->promise(); |
} |
void NFC::pageVisibilityChanged() { |
@@ -588,26 +750,76 @@ void NFC::OnConnectionError() { |
} |
m_nfc.reset(); |
+ m_callbacks.clear(); |
// If NFCService is not available or disappears when NFC hardware is |
// disabled, reject promise with NotSupportedError exception. |
- for (ScriptPromiseResolver* resolver : m_requests) { |
+ for (ScriptPromiseResolver* resolver : m_requests) |
resolver->reject(NFCError::take( |
resolver, device::nfc::mojom::blink::NFCErrorType::NOT_SUPPORTED)); |
- } |
m_requests.clear(); |
} |
void NFC::OnWatch(const WTF::Vector<uint32_t>& ids, |
- device::nfc::mojom::blink::NFCMessagePtr) { |
- // TODO(shalamov): Not implemented. |
+ device::nfc::mojom::blink::NFCMessagePtr message) { |
+ for (const auto& id : ids) { |
+ auto it = m_callbacks.find(id); |
+ if (it != m_callbacks.end()) { |
+ MessageCallback* callback = it->value; |
+ ScriptState* scriptState = callback->getScriptState(); |
+ DCHECK(scriptState); |
+ ScriptState::Scope scope(scriptState); |
+ NFCMessage nfcMessage = toNFCMessage(scriptState, message); |
+ callback->handleMessage(nfcMessage); |
+ } |
+ } |
+} |
+ |
+ScriptPromise NFC::rejectIfNotSupported(ScriptState* scriptState) { |
+ String errorMessage; |
+ if (!scriptState->getExecutionContext()->isSecureContext(errorMessage)) { |
+ return ScriptPromise::rejectWithDOMException( |
+ scriptState, DOMException::create(SecurityError, errorMessage)); |
+ } |
+ |
+ if (!m_nfc) { |
+ return ScriptPromise::rejectWithDOMException( |
+ scriptState, DOMException::create(NotSupportedError)); |
+ } |
+ |
+ return ScriptPromise(); |
+} |
+ |
+void NFC::OnWatchRegistered(MessageCallback* callback, |
+ ScriptPromiseResolver* resolver, |
+ uint32_t id, |
+ device::nfc::mojom::blink::NFCErrorPtr error) { |
+ m_requests.remove(resolver); |
+ |
+ // Invalid id was returned. |
+ // https://w3c.github.io/web-nfc/#dom-nfc-watch |
+ // 8. If the request fails, reject promise with "NotSupportedError" |
+ // and abort these steps. |
+ if (!id) { |
+ resolver->reject(NFCError::take( |
+ resolver, device::nfc::mojom::blink::NFCErrorType::NOT_SUPPORTED)); |
+ return; |
+ } |
+ |
+ if (error.is_null()) { |
+ m_callbacks.add(id, callback); |
+ resolver->resolve(id); |
+ } else { |
+ resolver->reject(NFCError::take(resolver, error->error_type)); |
+ } |
} |
DEFINE_TRACE(NFC) { |
PageVisibilityObserver::trace(visitor); |
ContextLifecycleObserver::trace(visitor); |
visitor->trace(m_requests); |
+ visitor->trace(m_callbacks); |
} |
} // namespace blink |