| 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
|
|
|