blob: bceaacb1093e9545f760b10fad45a00d04598b9d [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#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_array_buffer_view_or_ndef_message_init.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_ndef_message_init.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_ndef_record_init.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.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 ExecutionContext* execution_context,
const NDEFMessageInit* init,
ExceptionState& exception_state,
bool is_embedded) {
// https://w3c.github.io/web-nfc/#creating-ndef-message
// NDEFMessageInit#records is a required field.
DCHECK(init->hasRecords());
if (init->records().IsEmpty()) {
exception_state.ThrowTypeError(
"NDEFMessageInit#records being empty makes no sense.");
return nullptr;
}
NDEFMessage* message = MakeGarbageCollected<NDEFMessage>();
for (const NDEFRecordInit* record_init : init->records()) {
NDEFRecord* record = NDEFRecord::Create(execution_context, record_init,
exception_state, is_embedded);
if (exception_state.HadException())
return nullptr;
DCHECK(record);
message->records_.push_back(record);
}
return message;
}
// static
NDEFMessage* NDEFMessage::Create(const ExecutionContext* execution_context,
const NDEFMessageSource& source,
ExceptionState& exception_state) {
// https://w3c.github.io/web-nfc/#creating-ndef-message
if (source.IsString()) {
NDEFMessage* message = MakeGarbageCollected<NDEFMessage>();
message->records_.push_back(MakeGarbageCollected<NDEFRecord>(
execution_context, source.GetAsString()));
return message;
}
if (source.IsArrayBuffer()) {
WTF::Vector<uint8_t> payload_data;
size_t byte_length = source.GetAsArrayBuffer()->ByteLength();
if (byte_length > std::numeric_limits<wtf_size_t>::max()) {
exception_state.ThrowRangeError(
"Buffer size exceeds maximum heap object size.");
return nullptr;
}
payload_data.Append(
static_cast<uint8_t*>(source.GetAsArrayBuffer()->Data()),
static_cast<wtf_size_t>(byte_length));
NDEFMessage* message = MakeGarbageCollected<NDEFMessage>();
message->records_.push_back(MakeGarbageCollected<NDEFRecord>(
String() /* id */, "application/octet-stream",
std::move(payload_data)));
return message;
}
if (source.IsArrayBufferView()) {
size_t byte_length = source.GetAsArrayBufferView()->byteLength();
if (byte_length > std::numeric_limits<wtf_size_t>::max()) {
exception_state.ThrowRangeError(
"Buffer size exceeds maximum heap object size.");
return nullptr;
}
WTF::Vector<uint8_t> payload_data;
payload_data.Append(
static_cast<uint8_t*>(source.GetAsArrayBufferView()->BaseAddress()),
static_cast<wtf_size_t>(byte_length));
NDEFMessage* message = MakeGarbageCollected<NDEFMessage>();
message->records_.push_back(MakeGarbageCollected<NDEFRecord>(
String() /* id */, "application/octet-stream",
std::move(payload_data)));
return message;
}
if (source.IsNDEFMessageInit()) {
return Create(execution_context, source.GetAsNDEFMessageInit(),
exception_state);
}
NOTREACHED();
return nullptr;
}
// static
NDEFMessage* NDEFMessage::CreateAsPayloadOfSmartPoster(
const ExecutionContext* execution_context,
const NDEFMessageInit* init,
ExceptionState& exception_state) {
// NDEFMessageInit#records is a required field.
DCHECK(init->hasRecords());
NDEFMessage* payload_message = MakeGarbageCollected<NDEFMessage>();
bool has_url_record = false;
bool has_size_record = false;
bool has_type_record = false;
bool has_action_record = false;
for (const NDEFRecordInit* record_init : init->records()) {
const String& record_type = record_init->recordType();
if (record_type == "url") {
// The single mandatory url record.
if (has_url_record) {
exception_state.ThrowTypeError(
"'smart-poster' NDEFRecord contains more than one url record.");
return nullptr;
}
has_url_record = true;
} else if (record_type == ":s") {
// Zero or one size record.
if (has_size_record) {
exception_state.ThrowTypeError(
"'smart-poster' NDEFRecord contains more than one size record.");
return nullptr;
}
has_size_record = true;
} else if (record_type == ":t") {
// Zero or one type record.
if (has_type_record) {
exception_state.ThrowTypeError(
"'smart-poster' NDEFRecord contains more than one type record.");
return nullptr;
}
has_type_record = true;
} else if (record_type == ":act") {
// Zero or one action record.
if (has_action_record) {
exception_state.ThrowTypeError(
"'smart-poster' NDEFRecord contains more than one action record.");
return nullptr;
}
has_action_record = true;
} else {
// No restriction on other record types.
}
NDEFRecord* record = NDEFRecord::Create(
execution_context, record_init, exception_state, /*is_embedded=*/true);
if (exception_state.HadException())
return nullptr;
DCHECK(record);
if (record->recordType() == ":s" && record->payloadData().size() != 4) {
exception_state.ThrowTypeError(
"Size record of smart-poster must contain a 4-byte 32 bit unsigned "
"integer.");
return nullptr;
}
if (record->recordType() == ":act" && record->payloadData().size() != 1) {
exception_state.ThrowTypeError(
"Action record of smart-poster must contain only a single byte.");
return nullptr;
}
payload_message->records_.push_back(record);
}
if (!has_url_record) {
exception_state.ThrowTypeError(
"'smart-poster' NDEFRecord is missing the single mandatory url "
"record.");
return nullptr;
}
return payload_message;
}
NDEFMessage::NDEFMessage() = default;
NDEFMessage::NDEFMessage(const device::mojom::blink::NDEFMessage& message) {
for (wtf_size_t i = 0; i < message.data.size(); ++i) {
records_.push_back(MakeGarbageCollected<NDEFRecord>(*message.data[i]));
}
}
const HeapVector<Member<NDEFRecord>>& NDEFMessage::records() const {
return records_;
}
void NDEFMessage::Trace(Visitor* visitor) const {
visitor->Trace(records_);
ScriptWrappable::Trace(visitor);
}
} // namespace blink