Chromium Code Reviews| 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 ac7a95cf4a12254dd5d38df80fa35a226e8a9eff..0ac0c5255e07d020beded05f3ad587d73501e560 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(const device::nfc::mojom::blink::NFCRecordPtr& record, |
| + ScriptState* scriptState) { |
| + 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()); |
| + } |
| + |
| + if (record->record_type == |
| + device::nfc::mojom::blink::NFCRecordType::JSON) { |
| + return v8::JSON::Parse(scriptState->isolate(), |
| + v8String(scriptState->isolate(), stringData)) |
| + .ToLocalChecked(); |
| + } |
| + |
| + return v8String(scriptState->isolate(), stringData); |
| + } |
| + |
| + 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(const device::nfc::mojom::blink::NFCRecordPtr& record, |
| + ScriptState* scriptState) { |
| + NFCRecord nfcRecord; |
| + nfcRecord.setMediaType(record->media_type); |
| + nfcRecord.setRecordType(toNFCRecordType(record->record_type)); |
| + nfcRecord.setData(ScriptValue(scriptState, toV8(record, scriptState))); |
|
haraken
2016/11/22 02:38:14
Would you help me understand why you need to pass
shalamov
2016/11/22 12:50:41
This is a helper function that converts from mojom
|
| + return nfcRecord; |
| +} |
| + |
| +NFCMessage toNFCMessage(const device::nfc::mojom::blink::NFCMessagePtr& message, |
| + ScriptState* scriptState) { |
| + NFCMessage nfcMessage; |
| + nfcMessage.setURL(message->url); |
| + blink::HeapVector<NFCRecord> records; |
| + for (size_t i = 0; i < message->data.size(); ++i) |
| + records.append(toNFCRecord(message->data[i], scriptState)); |
| + nfcMessage.setData(records); |
| + return nfcMessage; |
| +} |
| + |
| +size_t getNFCMessageSize( |
| + const device::nfc::mojom::blink::NFCMessagePtr& message) { |
| + size_t messageSize = message->url.charactersSizeInBytes(); |
| + for (size_t i = 0; i < message->data.size(); ++i) { |
| + messageSize += message->data[i]->media_type.charactersSizeInBytes(); |
| + messageSize += message->data[i]->data.size(); |
| + } |
| + return messageSize; |
| +} |
| + |
| } // anonymous namespace |
| NFC::NFC(LocalFrame* frame) |
| @@ -457,25 +589,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) |
| @@ -488,6 +617,12 @@ ScriptPromise NFC::push(ScriptState* scriptState, |
| return ScriptPromise::rejectWithDOMException( |
| scriptState, DOMException::create(SyntaxError)); |
| + if (getNFCMessageSize(message) > |
| + device::nfc::mojom::blink::NFCMessage::kMaxSize) { |
| + return ScriptPromise::rejectWithDOMException( |
| + scriptState, DOMException::create(NotSupportedError)); |
| + } |
| + |
| ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); |
| m_requests.add(resolver); |
| auto callback = convertToBaseCallback(WTF::bind(&NFC::OnRequestCompleted, |
| @@ -500,15 +635,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); |
| @@ -520,24 +651,60 @@ 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; |
| + |
| + 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() { |
| @@ -572,26 +739,75 @@ 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) { |
| + ScriptState* scriptState = |
| + ScriptState::forMainWorld(toLocalFrame(page()->mainFrame())); |
|
haraken
2016/11/22 02:38:14
What happens if OnWatch is called on an isolated w
shalamov
2016/11/22 12:50:41
Unfortunately OnWatch() is a part of mojo client i
haraken
2016/11/22 14:39:09
If an extension uses the nfc object, we need to us
shalamov
2016/11/23 12:10:45
Done.
|
| + ScriptState::Scope scope(scriptState); |
| + NFCMessage nfcMessage = toNFCMessage(message, scriptState); |
| + |
| + for (const auto& id : ids) { |
| + auto it = m_callbacks.find(id); |
| + if (it != m_callbacks.end()) |
| + it->value->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 |