Commit 581b273e authored by Leon Han's avatar Leon Han Committed by Commit Bot

[webnfc] Refactor/Refine impl for "Creating Web NFC message" algorithm

This CL implements strictly the "Creating Web NFC message" algorithm
described at http://w3c.github.io/web-nfc/#creating-web-nfc-message.

1.
Changes the Web IDL definition for NDEFRecordData from
"typedef (DOMString or unrestricted double or ArrayBuffer or Dictionary) NDEFRecordData;"
to
"typedef any NDEFRecordData;"

2.
Adds verification of NDEFRecordInit when creating NDEFRecord, i.e. makes
NDEFRecord ctor throw exceptions in case of invalid NDEFRecordInit.
Also, the same thing for NDEFMessage and NFCReadingEvent.

3.
Changes code flow for Step 10.9 of "9.10.1 The push() method" described
at https://w3c.github.io/web-nfc/#the-push-method.
"
Let output be the notation for the NDEF message to be created by UA, as
the result of passing message to create Web NFC message. If this throws
an exception, reject p with that exception and abort these steps.
"
Previously,
  NDEFMessageSource (NDEFMessageInit) -Validity check->
  -Mojo TypeConverter-> Mojom NDEFMessagePtr
Now,
  Remove "Validity check" and "Mojo TypeConverter" mentioned above,
  instead, we reuse the process of creating NDEFMessage from
  NDEFMessageSource (NDEFMessageInit), then the next step converting
  NDEFMessage to Mojom NDEFMessagePtr is quite an easy job.

BUG=520391

