| 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
|
|
|