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 bc72ad1caa4e828e900dc0fde06e049ec16b7b58..86d008f888abc13ff40e974a525ef236a6fb950b 100644 |
--- a/third_party/WebKit/Source/modules/nfc/NFC.cpp |
+++ b/third_party/WebKit/Source/modules/nfc/NFC.cpp |
@@ -14,6 +14,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/ServiceRegistry.h" |
@@ -34,9 +35,14 @@ using device::wtf::NFCMessagePtr; |
using device::wtf::NFCRecord; |
using device::wtf::NFCRecordPtr; |
using device::wtf::NFCRecordType; |
+using device::wtf::NFCRecordTypeFilter; |
+using device::wtf::NFCRecordTypeFilterPtr; |
using device::wtf::NFCPushOptions; |
using device::wtf::NFCPushOptionsPtr; |
using device::wtf::NFCPushTarget; |
+using device::wtf::NFCWatchMode; |
+using device::wtf::NFCWatchOptions; |
+using device::wtf::NFCWatchOptionsPtr; |
NFCPushTarget toNFCPushTarget(const WTF::String& target) |
{ |
@@ -70,6 +76,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; |
+ |
+ ASSERT_NOT_REACHED(); |
+ return NFCWatchMode::WEBNFC_ONLY; |
+} |
+ |
// https://w3c.github.io/web-nfc/#creating-web-nfc-message Step 2.1 |
// If NFCRecord type is not provided, deduct NFCRecord type from JS data type: |
// String or Number => 'text' record |
@@ -308,6 +326,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->mediaType = watchOptions.mediaType(); |
+ |
+ if (watchOptions.hasMode()) |
+ watchOptionsPtr->mode = toNFCWatchMode(watchOptions.mode()); |
+ else |
+ watchOptionsPtr->mode = NFCWatchMode::WEBNFC_ONLY; |
+ |
+ if (watchOptions.hasRecordType()) { |
+ watchOptionsPtr->recordFilter = NFCRecordTypeFilter::New(); |
+ watchOptionsPtr->recordFilter->recordType = toNFCRecordType(watchOptions.recordType()); |
+ } |
+ |
+ return watchOptionsPtr; |
+ } |
+}; |
+ |
} // namespace mojo |
namespace blink { |
@@ -430,23 +473,107 @@ bool setURL(const String& origin, device::wtf::NFCMessagePtr& message) |
return originURL.isValid(); |
} |
+String toNFCRecordType(const device::wtf::NFCRecordType& type) |
+{ |
+ switch (type) { |
+ case device::wtf::NFCRecordType::TEXT: |
+ return "text"; |
+ case device::wtf::NFCRecordType::URL: |
+ return "url"; |
+ case device::wtf::NFCRecordType::JSON: |
+ return "json"; |
+ case device::wtf::NFCRecordType::OPAQUE_RECORD: |
+ return "opaque"; |
+ case device::wtf::NFCRecordType::EMPTY: |
+ return "empty"; |
+ } |
+ |
+ ASSERT_NOT_REACHED(); |
+ return String(); |
+} |
+ |
+v8::Local<v8::Value> toV8(const device::wtf::NFCRecordPtr& record, ScriptState* scriptState) |
+{ |
+ switch (record->recordType) { |
+ case device::wtf::NFCRecordType::TEXT: |
+ case device::wtf::NFCRecordType::URL: |
+ case device::wtf::NFCRecordType::JSON: { |
+ String stringData; |
+ if (!record->data.empty()) |
+ stringData = String::fromUTF8WithLatin1Fallback(static_cast<unsigned char*>(&record->data.front()), record->data.size()); |
+ |
+ if (record->recordType == device::wtf::NFCRecordType::JSON) |
+ return v8::JSON::Parse(scriptState->isolate(), v8String(scriptState->isolate(), stringData)).ToLocalChecked(); |
+ return v8String(scriptState->isolate(), stringData); |
+ } |
+ |
+ case device::wtf::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 device::wtf::NFCRecordType::EMPTY: |
+ return v8::Null(scriptState->isolate()); |
+ } |
+ |
+ ASSERT_NOT_REACHED(); |
+ return v8::Local<v8::Value>(); |
+} |
+ |
+NFCRecord toNFCRecord(const device::wtf::NFCRecordPtr& record, ScriptState* scriptState) |
+{ |
+ NFCRecord nfcRecord; |
+ nfcRecord.setMediaType(record->mediaType); |
+ nfcRecord.setRecordType(toNFCRecordType(record->recordType)); |
+ nfcRecord.setData(ScriptValue(scriptState, toV8(record, scriptState))); |
+ return nfcRecord; |
+} |
+ |
+NFCMessage toNFCMessage(const device::wtf::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; |
+} |
+ |
} // anonymous namespace |
-using NFCMojoCallback = mojo::Callback<void(device::wtf::NFCErrorPtr)>; |
-class NFCCallback final : public NFCMojoCallback::Runnable { |
- WTF_MAKE_NONCOPYABLE(NFCCallback); |
+template <typename T> |
+class NFCCallbackBase { |
public: |
- using NFCCallbackPromiseAdapter = CallbackPromiseAdapter<void, NFCError>; |
- explicit NFCCallback(ScriptPromiseResolver* resolver) |
- : m_promiseAdapter(adoptPtr(new NFCCallbackPromiseAdapter(resolver))) { } |
+ explicit NFCCallbackBase(ScriptPromiseResolver* resolver) |
+ : m_promiseAdapter(adoptPtr(new T(resolver))) { } |
- ~NFCCallback() override |
+ // If NFCService is not available or disappears when NFC HW is disabled, |
+ // mojo service will invalidate proxy and destroy all pending callbacks. |
+ // Promise will be rejected with NotSupportedError exception. |
+ virtual ~NFCCallbackBase() |
{ |
- // If NFCService is not available or disappears when NFC HW is disabled, |
- // reject promise with NotSupportedError exception. |
m_promiseAdapter->onError(device::wtf::NFCErrorType::NOT_SUPPORTED); |
} |
+protected: |
+ OwnPtr<T> m_promiseAdapter; |
+}; |
+ |
+using NFCMojoCallback = mojo::Callback<void(device::wtf::NFCErrorPtr)>; |
+using NFCCallbackPromiseAdapter = CallbackPromiseAdapter<void, NFCError>; |
+ |
+class NFCCallback final : public NFCMojoCallback::Runnable, |
+ public NFCCallbackBase<NFCCallbackPromiseAdapter> { |
+ WTF_MAKE_NONCOPYABLE(NFCCallback); |
+public: |
+ explicit NFCCallback(ScriptPromiseResolver* resolver) |
+ : NFCCallbackBase<NFCCallbackPromiseAdapter>(resolver) { } |
+ |
void Run(device::wtf::NFCErrorPtr error) override |
{ |
if (error.is_null()) |
@@ -454,9 +581,33 @@ public: |
else |
m_promiseAdapter->onError(error->error_type); |
} |
+}; |
+ |
+using NFCWatchMojoCallback = mojo::Callback<void(uint32_t, device::wtf::NFCErrorPtr)>; |
+using NFCWatchCallbackPromiseAdapter = CallbackPromiseAdapter<uint32_t, NFCError>; |
+ |
+class NFCWatchCallback final : public NFCWatchMojoCallback::Runnable, |
+ public NFCCallbackBase<NFCWatchCallbackPromiseAdapter> { |
+ WTF_MAKE_NONCOPYABLE(NFCWatchCallback); |
+public: |
+ NFCWatchCallback(ScriptPromiseResolver* resolver, NFC* nfc, MessageCallback* callback) |
+ : NFCCallbackBase<NFCWatchCallbackPromiseAdapter>(resolver) |
+ , m_nfc(nfc) |
+ , m_callback(callback) { } |
+ |
+ void Run(uint32_t watchId, device::wtf::NFCErrorPtr error) override |
+ { |
+ if (error.is_null()) { |
+ m_nfc->OnWatchRegistered(watchId, m_callback); |
+ m_promiseAdapter->onSuccess(watchId); |
+ } else { |
+ m_promiseAdapter->onError(error->error_type); |
+ } |
+ } |
private: |
- OwnPtr<NFCCallbackPromiseAdapter> m_promiseAdapter; |
+ Persistent<NFC> m_nfc; |
+ Persistent<MessageCallback> m_callback; |
}; |
NFC::NFC(LocalFrame* frame) |
@@ -474,20 +625,17 @@ NFC* NFC::create(LocalFrame* frame) |
NFC::~NFC() = default; |
// 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 (!Initialize(scriptState->domWindow()->frame()->serviceRegistry())) |
- return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(NotSupportedError)); |
- |
device::wtf::NFCMessagePtr message = device::wtf::NFCMessage::From(pushMessage); |
if (!message) |
return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(SyntaxError)); |
@@ -501,15 +649,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 (!Initialize(scriptState->domWindow()->frame()->serviceRegistry())) |
- return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(NotSupportedError)); |
+ ScriptPromise promise = RejectIfNotSupported(scriptState); |
+ if (!promise.isEmpty()) |
+ return promise; |
ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); |
m_nfc->CancelPush(mojo::toNFCPushTarget(target), device::wtf::NFC::CancelPushCallback(new NFCCallback(resolver))); |
@@ -517,22 +662,49 @@ 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); |
+ device::wtf::NFC::WatchCallback watchCallback(new NFCWatchCallback(resolver, this, callback)); |
+ m_nfc->Watch(device::wtf::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_nfc->CancelWatch(id, device::wtf::NFC::CancelWatchCallback(new NFCCallback(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_nfc->CancelAllWatches(device::wtf::NFC::CancelAllWatchesCallback(new NFCCallback(resolver))); |
+ return resolver->promise(); |
} |
void NFC::pageVisibilityChanged() |
@@ -551,6 +723,12 @@ void NFC::pageVisibilityChanged() |
void NFC::OnWatch(uint32_t id, device::wtf::NFCMessagePtr message) |
{ |
+ WatchCallbacksMap::iterator it = m_callbacks.find(id); |
+ if (it != m_callbacks.end()) { |
+ ScriptState* scriptState = ScriptState::forMainWorld(toLocalFrame(page()->mainFrame())); |
+ ScriptState::Scope scope(scriptState); |
+ it->value->handleMessage(toNFCMessage(message, scriptState)); |
+ } |
} |
bool NFC::Initialize(ServiceRegistry* registry) |
@@ -570,17 +748,37 @@ bool NFC::Initialize(ServiceRegistry* registry) |
return m_nfc; |
} |
+ScriptPromise NFC::RejectIfNotSupported(ScriptState* scriptState) |
+{ |
+ String errorMessage; |
+ if (!scriptState->getExecutionContext()->isSecureContext(errorMessage)) { |
+ return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(SecurityError, errorMessage)); |
+ } |
+ |
+ if (!Initialize(scriptState->domWindow()->frame()->serviceRegistry())) |
+ return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(NotSupportedError)); |
+ |
+ return ScriptPromise(); |
+} |
+ |
void NFC::OnError() |
{ |
- // todo(shalamov): clear map that contains nfc watch callbacks |
+ m_callbacks.clear(); |
if (m_client.is_bound()) |
m_client.Close(); |
m_nfc.reset(); |
} |
+void NFC::OnWatchRegistered(uint32_t id, MessageCallback* callback) |
+{ |
+ if (id) |
+ m_callbacks.add(id, callback); |
+} |
+ |
DEFINE_TRACE(NFC) |
{ |
PageLifecycleObserver::trace(visitor); |
+ visitor->trace(m_callbacks); |
} |
} // namespace blink |