Change-Id: I628981e556f89ffdd0f883da0cfa99b20a6f8300
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1767719
Commit-Queue: Leon Han <leon.han@intel.com>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarReilly Grant <reillyg@chromium.org>
Reviewed-by: default avatarRijubrata Bhaumik <rijubrata.bhaumik@intel.com>
Cr-Commit-Position: refs/heads/master@{#693539}
parent 972da046
......@@ -8,7 +8,6 @@ import android.net.Uri;
import android.os.Build;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.Log;
import org.chromium.device.mojom.NdefMessage;
import org.chromium.device.mojom.NdefRecord;
import org.chromium.device.mojom.NdefRecordType;
......@@ -29,8 +28,6 @@ public final class NdefMessageUtils {
private static final String TEXT_MIME = "text/plain";
private static final String JSON_MIME = "application/json";
private static final String OCTET_STREAM_MIME = "application/octet-stream";
private static final String CHARSET_UTF8 = ";charset=UTF-8";
private static final String CHARSET_UTF16 = ";charset=UTF-16";
/**
* Converts mojo NdefMessage to android.nfc.NdefMessage
......@@ -85,37 +82,21 @@ public final class NdefMessageUtils {
return webNdefMessage;
}
/**
* Returns charset of mojo NdefRecord. Only applicable for URL and TEXT records.
* If charset cannot be determined, UTF-8 charset is used by default.
*/
private static String getCharset(NdefRecord record) {
if (record.mediaType.endsWith(CHARSET_UTF8)) return "UTF-8";
// When 16bit WTF::String data is converted to bytearray, it is in LE byte order, without
// BOM. By default, Android interprets UTF-16 charset without BOM as UTF-16BE, thus, use
// UTF-16LE as encoding for text data.
if (record.mediaType.endsWith(CHARSET_UTF16)) return "UTF-16LE";
Log.w(TAG, "Unknown charset, defaulting to UTF-8.");
return "UTF-8";
}
/**
* Converts mojo NdefRecord to android.nfc.NdefRecord
* |record.data| should always be treated as "UTF-8" encoding bytes, this is guaranteed by the
* sender (Blink).
*/
private static android.nfc.NdefRecord toNdefRecord(NdefRecord record)
throws InvalidNdefMessageException, IllegalArgumentException,
UnsupportedEncodingException {
switch (record.recordType) {
case NdefRecordType.URL:
return android.nfc.NdefRecord.createUri(
new String(record.data, getCharset(record)));
return android.nfc.NdefRecord.createUri(new String(record.data, "UTF-8"));
case NdefRecordType.TEXT:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return android.nfc.NdefRecord.createTextRecord(
"en-US", new String(record.data, getCharset(record)));
"en-US", new String(record.data, "UTF-8"));
} else {
return android.nfc.NdefRecord.createMime(TEXT_MIME, record.data);
}
......
......@@ -5,23 +5,57 @@
#include "third_party/blink/renderer/modules/nfc/ndef_message.h"
#include "services/device/public/mojom/nfc.mojom-blink.h"
#include "third_party/blink/renderer/bindings/modules/v8/string_or_array_buffer_or_ndef_message_init.h"
#include "third_party/blink/renderer/modules/nfc/ndef_message_init.h"
#include "third_party/blink/renderer/modules/nfc/ndef_record.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
namespace blink {
// static
NDEFMessage* NDEFMessage::Create(const NDEFMessageInit* init) {
return MakeGarbageCollected<NDEFMessage>(init);
NDEFMessage* NDEFMessage::Create(const NDEFMessageInit* init,
ExceptionState& exception_state) {
NDEFMessage* message = MakeGarbageCollected<NDEFMessage>();
message->url_ = init->url();
if (init->hasRecords()) {
for (const NDEFRecordInit* record_init : init->records()) {
NDEFRecord* record = NDEFRecord::Create(record_init, exception_state);
if (exception_state.HadException())
return nullptr;
DCHECK(record);
message->records_.push_back(record);
}
}
return message;
}
NDEFMessage::NDEFMessage(const NDEFMessageInit* init) : url_(init->url()) {
if (init->hasRecords()) {
for (const NDEFRecordInit* record_init : init->records())
records_.push_back(NDEFRecord::Create(record_init));
// static
NDEFMessage* NDEFMessage::Create(const NDEFMessageSource& source,
ExceptionState& exception_state) {
if (source.IsString()) {
NDEFMessage* message = MakeGarbageCollected<NDEFMessage>();
message->records_.push_back(
MakeGarbageCollected<NDEFRecord>(source.GetAsString()));
return message;
}
if (source.IsArrayBuffer()) {
NDEFMessage* message = MakeGarbageCollected<NDEFMessage>();
message->records_.push_back(
MakeGarbageCollected<NDEFRecord>(source.GetAsArrayBuffer()));
return message;
}
if (source.IsNDEFMessageInit()) {
return Create(source.GetAsNDEFMessageInit(), exception_state);
}
NOTREACHED();
return nullptr;
}
NDEFMessage::NDEFMessage() = default;
NDEFMessage::NDEFMessage(const device::mojom::blink::NDEFMessage& message)
: url_(message.url) {
for (wtf_size_t i = 0; i < message.data.size(); ++i) {
......
......@@ -13,17 +13,22 @@
namespace blink {
class ExceptionState;
class NDEFMessageInit;
class NDEFRecord;
class StringOrArrayBufferOrNDEFMessageInit;
using NDEFMessageSource = StringOrArrayBufferOrNDEFMessageInit;
class MODULES_EXPORT NDEFMessage final : public ScriptWrappable {
DEFINE_WRAPPERTYPEINFO();
public:
static NDEFMessage* Create(const NDEFMessageInit*);
static NDEFMessage* Create(const NDEFMessageInit*, ExceptionState&);
static NDEFMessage* Create(const NDEFMessageSource&, ExceptionState&);
NDEFMessage(const NDEFMessageInit*);
NDEFMessage(const device::mojom::blink::NDEFMessage&);
NDEFMessage();
explicit NDEFMessage(const device::mojom::blink::NDEFMessage&);
const String& url() const;
const HeapVector<Member<NDEFRecord>>& records() const;
......
......@@ -8,6 +8,7 @@
RuntimeEnabled=WebNFC,
SecureContext,
Constructor(NDEFMessageInit messageInit),
RaisesException=Constructor,
Exposed=Window
] interface NDEFMessage {
readonly attribute USVString url;
......
......@@ -5,12 +5,14 @@
#include "third_party/blink/renderer/modules/nfc/ndef_record.h"
#include "services/device/public/mojom/nfc.mojom-blink.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_array_buffer.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/bindings/modules/v8/string_or_unrestricted_double_or_array_buffer_or_dictionary.h"
#include "third_party/blink/renderer/modules/nfc/ndef_record_init.h"
#include "third_party/blink/renderer/modules/nfc/nfc_utils.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
#include "third_party/blink/renderer/platform/network/http_parsers.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
namespace blink {
......@@ -24,43 +26,177 @@ WTF::Vector<uint8_t> GetUTF8DataFromString(const String& string) {
return data;
}
} // namespace
static NDEFRecord* CreateTextRecord(const String& media_type,
const ScriptValue& data,
ExceptionState& exception_state) {
// https://w3c.github.io/web-nfc/#mapping-string-to-ndef
if (data.IsEmpty() || !data.V8Value()->IsString()) {
exception_state.ThrowTypeError(kNfcTextRecordTypeError);
return nullptr;
}
// static
NDEFRecord* NDEFRecord::Create(const NDEFRecordInit* init) {
// TODO(https://crbug.com/520391):
// - Modify IDL definition of NDEFRecordData to be "typedef any
// NDEFRecordData;" as described at
// http://w3c.github.io/web-nfc/#dom-ndefrecorddata.
// - Check validity of |init|, like whether the record type matches the
// provided NDEFRecordData as described at
// http://w3c.github.io/web-nfc/#creating-web-nfc-message, if not, return a
// nullptr.
return MakeGarbageCollected<NDEFRecord>(init);
// ExtractMIMETypeFromMediaType() ignores parameters of the MIME type.
String mime_type = ExtractMIMETypeFromMediaType(AtomicString(media_type));
// TODO(https://crbug.com/520391): Step 2-5, parse a MIME type on |media_type|
// to get 'lang' and 'charset' parameters. Now we ignore them and the embedder
// always uses "lang=en-US;charset=UTF-8" when pushing the record to a NFC
// tag.
if (mime_type.IsEmpty()) {
mime_type = kNfcPlainTextMimeType;
} else if (!mime_type.StartsWithIgnoringASCIICase(kNfcPlainTextMimePrefix)) {
exception_state.ThrowDOMException(DOMExceptionCode::kSyntaxError,
kNfcTextRecordMediaTypeError);
return nullptr;
}
String text = ToCoreString(data.V8Value().As<v8::String>());
return MakeGarbageCollected<NDEFRecord>("text", mime_type,
GetUTF8DataFromString(text));
}
static NDEFRecord* CreateUrlRecord(const String& media_type,
const ScriptValue& data,
ExceptionState& exception_state) {
// https://w3c.github.io/web-nfc/#mapping-url-to-ndef
if (data.IsEmpty() || !data.V8Value()->IsString()) {
exception_state.ThrowTypeError(kNfcUrlRecordTypeError);
return nullptr;
}
// No need to check mediaType according to the spec.
String url = ToCoreString(data.V8Value().As<v8::String>());
if (!KURL(NullURL(), url).IsValid()) {
exception_state.ThrowDOMException(DOMExceptionCode::kSyntaxError,
kNfcUrlRecordParseError);
return nullptr;
}
return MakeGarbageCollected<NDEFRecord>("url", media_type,
GetUTF8DataFromString(url));
}
static NDEFRecord* CreateJsonRecord(const String& media_type,
const ScriptValue& data,
ExceptionState& exception_state) {
// https://w3c.github.io/web-nfc/#mapping-json-to-ndef
if (data.IsEmpty()) {
exception_state.ThrowTypeError(kNfcJsonRecordNoDataError);
return nullptr;
}
// ExtractMIMETypeFromMediaType() ignores parameters of the MIME type.
String mime_type = ExtractMIMETypeFromMediaType(AtomicString(media_type));
if (mime_type.IsEmpty()) {
mime_type = kNfcJsonMimeType;
} else if (mime_type != kNfcJsonMimeType &&
mime_type != kNfcJsonTextMimeType &&
!mime_type.EndsWithIgnoringASCIICase(kNfcJsonMimePostfix)) {
// According to https://mimesniff.spec.whatwg.org/#json-mime-type, a JSON
// MIME type is any MIME type whose subtype ends in "+json" or whose
// essence is "application/json" or "text/json".
exception_state.ThrowDOMException(DOMExceptionCode::kSyntaxError,
kNfcJsonRecordMediaTypeError);
return nullptr;
}
// Serialize JSON to bytes, rethrow any exceptions.
v8::Local<v8::String> jsonString;
v8::TryCatch try_catch(data.GetIsolate());
if (!v8::JSON::Stringify(data.GetIsolate()->GetCurrentContext(),
data.V8Value())
.ToLocal(&jsonString)) {
DCHECK(try_catch.HasCaught());
exception_state.RethrowV8Exception(try_catch.Exception());
return nullptr;
}
return MakeGarbageCollected<NDEFRecord>(
"json", mime_type,
GetUTF8DataFromString(
ToBlinkString<String>(jsonString, kDoNotExternalize)));
}
NDEFRecord::NDEFRecord(const NDEFRecordInit* init)
: record_type_(init->recordType()), media_type_(init->mediaType()) {
if (init->data().IsArrayBuffer()) {
DOMArrayBuffer* buffer = init->data().GetAsArrayBuffer();
data_.Append(static_cast<uint8_t*>(buffer->Data()), buffer->ByteLength());
} else if (init->data().IsDictionary()) {
Dictionary dictionary = init->data().GetAsDictionary();
v8::Local<v8::String> jsonString;
v8::Isolate* isolate = dictionary.GetIsolate();
v8::TryCatch try_catch(isolate);
if (v8::JSON::Stringify(dictionary.V8Context(), dictionary.V8Value())
.ToLocal(&jsonString) &&
!try_catch.HasCaught()) {
data_ = GetUTF8DataFromString(
ToBlinkString<String>(jsonString, kDoNotExternalize));
static NDEFRecord* CreateOpaqueRecord(const String& media_type,
const ScriptValue& data,
ExceptionState& exception_state) {
// https://w3c.github.io/web-nfc/#mapping-binary-data-to-ndef
if (data.IsEmpty() || !data.V8Value()->IsArrayBuffer()) {
exception_state.ThrowTypeError(kNfcOpaqueRecordTypeError);
return nullptr;
}
// ExtractMIMETypeFromMediaType() ignores parameters of the MIME type.
String mime_type = ExtractMIMETypeFromMediaType(AtomicString(media_type));
if (mime_type.IsEmpty()) {
mime_type = kNfcOpaqueMimeType;
}
DOMArrayBuffer* array_buffer =
V8ArrayBuffer::ToImpl(data.V8Value().As<v8::Object>());
WTF::Vector<uint8_t> bytes;
bytes.Append(static_cast<uint8_t*>(array_buffer->Data()),
array_buffer->ByteLength());
return MakeGarbageCollected<NDEFRecord>("opaque", mime_type,
std::move(bytes));
}
} // namespace
// static
NDEFRecord* NDEFRecord::Create(const NDEFRecordInit* init,
ExceptionState& exception_state) {
// https://w3c.github.io/web-nfc/#creating-web-nfc-message
String record_type;
if (!init->hasRecordType()) {
if (!init->hasData()) {
exception_state.ThrowTypeError("The record has neither type nor data.");
return nullptr;
}
v8::Local<v8::Value> data = init->data().V8Value();
if (data->IsString()) {
record_type = "text";
} else if (data->IsArrayBuffer()) {
record_type = "opaque";
} else {
record_type = "json";
}
} else if (init->data().IsString()) {
data_ = GetUTF8DataFromString(init->data().GetAsString());
} else if (init->data().IsUnrestrictedDouble()) {
data_ = GetUTF8DataFromString(
String::Number(init->data().GetAsUnrestrictedDouble()));
} else {
record_type = init->recordType();
}
if (record_type == "empty") {
// https://w3c.github.io/web-nfc/#mapping-empty-record-to-ndef
// If record type is "empty", no need to set media type and data.
return MakeGarbageCollected<NDEFRecord>(record_type, String(),
WTF::Vector<uint8_t>());
} else if (record_type == "text") {
return CreateTextRecord(init->mediaType(), init->data(), exception_state);
} else if (record_type == "url") {
return CreateUrlRecord(init->mediaType(), init->data(), exception_state);
} else if (record_type == "json") {
return CreateJsonRecord(init->mediaType(), init->data(), exception_state);
} else if (record_type == "opaque") {
return CreateOpaqueRecord(init->mediaType(), init->data(), exception_state);
}
NOTREACHED();
return nullptr;
}
NDEFRecord::NDEFRecord(const String& record_type,
const String& media_type,
WTF::Vector<uint8_t> data)
: record_type_(record_type),
media_type_(media_type),
data_(std::move(data)) {}
NDEFRecord::NDEFRecord(const String& text)
: record_type_("text"),
media_type_(StringView(kNfcPlainTextMimeType) + kNfcCharSetUTF8),
data_(GetUTF8DataFromString(text)) {}
NDEFRecord::NDEFRecord(DOMArrayBuffer* array_buffer)
: record_type_("opaque"), media_type_(kNfcOpaqueMimeType) {
data_.Append(static_cast<uint8_t*>(array_buffer->Data()),
array_buffer->ByteLength());
}
NDEFRecord::NDEFRecord(const device::mojom::blink::NDEFRecordPtr& record)
......@@ -77,9 +213,6 @@ const String& NDEFRecord::mediaType() const {
}
String NDEFRecord::toText() const {
if (record_type_.IsEmpty())
return String();
device::mojom::blink::NDEFRecordType type =
StringToNDEFRecordType(record_type_);
if (type == device::mojom::blink::NDEFRecordType::EMPTY)
......@@ -92,9 +225,6 @@ String NDEFRecord::toText() const {
}
DOMArrayBuffer* NDEFRecord::toArrayBuffer() const {
if (record_type_.IsEmpty())
return nullptr;
device::mojom::blink::NDEFRecordType type =
StringToNDEFRecordType(record_type_);
if (type != device::mojom::blink::NDEFRecordType::JSON &&
......@@ -107,9 +237,6 @@ DOMArrayBuffer* NDEFRecord::toArrayBuffer() const {
ScriptValue NDEFRecord::toJSON(ScriptState* script_state,
ExceptionState& exception_state) const {
if (record_type_.IsEmpty())
return ScriptValue::CreateNull(script_state);
device::mojom::blink::NDEFRecordType type =
StringToNDEFRecordType(record_type_);
if (type != device::mojom::blink::NDEFRecordType::JSON &&
......@@ -127,6 +254,10 @@ ScriptValue NDEFRecord::toJSON(ScriptState* script_state,
return ScriptValue(script_state, json_object);
}
const WTF::Vector<uint8_t>& NDEFRecord::data() const {
return data_;
}
void NDEFRecord::Trace(blink::Visitor* visitor) {
ScriptWrappable::Trace(visitor);
}
......
......@@ -24,22 +24,32 @@ class MODULES_EXPORT NDEFRecord final : public ScriptWrappable {
DEFINE_WRAPPERTYPEINFO();
public:
static NDEFRecord* Create(const NDEFRecordInit*);
static NDEFRecord* Create(const NDEFRecordInit*, ExceptionState&);
NDEFRecord(const NDEFRecordInit*);
NDEFRecord(const device::mojom::blink::NDEFRecordPtr&);
// Construct a "text" record from a string.
explicit NDEFRecord(const String&);
// Construct a "opaque" record from an array buffer.
explicit NDEFRecord(DOMArrayBuffer*);
NDEFRecord(const String&, const String&, WTF::Vector<uint8_t>);
explicit NDEFRecord(const device::mojom::blink::NDEFRecordPtr&);
const String& recordType() const;
const String& mediaType() const;
String toText() const;
DOMArrayBuffer* toArrayBuffer() const;
ScriptValue toJSON(ScriptState*, ExceptionState& exception_state) const;
ScriptValue toJSON(ScriptState*, ExceptionState&) const;
const WTF::Vector<uint8_t>& data() const;
void Trace(blink::Visitor*) override;
private:
String record_type_;
String media_type_;
// Holds the NDEFRecord.[[PayloadData]] bytes defined at
// https://w3c.github.io/web-nfc/#the-ndefrecord-interface.
WTF::Vector<uint8_t> data_;
};
......
......@@ -8,6 +8,7 @@
RuntimeEnabled=WebNFC,
SecureContext,
Constructor(NDEFRecordInit recordInit),
RaisesException=Constructor,
Exposed=Window
] interface NDEFRecord {
readonly attribute NDEFRecordType recordType;
......
......@@ -6,7 +6,7 @@
enum NDEFRecordType { "empty", "text", "url", "json", "opaque" };
typedef (DOMString or unrestricted double or ArrayBuffer or Dictionary) NDEFRecordData;
typedef any NDEFRecordData;
dictionary NDEFRecordInit {
NDEFRecordType recordType;
......
......@@ -9,6 +9,7 @@ namespace blink {
const char kNfcJsonMimePostfix[] = "+json";
const char kNfcJsonMimePrefix[] = "application/";
const char kNfcJsonMimeType[] = "application/json";
const char kNfcJsonTextMimeType[] = "text/json";
const char kNfcOpaqueMimeType[] = "application/octet-stream";
const char kNfcPlainTextMimeType[] = "text/plain";
const char kNfcPlainTextMimePrefix[] = "text/";
......@@ -21,15 +22,17 @@ const char kNfcNotSupported[] =
"No NFC adapter or cannot establish connection.";
const char kNfcNotReadable[] = "NFC is not enabled.";
const char kNfcTextRecordTypeError[] =
"The data for 'text' NDEFRecords must be of String or UnrestrctedDouble.";
"The data for 'text' NDEFRecords must be of String.";
const char kNfcSetIdError[] = "Cannot set WebNFC Id.";
const char kNfcTextRecordMediaTypeError[] =
"Invalid media type for 'text' record.";
const char kNfcUrlRecordTypeError[] =
"The data for 'url' NDEFRecord must be of String type.";
const char kNfcUrlRecordParseError[] = "Cannot parse data for 'url' record.";
const char kNfcJsonRecordTypeError[] =
"The data for 'json' NDEFRecord must be of Object type.";
const char kNfcJsonRecordNoDataError[] =
"The data for 'json' NDEFRecord is missing.";
const char kNfcJsonRecordStringifyError[] =
"Cannot stringify data for 'json' record.";
const char kNfcJsonRecordMediaTypeError[] =
"Invalid media type for 'json' record.";
const char kNfcOpaqueRecordTypeError[] =
......
......@@ -5,18 +5,13 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_NFC_NFC_CONSTANTS_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_NFC_NFC_CONSTANTS_H_
#include "third_party/blink/renderer/bindings/modules/v8/string_or_array_buffer_or_ndef_message_init.h"
#include "third_party/blink/renderer/bindings/modules/v8/string_or_unrestricted_double_or_array_buffer_or_dictionary.h"
namespace blink {
using NDEFMessageSource = blink::StringOrArrayBufferOrNDEFMessageInit;
using NDEFRecordData =
blink::StringOrUnrestrictedDoubleOrArrayBufferOrDictionary;
extern const char kNfcJsonMimePostfix[];
extern const char kNfcJsonMimePrefix[];
extern const char kNfcJsonMimeType[];
extern const char kNfcJsonTextMimeType[];
extern const char kNfcOpaqueMimeType[];
extern const char kNfcPlainTextMimeType[];
extern const char kNfcPlainTextMimePrefix[];
......@@ -32,7 +27,8 @@ extern const char kNfcSetIdError[];
extern const char kNfcTextRecordMediaTypeError[];
extern const char kNfcUrlRecordTypeError[];
extern const char kNfcUrlRecordParseError[];
extern const char kNfcJsonRecordTypeError[];
extern const char kNfcJsonRecordNoDataError[];
extern const char kNfcJsonRecordStringifyError[];
extern const char kNfcJsonRecordMediaTypeError[];
extern const char kNfcOpaqueRecordTypeError[];
extern const char kNfcRecordTypeError[];
......
......@@ -6,14 +6,27 @@
#include "third_party/blink/renderer/modules/nfc/ndef_message.h"
#include "third_party/blink/renderer/modules/nfc/nfc_reading_event_init.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
namespace blink {
// static
NFCReadingEvent* NFCReadingEvent::Create(const AtomicString& event_type,
const NFCReadingEventInit* init,
ExceptionState& exception_state) {
NDEFMessage* message = NDEFMessage::Create(init->message(), exception_state);
if (exception_state.HadException())
return nullptr;
DCHECK(message);
return MakeGarbageCollected<NFCReadingEvent>(event_type, init, message);
}
NFCReadingEvent::NFCReadingEvent(const AtomicString& event_type,
const NFCReadingEventInit* initializer)
: Event(event_type, initializer),
serial_number_(initializer->serialNumber()),
message_(NDEFMessage::Create(initializer->message())) {}
const NFCReadingEventInit* init,
NDEFMessage* message)
: Event(event_type, init),
serial_number_(init->serialNumber()),
message_(message) {}
NFCReadingEvent::NFCReadingEvent(const AtomicString& event_type,
const String& serial_number,
......
......@@ -11,6 +11,7 @@
namespace blink {
class ExceptionState;
class NDEFMessage;
class NFCReadingEventInit;
......@@ -18,12 +19,13 @@ class NFCReadingEvent final : public Event {
DEFINE_WRAPPERTYPEINFO();
public:
static NFCReadingEvent* Create(const AtomicString& event_type,
const NFCReadingEventInit* initializer) {
return MakeGarbageCollected<NFCReadingEvent>(event_type, initializer);
}
static NFCReadingEvent* Create(const AtomicString&,
const NFCReadingEventInit*,
ExceptionState&);
NFCReadingEvent(const AtomicString&, const NFCReadingEventInit*);
NFCReadingEvent(const AtomicString&,
const NFCReadingEventInit*,
NDEFMessage*);
NFCReadingEvent(const AtomicString&, const String&, NDEFMessage*);
~NFCReadingEvent() override;
......
......@@ -9,6 +9,7 @@
RuntimeEnabled=WebNFC,
SecureContext,
Constructor(DOMString type, NFCReadingEventInit eventInitDict),
RaisesException=Constructor,
Exposed=Window
] interface NFCReadingEvent : Event {
readonly attribute DOMString serialNumber;
......
......@@ -8,213 +8,45 @@
#include <utility>
#include "services/device/public/mojom/nfc.mojom-blink.h"
#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
#include "third_party/blink/renderer/modules/nfc/ndef_message.h"
#include "third_party/blink/renderer/modules/nfc/ndef_record.h"
#include "third_party/blink/renderer/modules/nfc/nfc_push_options.h"
#include "third_party/blink/renderer/modules/nfc/nfc_scan_options.h"
#include "third_party/blink/renderer/modules/nfc/nfc_utils.h"
#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
using device::mojom::blink::NDEFCompatibility;
using device::mojom::blink::NDEFMessage;
using device::mojom::blink::NDEFMessagePtr;
using device::mojom::blink::NDEFRecord;
using device::mojom::blink::NDEFRecordPtr;
using device::mojom::blink::NDEFRecordType;
using device::mojom::blink::NDEFRecordTypeFilter;
using device::mojom::blink::NFCPushOptions;
using device::mojom::blink::NFCPushOptionsPtr;
using device::mojom::blink::NFCPushTarget;
using device::mojom::blink::NFCScanOptions;
using device::mojom::blink::NFCScanOptionsPtr;
using WTF::String;
namespace {
void SetMediaType(NDEFRecordPtr& recordPtr,
const String& recordMediaType,
const String& defaultMediaType) {
recordPtr->media_type =
recordMediaType.IsEmpty() ? defaultMediaType : recordMediaType;
}
} // anonymous namespace
// Mojo type converters
namespace mojo {
Vector<uint8_t> TypeConverter<Vector<uint8_t>, String>::Convert(
const String& string) {
StringUTF8Adaptor utf8_string(string);
Vector<uint8_t> array;
array.Append(utf8_string.data(), utf8_string.size());
return array;
}
Vector<uint8_t> TypeConverter<Vector<uint8_t>, blink::DOMArrayBuffer*>::Convert(
blink::DOMArrayBuffer* buffer) {
Vector<uint8_t> array;
array.Append(static_cast<uint8_t*>(buffer->Data()), buffer->ByteLength());
return array;
}
NDEFRecordPtr TypeConverter<NDEFRecordPtr, String>::Convert(
const String& string) {
NDEFRecordPtr record = NDEFRecord::New();
record->record_type = NDEFRecordType::TEXT;
record->media_type =
StringView(blink::kNfcPlainTextMimeType) + blink::kNfcCharSetUTF8;
record->data = mojo::ConvertTo<Vector<uint8_t>>(string);
return record;
}
NDEFRecordPtr TypeConverter<NDEFRecordPtr, blink::DOMArrayBuffer*>::Convert(
blink::DOMArrayBuffer* buffer) {
NDEFRecordPtr record = NDEFRecord::New();
record->record_type = NDEFRecordType::OPAQUE_RECORD;
record->media_type = blink::kNfcOpaqueMimeType;
record->data = mojo::ConvertTo<Vector<uint8_t>>(buffer);
return record;
}
NDEFMessagePtr TypeConverter<NDEFMessagePtr, String>::Convert(
const String& string) {
NDEFMessagePtr message = NDEFMessage::New();
message->data.push_back(NDEFRecord::From(string));
return message;
}
base::Optional<Vector<uint8_t>>
TypeConverter<base::Optional<Vector<uint8_t>>, blink::NDEFRecordData>::Convert(
const blink::NDEFRecordData& value) {
if (value.IsUnrestrictedDouble()) {
return mojo::ConvertTo<Vector<uint8_t>>(
String::Number(value.GetAsUnrestrictedDouble()));
}
if (value.IsString()) {
return mojo::ConvertTo<Vector<uint8_t>>(value.GetAsString());
}
// TODO(https://crbug.com/520391): Remove this duplicate code for stringifying
// json, by reusing the same code in NDEFRecord ctor. i.e. Make users of this
// type converter:
// 1. first construct a NDEFRecord from the given NDEFRecordInit, the
// NDEFRecord ctor would stringify json.
// 2. then convert the NDEFRecord into an mojom::NDEFRecordPtr, which should
// be just simple.
if (value.IsDictionary()) {
v8::Local<v8::String> jsonString;
blink::Dictionary dictionary = value.GetAsDictionary();
v8::Isolate* isolate = dictionary.GetIsolate();
v8::TryCatch try_catch(isolate);
// https://w3c.github.io/web-nfc/#mapping-json-to-ndef
// If serialization throws, reject promise with a "SyntaxError" exception.
if (!v8::JSON::Stringify(dictionary.V8Context(), dictionary.V8Value())
.ToLocal(&jsonString) ||
try_catch.HasCaught()) {
return base::nullopt;
}
String string =
blink::ToBlinkString<String>(jsonString, blink::kDoNotExternalize);
return mojo::ConvertTo<Vector<uint8_t>>(string);
}
if (value.IsArrayBuffer()) {
return mojo::ConvertTo<Vector<uint8_t>>(value.GetAsArrayBuffer());
}
return base::nullopt;
NDEFRecordPtr TypeConverter<NDEFRecordPtr, ::blink::NDEFRecord*>::Convert(
const ::blink::NDEFRecord* record) {
return NDEFRecord::New(blink::StringToNDEFRecordType(record->recordType()),
record->mediaType(), record->data());
}
NDEFRecordPtr TypeConverter<NDEFRecordPtr, blink::NDEFRecordInit*>::Convert(
const blink::NDEFRecordInit* record) {
NDEFRecordPtr recordPtr = NDEFRecord::New();
if (record->hasRecordType()) {
recordPtr->record_type =
blink::StringToNDEFRecordType(record->recordType());
} else {
recordPtr->record_type = blink::DeduceRecordTypeFromDataType(record);
}
// If record type is "empty", no need to set media type or data.
// https://w3c.github.io/web-nfc/#creating-web-nfc-message
if (recordPtr->record_type == NDEFRecordType::EMPTY)
return recordPtr;
switch (recordPtr->record_type) {
case NDEFRecordType::TEXT:
case NDEFRecordType::URL:
SetMediaType(recordPtr, record->mediaType(),
blink::kNfcPlainTextMimeType);
recordPtr->media_type = recordPtr->media_type + blink::kNfcCharSetUTF8;
break;
case NDEFRecordType::JSON:
SetMediaType(recordPtr, record->mediaType(), blink::kNfcJsonMimeType);
break;
case NDEFRecordType::OPAQUE_RECORD:
SetMediaType(recordPtr, record->mediaType(), blink::kNfcOpaqueMimeType);
break;
default:
NOTREACHED();
break;
}
auto recordData =
mojo::ConvertTo<base::Optional<Vector<uint8_t>>>(record->data());
// If JS object cannot be converted to uint8_t array, return null,
// interrupt NDEFMessage conversion algorithm and reject promise with
// SyntaxError exception.
if (!recordData)
return nullptr;
recordPtr->data = recordData.value();
return recordPtr;
}
NDEFMessagePtr TypeConverter<NDEFMessagePtr, blink::NDEFMessageInit*>::Convert(
const blink::NDEFMessageInit* message) {
NDEFMessagePtr TypeConverter<NDEFMessagePtr, ::blink::NDEFMessage*>::Convert(
const ::blink::NDEFMessage* message) {
NDEFMessagePtr messagePtr = NDEFMessage::New();
messagePtr->url = message->url();
messagePtr->data.resize(message->records().size());
for (wtf_size_t i = 0; i < message->records().size(); ++i) {
NDEFRecordPtr record = NDEFRecord::From(message->records()[i].Get());
if (record.is_null())
return nullptr;
DCHECK(record);
messagePtr->data[i] = std::move(record);
}
return messagePtr;
}
NDEFMessagePtr TypeConverter<NDEFMessagePtr, blink::DOMArrayBuffer*>::Convert(
blink::DOMArrayBuffer* buffer) {
NDEFMessagePtr message = NDEFMessage::New();
message->data.push_back(NDEFRecord::From(buffer));
return message;
}
NDEFMessagePtr TypeConverter<NDEFMessagePtr, blink::NDEFMessageSource>::Convert(
const blink::NDEFMessageSource& message) {
if (message.IsString())
return NDEFMessage::From(message.GetAsString());
if (message.IsNDEFMessageInit())
return NDEFMessage::From(message.GetAsNDEFMessageInit());
if (message.IsArrayBuffer())
return NDEFMessage::From(message.GetAsArrayBuffer());
NOTREACHED();
return nullptr;
}
NFCPushOptionsPtr
TypeConverter<NFCPushOptionsPtr, const blink::NFCPushOptions*>::Convert(
const blink::NFCPushOptions* pushOptions) {
......
......@@ -7,103 +7,46 @@
#include "mojo/public/cpp/bindings/type_converter.h"
#include "services/device/public/mojom/nfc.mojom-blink-forward.h"
#include "third_party/blink/renderer/modules/nfc/nfc_constants.h"
#include "third_party/blink/renderer/platform/wtf/forward.h"
namespace blink {
class DOMArrayBuffer;
class NDEFRecordInit;
class NDEFMessageInit;
class NDEFRecord;
class NDEFMessage;
class NFCScanOptions;
class NFCPushOptions;
} // namespace blink
// Mojo type converters
namespace mojo {
template <>
struct TypeConverter<Vector<uint8_t>, WTF::String> {
static Vector<uint8_t> Convert(const WTF::String& string);
};
template <>
struct TypeConverter<Vector<uint8_t>, blink::DOMArrayBuffer*> {
static Vector<uint8_t> Convert(blink::DOMArrayBuffer* buffer);
};
template <>
struct TypeConverter<device::mojom::blink::NDEFRecordPtr, WTF::String> {
static device::mojom::blink::NDEFRecordPtr Convert(const WTF::String& string);
};
template <>
struct TypeConverter<device::mojom::blink::NDEFRecordPtr,
blink::DOMArrayBuffer*> {
::blink::NDEFRecord*> {
static device::mojom::blink::NDEFRecordPtr Convert(
blink::DOMArrayBuffer* buffer);
};
template <>
struct TypeConverter<device::mojom::blink::NDEFMessagePtr, WTF::String> {
static device::mojom::blink::NDEFMessagePtr Convert(
const WTF::String& string);
};
template <>
struct TypeConverter<base::Optional<Vector<uint8_t>>, blink::NDEFRecordData> {
static base::Optional<Vector<uint8_t>> Convert(
const blink::NDEFRecordData& value);
};
template <>
struct TypeConverter<device::mojom::blink::NDEFRecordPtr,
blink::NDEFRecordInit*> {
static device::mojom::blink::NDEFRecordPtr Convert(
const blink::NDEFRecordInit* record);
};
template <>
struct TypeConverter<device::mojom::blink::NDEFMessagePtr,
blink::NDEFMessageInit*> {
static device::mojom::blink::NDEFMessagePtr Convert(
const blink::NDEFMessageInit* message);
};
template <>
struct TypeConverter<device::mojom::blink::NDEFMessagePtr,
blink::DOMArrayBuffer*> {
static device::mojom::blink::NDEFMessagePtr Convert(
blink::DOMArrayBuffer* buffer);
const ::blink::NDEFRecord* record);
};
// TODO(https://crbug.com/520391): Add a creator function like
// static NDEFMessage* NDEFMessage::Create(const NDEFMessageSource&);
// which constructs NDEFMessage with
// - validity check of the input NDEFMessageSource, and
// - data serialization of each record (duplicated with code of this type
// converter),
// after that we can convert the NDEFMessage easily to a
// device::mojom::blink::NDEFMessagePtr. This enables us to remove this type
// converter and a bunch of sub converters it depends on.
template <>
struct TypeConverter<device::mojom::blink::NDEFMessagePtr,
blink::NDEFMessageSource> {
::blink::NDEFMessage*> {
static device::mojom::blink::NDEFMessagePtr Convert(
const blink::NDEFMessageSource& message);
const ::blink::NDEFMessage* message);
};
template <>
struct TypeConverter<device::mojom::blink::NFCPushOptionsPtr,
const blink::NFCPushOptions*> {
const ::blink::NFCPushOptions*> {
static device::mojom::blink::NFCPushOptionsPtr Convert(
const blink::NFCPushOptions* pushOptions);
const ::blink::NFCPushOptions* pushOptions);
};
template <>
struct TypeConverter<device::mojom::blink::NFCScanOptionsPtr,
const blink::NFCScanOptions*> {
const ::blink::NFCScanOptions*> {
static device::mojom::blink::NFCScanOptionsPtr Convert(
const blink::NFCScanOptions* scanOptions);
const ::blink::NFCScanOptions* scanOptions);
};
} // namespace mojo
......
......@@ -20,193 +20,6 @@ using device::mojom::blink::NFCPushTarget;
namespace blink {
ScriptPromise RejectIfInvalidTextRecord(ScriptState* script_state,
const NDEFRecordInit* record) {
const NDEFRecordData& value = record->data();
if (!value.IsString() && !(value.IsUnrestrictedDouble() &&
!std::isnan(value.GetAsUnrestrictedDouble()))) {
return ScriptPromise::Reject(
script_state, V8ThrowException::CreateTypeError(
script_state->GetIsolate(), kNfcTextRecordTypeError));
}
if (record->hasMediaType() &&
!record->mediaType().StartsWithIgnoringASCIICase(
kNfcPlainTextMimePrefix)) {
return ScriptPromise::RejectWithDOMException(
script_state,
MakeGarbageCollected<DOMException>(DOMExceptionCode::kSyntaxError,
kNfcTextRecordMediaTypeError));
}
return ScriptPromise();
}
ScriptPromise RejectIfInvalidURLRecord(ScriptState* script_state,
const NDEFRecordInit* record) {
if (!record->data().IsString()) {
return ScriptPromise::Reject(
script_state, V8ThrowException::CreateTypeError(
script_state->GetIsolate(), kNfcUrlRecordTypeError));
}
if (!KURL(NullURL(), record->data().GetAsString()).IsValid()) {
return ScriptPromise::RejectWithDOMException(
script_state,
MakeGarbageCollected<DOMException>(DOMExceptionCode::kSyntaxError,
kNfcUrlRecordParseError));
}
return ScriptPromise();
}
ScriptPromise RejectIfInvalidJSONRecord(ScriptState* script_state,
const NDEFRecordInit* record) {
if (!record->data().IsDictionary()) {
return ScriptPromise::Reject(
script_state, V8ThrowException::CreateTypeError(
script_state->GetIsolate(), kNfcJsonRecordTypeError));
}
// If JSON record has media type, it must be equal to "application/json" or
// start with "application/" and end with "+json".
if (record->hasMediaType() &&
(record->mediaType() != kNfcJsonMimeType &&
!(record->mediaType().StartsWithIgnoringASCIICase(kNfcJsonMimePrefix) &&
record->mediaType().EndsWithIgnoringASCIICase(kNfcJsonMimePostfix)))) {
return ScriptPromise::RejectWithDOMException(
script_state,
MakeGarbageCollected<DOMException>(DOMExceptionCode::kSyntaxError,
kNfcJsonRecordMediaTypeError));
}
return ScriptPromise();
}
ScriptPromise RejectIfInvalidOpaqueRecord(ScriptState* script_state,
const NDEFRecordInit* record) {
if (!record->data().IsArrayBuffer()) {
return ScriptPromise::Reject(
script_state,
V8ThrowException::CreateTypeError(script_state->GetIsolate(),
kNfcOpaqueRecordTypeError));
}
return ScriptPromise();
}
ScriptPromise RejectIfInvalidNDEFRecord(ScriptState* script_state,
const NDEFRecordInit* record) {
NDEFRecordType type;
if (record->hasRecordType()) {
type = StringToNDEFRecordType(record->recordType());
} else {
type = DeduceRecordTypeFromDataType(record);
// https://w3c.github.io/web-nfc/#creating-web-nfc-message
// If NDEFRecord.recordType is not set and record type cannot be deduced
// from NDEFRecord.data, reject promise with TypeError.
if (type == NDEFRecordType::EMPTY) {
return ScriptPromise::Reject(
script_state, V8ThrowException::CreateTypeError(
script_state->GetIsolate(), kNfcRecordTypeError));
}
}
// Non-empty records must have data.
if (!record->hasData() && (type != NDEFRecordType::EMPTY)) {
return ScriptPromise::Reject(
script_state, V8ThrowException::CreateTypeError(
script_state->GetIsolate(), kNfcRecordDataError));
}
switch (type) {
case NDEFRecordType::TEXT:
return RejectIfInvalidTextRecord(script_state, record);
case NDEFRecordType::URL:
return RejectIfInvalidURLRecord(script_state, record);
case NDEFRecordType::JSON:
return RejectIfInvalidJSONRecord(script_state, record);
case NDEFRecordType::OPAQUE_RECORD:
return RejectIfInvalidOpaqueRecord(script_state, record);
case NDEFRecordType::EMPTY:
return ScriptPromise();
}
NOTREACHED();
return ScriptPromise::Reject(
script_state, V8ThrowException::CreateTypeError(
script_state->GetIsolate(), kNfcRecordError));
}
ScriptPromise RejectIfInvalidNDEFRecordArray(
ScriptState* script_state,
const HeapVector<Member<NDEFRecordInit>>& records) {
for (const auto& record : records) {
ScriptPromise isValidRecord =
RejectIfInvalidNDEFRecord(script_state, record);
if (!isValidRecord.IsEmpty())
return isValidRecord;
}
return ScriptPromise();
}
ScriptPromise RejectIfInvalidNDEFMessageSource(
ScriptState* script_state,
const NDEFMessageSource& push_message) {
// If NDEFMessageSource of invalid type, reject promise with TypeError
if (!push_message.IsNDEFMessageInit() && !push_message.IsString() &&
!push_message.IsArrayBuffer()) {
return ScriptPromise::Reject(
script_state, V8ThrowException::CreateTypeError(
script_state->GetIsolate(), kNfcMsgTypeError));
}
if (push_message.IsNDEFMessageInit()) {
// https://w3c.github.io/web-nfc/#the-push-method
// If NDEFMessage.records is empty, reject promise with TypeError
const NDEFMessageInit* message = push_message.GetAsNDEFMessageInit();
if (!message->hasRecords() || message->records().IsEmpty()) {
return ScriptPromise::Reject(
script_state, V8ThrowException::CreateTypeError(
script_state->GetIsolate(), kNfcEmptyMsg));
}
return RejectIfInvalidNDEFRecordArray(script_state, message->records());
}
return ScriptPromise();
}
// https://w3c.github.io/web-nfc/#creating-web-nfc-message Step 2.1
// If NDEFRecord type is not provided, deduce NDEFRecord type from JS data type:
// String or Number => 'text' record
// ArrayBuffer => 'opaque' record
// Dictionary, JSON serializable Object => 'json' record
NDEFRecordType DeduceRecordTypeFromDataType(
const blink::NDEFRecordInit* record) {
if (record->hasData()) {
const blink::NDEFRecordData& value = record->data();
if (value.IsString() || (value.IsUnrestrictedDouble() &&
!std::isnan(value.GetAsUnrestrictedDouble()))) {
return NDEFRecordType::TEXT;
}
if (value.IsDictionary()) {
return NDEFRecordType::JSON;
}
if (value.IsArrayBuffer()) {
return NDEFRecordType::OPAQUE_RECORD;
}
}
return NDEFRecordType::EMPTY;
}
size_t GetNDEFMessageSize(const device::mojom::blink::NDEFMessagePtr& message) {
size_t message_size = message->url.CharactersSizeInBytes();
for (wtf_size_t i = 0; i < message->data.size(); ++i) {
......
......@@ -16,32 +16,6 @@ namespace blink {
class DOMException;
ScriptPromise RejectIfInvalidTextRecord(ScriptState* script_state,
const NDEFRecordInit* record);
ScriptPromise RejectIfInvalidURLRecord(ScriptState* script_state,
const NDEFRecordInit* record);
ScriptPromise RejectIfInvalidJSONRecord(ScriptState* script_state,
const NDEFRecordInit* record);
ScriptPromise RejectIfInvalidOpaqueRecord(ScriptState* script_state,
const NDEFRecordInit* record);
ScriptPromise RejectIfInvalidNDEFRecord(ScriptState* script_state,
const NDEFRecordInit* record);
ScriptPromise RejectIfInvalidNDEFRecordArray(
ScriptState* script_state,
const HeapVector<Member<NDEFRecordInit>>& records);
ScriptPromise RejectIfInvalidNDEFMessageSource(
ScriptState* script_state,
const NDEFMessageSource& push_message);
device::mojom::blink::NDEFRecordType DeduceRecordTypeFromDataType(
const blink::NDEFRecordInit* record);
size_t GetNDEFMessageSize(const device::mojom::blink::NDEFMessagePtr& message);
bool SetNDEFMessageURL(const String& origin,
......
......@@ -8,6 +8,7 @@
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
#include "third_party/blink/renderer/bindings/modules/v8/string_or_array_buffer_or_ndef_message_init.h"
#include "third_party/blink/renderer/core/dom/abort_signal.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/modules/nfc/nfc_push_options.h"
......@@ -35,7 +36,8 @@ void NFCWriter::Trace(blink::Visitor* visitor) {
// https://w3c.github.io/web-nfc/#the-push-method
ScriptPromise NFCWriter::push(ScriptState* script_state,
const NDEFMessageSource& push_message,
const NFCPushOptions* options) {
const NFCPushOptions* options,
ExceptionState& exception_state) {
ExecutionContext* execution_context = GetExecutionContext();
// https://w3c.github.io/web-nfc/#security-policies
// WebNFC API must be only accessible from top level browsing context.
......@@ -54,11 +56,6 @@ ScriptPromise NFCWriter::push(ScriptState* script_state,
DOMExceptionCode::kAbortError, kNfcCancelled));
}
ScriptPromise is_valid_message =
RejectIfInvalidNDEFMessageSource(script_state, push_message);
if (!is_valid_message.IsEmpty())
return is_valid_message;
// 9. If timeout value is NaN or negative, reject promise with "TypeError"
// and abort these steps.
if (options->hasTimeout() &&
......@@ -68,13 +65,24 @@ ScriptPromise NFCWriter::push(ScriptState* script_state,
script_state->GetIsolate(), kNfcInvalidPushTimeout));
}
auto message = device::mojom::blink::NDEFMessage::From(push_message);
if (!message) {
return ScriptPromise::RejectWithDOMException(
script_state, MakeGarbageCollected<DOMException>(
DOMExceptionCode::kSyntaxError, kNfcMsgConvertError));
// Step 10.8: Run "create Web NFC message", if this throws an exception,
// reject p with that exception and abort these steps.
NDEFMessage* ndef_message =
NDEFMessage::Create(push_message, exception_state);
if (exception_state.HadException()) {
return ScriptPromise();
}
// If NDEFMessage.records is empty, reject promise with TypeError
if (ndef_message->records().size() == 0) {
return ScriptPromise::Reject(script_state,
V8ThrowException::CreateTypeError(
script_state->GetIsolate(), kNfcEmptyMsg));
}
auto message = device::mojom::blink::NDEFMessage::From(ndef_message);
DCHECK(message);
if (!SetNDEFMessageURL(execution_context->GetSecurityOrigin()->ToString(),
message)) {
return ScriptPromise::RejectWithDOMException(
......
......@@ -15,8 +15,12 @@
namespace blink {
class NFCPushOptions;
class ExceptionState;
class ExecutionContext;
class ScriptPromise;
class StringOrArrayBufferOrNDEFMessageInit;
using NDEFMessageSource = StringOrArrayBufferOrNDEFMessageInit;
class NFCWriter : public ScriptWrappable, public ContextClient {
DEFINE_WRAPPERTYPEINFO();
......@@ -33,7 +37,8 @@ class NFCWriter : public ScriptWrappable, public ContextClient {
// Pushes NDEFMessageSource asynchronously to NFC tag / peer.
ScriptPromise push(ScriptState*,
const NDEFMessageSource&,
const NFCPushOptions*);
const NFCPushOptions*,
ExceptionState&);
// Called by NFCProxy for notification about connection error.
void OnMojoConnectionError();
......
......@@ -15,5 +15,5 @@ typedef (DOMString or ArrayBuffer or NDEFMessageInit) NDEFMessageSource;
ConstructorCallWith=ExecutionContext,
Exposed=Window
] interface NFCWriter {
[CallWith=ScriptState] Promise<void> push (NDEFMessageSource message, optional NFCPushOptions options);
[CallWith=ScriptState, RaisesException] Promise<void> push (NDEFMessageSource message, optional NFCPushOptions options);
};
......@@ -12,12 +12,8 @@
}, 'NDEFRecord constructor without init dict');
test(() => {
const record = new NDEFRecord(null);
assert_equals(record.recordType.length, 0, 'empty recordType');
assert_equals(record.mediaType.length, 0, 'empty mediaType');
assert_equals(record.toText(), null, 'toText() returns null');
assert_equals(record.toArrayBuffer(), null, 'toArrayBuffer() returns null');
assert_equals(record.toJSON(), null, 'toJSON() returns null');
assert_throws(new TypeError, () => new NDEFRecord(null),
"The record has neither type nor data.");
}, 'NDEFRecord constructor with null init dict');
test(() => {
......
......@@ -23,19 +23,15 @@ const invalid_type_messages =
// NDEFRecord must have data.
createMessage([createTextRecord()]),
// NDEFRecord.data for 'text' record must be number or string.
// NDEFRecord.data for 'text' record must be a string.
createMessage([createTextRecord(test_buffer_data)]),
createMessage([createTextRecord(test_json_data)]),
createMessage([createTextRecord(test_number_data)]),
// https://w3c.github.io/web-nfc/#dfn-map-a-json-object-to-ndef
// NDEFRecord must have data.
createMessage([createJsonRecord()]),
// NDEFRecord.data for 'json' record must be object.
createMessage([createJsonRecord(test_buffer_data)]),
createMessage([createJsonRecord(test_number_data)]),
createMessage([createJsonRecord(test_text_data)]),
// https://w3c.github.io/web-nfc/#dfn-map-a-url-to-ndef
// NDEFRecord must have data.
createMessage([createUrlRecord()]),
......@@ -59,16 +55,16 @@ const invalid_syntax_messages =
[
// NDEFRecord.mediaType for 'text' record must be 'text/*'.
createMessage([createRecord('text', 'application/json',
test_number_data)]),
test_text_data)]),
// Data for 'url' record, must be a valid URL.
createMessage([createUrlRecord('Invalid URL:// Data')]),
// NDEFRecord.mediaType for 'json' record must be 'application/json' or
// starts with 'application/' and ends with '+json'.
// A JSON MIME type is any MIME type whose subtype ends in "+json" or
// whose essence is "application/json" or "text/json".
createMessage([createRecord('json', 'image/png', test_json_data)]),
createMessage([createRecord('json', 'application/x+y', test_json_data)]),
createMessage([createRecord('json', 'custom/app+json', test_json_data)]),
createMessage([createRecord('json', 'custom/app+jsonx', test_json_data)]),
];
const invalid_signals = [
......@@ -195,8 +191,8 @@ promise_test(async t => {
const writer = new NFCWriter();
const message = createMessage([createRecord('json','application/json',
{ get x(){ return this; } })]);
await promise_rejects(t, 'SyntaxError', writer.push(message));
}, "Reject promise with SyntaxError if 'json' record cannot be serialized.");
await promise_rejects(t, new TypeError(), writer.push(message));
}, "Reject promise with exceptions thrown from serializing the 'json' record data.");
promise_test(async t => {
const writer = new NFCWriter();
......@@ -251,8 +247,8 @@ nfc_test(async (t, mockNFC) => {
const writer = new NFCWriter();
let message = createMessage([createTextRecord(test_text_data),
createJsonRecord(test_json_data),
createJsonRecord(test_number_data),
createOpaqueRecord(test_buffer_data),
createTextRecord(test_number_data),
createUrlRecord(test_url_data)],
test_message_origin);
await writer.push(message);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment