Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1432)

Unified Diff: third_party/WebKit/Source/modules/nfc/NFC.cpp

Issue 1759373003: [webnfc] Implement nfc.watch in blink nfc module. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@implement_nfc_push_in_android
Patch Set: Rebased to master and improved tests Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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
« third_party/WebKit/Source/modules/nfc/NFC.h ('K') | « third_party/WebKit/Source/modules/nfc/NFC.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698