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 |