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 ac7a95cf4a12254dd5d38df80fa35a226e8a9eff..0ac0c5255e07d020beded05f3ad587d73501e560 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" |
#include "public/platform/Platform.h" |
@@ -36,9 +37,13 @@ using device::nfc::mojom::blink::NFCMessagePtr; |
using device::nfc::mojom::blink::NFCRecord; |
using device::nfc::mojom::blink::NFCRecordPtr; |
using device::nfc::mojom::blink::NFCRecordType; |
+using device::nfc::mojom::blink::NFCRecordTypeFilter; |
using device::nfc::mojom::blink::NFCPushOptions; |
using device::nfc::mojom::blink::NFCPushOptionsPtr; |
using device::nfc::mojom::blink::NFCPushTarget; |
+using device::nfc::mojom::blink::NFCWatchMode; |
+using device::nfc::mojom::blink::NFCWatchOptions; |
+using device::nfc::mojom::blink::NFCWatchOptionsPtr; |
NFCPushTarget toNFCPushTarget(const WTF::String& target) { |
if (target == "tag") |
@@ -70,6 +75,17 @@ 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 |
@@ -302,6 +318,32 @@ 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; |
+ } |
+}; |
+ |
} // namespace mojo |
namespace blink { |
@@ -426,6 +468,96 @@ bool setURL(const String& origin, |
return originURL.isValid(); |
} |
+String toNFCRecordType(const device::nfc::mojom::blink::NFCRecordType& type) { |
+ switch (type) { |
+ case device::nfc::mojom::blink::NFCRecordType::TEXT: |
+ return "text"; |
+ case device::nfc::mojom::blink::NFCRecordType::URL: |
+ return "url"; |
+ case device::nfc::mojom::blink::NFCRecordType::JSON: |
+ return "json"; |
+ case device::nfc::mojom::blink::NFCRecordType::OPAQUE_RECORD: |
+ return "opaque"; |
+ case device::nfc::mojom::blink::NFCRecordType::EMPTY: |
+ return "empty"; |
+ } |
+ |
+ NOTREACHED(); |
+ return String(); |
+} |
+ |
+v8::Local<v8::Value> toV8(const device::nfc::mojom::blink::NFCRecordPtr& record, |
+ ScriptState* scriptState) { |
+ switch (record->record_type) { |
+ case device::nfc::mojom::blink::NFCRecordType::TEXT: |
+ case device::nfc::mojom::blink::NFCRecordType::URL: |
+ case device::nfc::mojom::blink::NFCRecordType::JSON: { |
+ String stringData; |
+ if (!record->data.isEmpty()) { |
+ stringData = String::fromUTF8WithLatin1Fallback( |
+ static_cast<unsigned char*>(&record->data.first()), |
+ record->data.size()); |
+ } |
+ |
+ if (record->record_type == |
+ device::nfc::mojom::blink::NFCRecordType::JSON) { |
+ return v8::JSON::Parse(scriptState->isolate(), |
+ v8String(scriptState->isolate(), stringData)) |
+ .ToLocalChecked(); |
+ } |
+ |
+ return v8String(scriptState->isolate(), stringData); |
+ } |
+ |
+ case device::nfc::mojom::blink::NFCRecordType::OPAQUE_RECORD: { |
+ if (!record->data.isEmpty()) { |
+ DOMArrayBuffer* buffer = DOMArrayBuffer::create( |
+ static_cast<void*>(&record->data.first()), record->data.size()); |
+ return toV8(buffer, scriptState->context()->Global(), |
+ scriptState->isolate()); |
+ } |
+ |
+ return v8::Null(scriptState->isolate()); |
+ } |
+ |
+ case device::nfc::mojom::blink::NFCRecordType::EMPTY: |
+ return v8::Null(scriptState->isolate()); |
+ } |
+ |
+ NOTREACHED(); |
+ return v8::Local<v8::Value>(); |
+} |
+ |
+NFCRecord toNFCRecord(const device::nfc::mojom::blink::NFCRecordPtr& record, |
+ ScriptState* scriptState) { |
+ NFCRecord nfcRecord; |
+ nfcRecord.setMediaType(record->media_type); |
+ nfcRecord.setRecordType(toNFCRecordType(record->record_type)); |
+ nfcRecord.setData(ScriptValue(scriptState, toV8(record, scriptState))); |
haraken
2016/11/22 02:38:14
Would you help me understand why you need to pass
shalamov
2016/11/22 12:50:41
This is a helper function that converts from mojom
|
+ return nfcRecord; |
+} |
+ |
+NFCMessage toNFCMessage(const device::nfc::mojom::blink::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 device::nfc::mojom::blink::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) |
@@ -457,25 +589,22 @@ void NFC::dispose() { |
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)); |
- |
device::nfc::mojom::blink::NFCMessagePtr message = |
device::nfc::mojom::blink::NFCMessage::From(pushMessage); |
if (!message) |
@@ -488,6 +617,12 @@ ScriptPromise NFC::push(ScriptState* scriptState, |
return ScriptPromise::rejectWithDOMException( |
scriptState, DOMException::create(SyntaxError)); |
+ if (getNFCMessageSize(message) > |
+ device::nfc::mojom::blink::NFCMessage::kMaxSize) { |
+ return ScriptPromise::rejectWithDOMException( |
+ scriptState, DOMException::create(NotSupportedError)); |
+ } |
+ |
ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); |
m_requests.add(resolver); |
auto callback = convertToBaseCallback(WTF::bind(&NFC::OnRequestCompleted, |
@@ -500,15 +635,11 @@ ScriptPromise NFC::push(ScriptState* scriptState, |
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); |
@@ -520,24 +651,60 @@ 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(device::nfc::mojom::blink::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() { |
@@ -572,26 +739,75 @@ 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. |
- for (ScriptPromiseResolver* resolver : m_requests) { |
+ for (ScriptPromiseResolver* resolver : m_requests) |
resolver->reject(NFCError::take( |
resolver, device::nfc::mojom::blink::NFCErrorType::NOT_SUPPORTED)); |
- } |
m_requests.clear(); |
} |
void NFC::OnWatch(const WTF::Vector<uint32_t>& ids, |
- device::nfc::mojom::blink::NFCMessagePtr) { |
- // TODO(shalamov): Not implemented. |
+ device::nfc::mojom::blink::NFCMessagePtr message) { |
+ ScriptState* scriptState = |
+ ScriptState::forMainWorld(toLocalFrame(page()->mainFrame())); |
haraken
2016/11/22 02:38:14
What happens if OnWatch is called on an isolated w
shalamov
2016/11/22 12:50:41
Unfortunately OnWatch() is a part of mojo client i
haraken
2016/11/22 14:39:09
If an extension uses the nfc object, we need to us
shalamov
2016/11/23 12:10:45
Done.
|
+ ScriptState::Scope scope(scriptState); |
+ NFCMessage nfcMessage = toNFCMessage(message, scriptState); |
+ |
+ for (const auto& id : ids) { |
+ auto it = m_callbacks.find(id); |
+ 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, |
+ device::nfc::mojom::blink::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, device::nfc::mojom::blink::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) { |
PageVisibilityObserver::trace(visitor); |
ContextLifecycleObserver::trace(visitor); |
visitor->trace(m_requests); |
+ visitor->trace(m_callbacks); |
} |
} // namespace blink |