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 23c28a548ad4be449eb3557f2ce02c0c56fa6897..f6929a30e56650d67ae8c6105744e376275b2778 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" |
| @@ -37,9 +38,14 @@ using nfc::NFCMessagePtr; |
| using nfc::NFCRecord; |
| using nfc::NFCRecordPtr; |
| using nfc::NFCRecordType; |
| +using nfc::NFCRecordTypeFilter; |
| +using nfc::NFCRecordTypeFilterPtr; |
| using nfc::NFCPushOptions; |
| using nfc::NFCPushOptionsPtr; |
| using nfc::NFCPushTarget; |
| +using nfc::NFCWatchMode; |
| +using nfc::NFCWatchOptions; |
| +using nfc::NFCWatchOptionsPtr; |
| NFCPushTarget toNFCPushTarget(const WTF::String& target) |
| { |
| @@ -73,6 +79,18 @@ 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 |
| @@ -311,6 +329,31 @@ 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; |
|
dcheng
2016/09/08 04:42:38
Please use a typemap instead (see https://www.chro
shalamov
2016/09/22 13:53:22
I don't think blink generated bindings <=> mojo ty
|
| + } |
| +}; |
| + |
| } // namespace mojo |
| namespace blink { |
| @@ -435,6 +478,87 @@ bool setURL(const String& origin, nfc::NFCMessagePtr& message) |
| return originURL.isValid(); |
| } |
| +String toNFCRecordType(const nfc::NFCRecordType& type) |
| +{ |
| + switch (type) { |
| + case nfc::NFCRecordType::TEXT: |
| + return "text"; |
| + case nfc::NFCRecordType::URL: |
| + return "url"; |
| + case nfc::NFCRecordType::JSON: |
| + return "json"; |
| + case nfc::NFCRecordType::OPAQUE_RECORD: |
| + return "opaque"; |
| + case nfc::NFCRecordType::EMPTY: |
| + return "empty"; |
| + } |
| + |
| + NOTREACHED(); |
| + return String(); |
| +} |
| + |
| +v8::Local<v8::Value> toV8(const nfc::NFCRecordPtr& record, ScriptState* scriptState) |
|
dcheng
2016/09/08 04:42:38
Why do we need to do this manually? I would expect
shalamov
2016/09/22 13:53:22
NFCRecord is a kind of container Type,Mime,Data. V
|
| +{ |
| + switch (record->record_type) { |
| + case nfc::NFCRecordType::TEXT: |
| + case nfc::NFCRecordType::URL: |
| + case nfc::NFCRecordType::JSON: { |
| + String stringData; |
| + if (!record->data.empty()) |
| + stringData = String::fromUTF8WithLatin1Fallback(static_cast<unsigned char*>(&record->data.front()), record->data.size()); |
| + |
| + if (record->record_type == nfc::NFCRecordType::JSON) |
| + return v8::JSON::Parse(scriptState->isolate(), v8String(scriptState->isolate(), stringData)).ToLocalChecked(); |
| + return v8String(scriptState->isolate(), stringData); |
| + } |
| + |
| + case nfc::NFCRecordType::OPAQUE_RECORD: { |
| + if (!record->data.empty()) { |
| + DOMArrayBuffer* buffer = DOMArrayBuffer::create(static_cast<void*>(&record->data.front()), record->data.size()); |
| + return toV8(buffer, scriptState->context()->Global(), scriptState->isolate()); |
| + } |
| + |
| + return v8::Null(scriptState->isolate()); |
| + } |
| + |
| + case nfc::NFCRecordType::EMPTY: |
| + return v8::Null(scriptState->isolate()); |
| + } |
| + |
| + NOTREACHED(); |
| + return v8::Local<v8::Value>(); |
| +} |
| + |
| +NFCRecord toNFCRecord(const nfc::NFCRecordPtr& record, ScriptState* scriptState) |
| +{ |
| + NFCRecord nfcRecord; |
| + nfcRecord.setMediaType(record->media_type); |
| + nfcRecord.setRecordType(toNFCRecordType(record->record_type)); |
| + nfcRecord.setData(ScriptValue(scriptState, toV8(record, scriptState))); |
| + return nfcRecord; |
| +} |
| + |
| +NFCMessage toNFCMessage(const nfc::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 nfc::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) |
| @@ -470,22 +594,21 @@ 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)); |
| - |
| nfc::NFCMessagePtr message = nfc::NFCMessage::From(pushMessage); |
| if (!message) |
| return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(SyntaxError)); |
| @@ -493,6 +616,9 @@ ScriptPromise NFC::push(ScriptState* scriptState, const NFCPushMessage& pushMess |
| if (!setURL(scriptState->getExecutionContext()->getSecurityOrigin()->toString(), message)) |
| return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(SyntaxError)); |
| + if (getNFCMessageSize(message) > nfc::NFCMessage::kMaxNFCMessageSize) |
|
dcheng
2016/09/08 04:42:38
Do we have an equivalent check on the handling sid
shalamov
2016/09/22 13:53:22
We need two checks, one for push (write), one for
|
| + return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(NotSupportedError)); |
| + |
| ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); |
| m_requests.add(resolver); |
| auto callback = convertToBaseCallback(WTF::bind(&NFC::OnRequestCompleted, wrapPersistent(this), wrapPersistent(resolver))); |
| @@ -501,14 +627,12 @@ ScriptPromise NFC::push(ScriptState* scriptState, const NFCPushMessage& pushMess |
| 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); |
| @@ -518,22 +642,52 @@ 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(nfc::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() |
| @@ -565,6 +719,7 @@ void NFC::OnRequestCompleted(ScriptPromiseResolver* resolver, nfc::NFCErrorPtr e |
| 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. |
| @@ -574,9 +729,52 @@ void NFC::OnConnectionError() |
| m_requests.clear(); |
| } |
| -void NFC::OnWatch(mojo::WTFArray<uint32_t> ids, nfc::NFCMessagePtr) |
| +void NFC::OnWatch(mojo::WTFArray<uint32_t> ids, nfc::NFCMessagePtr message) |
| { |
| - // TODO(shalamov): Not implemented. |
| + ScriptState* scriptState = ScriptState::forMainWorld(toLocalFrame(page()->mainFrame())); |
|
dcheng
2016/09/08 04:42:38
Why does this always use the main frame? Can this
shalamov
2016/09/22 13:53:22
I added comment, according to the specification, a
|
| + ScriptState::Scope scope(scriptState); |
| + NFCMessage nfcMessage = toNFCMessage(message, scriptState); |
| + |
| + for (size_t i = 0; i < ids.size(); ++i) { |
| + WatchCallbacksMap::iterator it = m_callbacks.find(ids[i]); |
| + 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, nfc::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, nfc::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) |
| @@ -584,6 +782,7 @@ DEFINE_TRACE(NFC) |
| PageVisibilityObserver::trace(visitor); |
| ContextLifecycleObserver::trace(visitor); |
| visitor->trace(m_requests); |
| + visitor->trace(m_callbacks); |
| } |
| } // namespace blink |