blob: 9e8790d73b5cd5254d65856d7d4edd318cb3c254 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// 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/nfc_parser_ios.h"
#include <CoreNFC/CoreNFC.h>
#include "base/apple/foundation_util.h"
#include "services/device/public/cpp/nfc/nfc_utils.h"
#include "third_party/blink/renderer/platform/wtf/text/strcat.h"
namespace blink {
namespace {
device::mojom::blink::NDEFRawMessagePtr ConvertNFCDefRawMessage(
NFCNDEFMessage* message) {
if (!message) {
return nullptr;
}
Vector<device::mojom::blink::NDEFRawRecordPtr> records;
for (NFCNDEFPayload* record : [message records]) {
device::mojom::blink::NDEFRawRecordPtr mojo_record =
device::mojom::blink::NDEFRawRecord::New();
mojo_record->type_name_format =
device::MapCoreNFCFormat([record typeNameFormat]);
auto identifier_span = base::apple::NSDataToSpan([record identifier]);
mojo_record->identifier.assign(identifier_span);
auto payload_span = base::apple::NSDataToSpan([record payload]);
mojo_record->identifier.assign(payload_span);
auto type_span = base::apple::NSDataToSpan([record type]);
mojo_record->identifier.assign(type_span);
records.push_back(std::move(mojo_record));
}
return device::mojom::blink::NDEFRawMessage::New(std::move(records));
}
device::mojom::blink::NDEFRawMessagePtr ConvertNFCDefMessage(
const Vector<uint8_t>& payload) {
return ConvertNFCDefRawMessage([NFCNDEFMessage
ndefMessageWithData:[NSData dataWithBytes:payload.data()
length:payload.size()]]);
}
device::mojom::blink::NDEFRecordPtr CreateEmptyRecord() {
auto result = device::mojom::blink::NDEFRecord::New();
result->category =
device::mojom::blink::NDEFRecordTypeCategory::kStandardized;
result->record_type = "empty";
return result;
}
device::mojom::blink::NDEFRecordPtr CreateUnknownRecord(
const Vector<uint8_t>& payload) {
auto result = device::mojom::blink::NDEFRecord::New();
result->category =
device::mojom::blink::NDEFRecordTypeCategory::kStandardized;
result->record_type = "unknown";
result->data = payload;
return result;
}
device::mojom::blink::NDEFRecordPtr CreateExternalRecord(
const String& type,
const Vector<uint8_t>& payload) {
auto result = device::mojom::blink::NDEFRecord::New();
result->category = device::mojom::blink::NDEFRecordTypeCategory::kExternal;
result->record_type = type;
result->data = payload;
result->payload_message =
blink::ParseRawNDEFMessage(ConvertNFCDefMessage(payload));
return result;
}
device::mojom::blink::NDEFRecordPtr CreateMediaRecord(
const String& type,
const Vector<uint8_t>& payload) {
auto result = device::mojom::blink::NDEFRecord::New();
result->category =
device::mojom::blink::NDEFRecordTypeCategory::kStandardized;
result->record_type = "mime";
result->media_type = type;
result->data = payload;
return result;
}
device::mojom::blink::NDEFRecordPtr CreateUrlRecord(
bool absolute,
const Vector<uint8_t>& payload) {
auto result = device::mojom::blink::NDEFRecord::New();
result->category =
device::mojom::blink::NDEFRecordTypeCategory::kStandardized;
result->record_type = absolute ? "absolute-url" : "url";
result->data = payload;
return result;
}
device::mojom::blink::NDEFRecordPtr CreateTextRecord(
const Vector<uint8_t>& payload) {
if (payload.empty()) {
return nullptr;
}
auto result = device::mojom::blink::NDEFRecord::New();
result->category =
device::mojom::blink::NDEFRecordTypeCategory::kStandardized;
result->record_type = "text";
result->encoding = (payload[0] & (1 << 7)) == 0 ? "utf-8" : "utf-16";
unsigned lang_length = payload[0] & 0x3f;
wtf_size_t text_body_start = lang_length + 1;
if (text_body_start > payload.size()) {
return nullptr;
}
base::span<const uint8_t> data_span(payload);
result->lang = String(data_span.subspan(1u, lang_length));
result->data.assign(data_span.subspan(text_body_start));
return result;
}
device::mojom::blink::NDEFRecordPtr CreateSmartPosterRecord(
const Vector<uint8_t>& payload) {
auto result = device::mojom::blink::NDEFRecord::New();
result->category =
device::mojom::blink::NDEFRecordTypeCategory::kStandardized;
result->record_type = "smart-poster";
result->data = payload;
result->payload_message =
blink::ParseRawNDEFMessage(ConvertNFCDefMessage(payload));
return result;
}
device::mojom::blink::NDEFRecordPtr CreateLocalRecord(
const String& type,
const Vector<uint8_t>& payload) {
auto result = device::mojom::blink::NDEFRecord::New();
result->category = device::mojom::blink::NDEFRecordTypeCategory::kLocal;
result->record_type = StrCat({":", type});
result->data = payload;
result->payload_message =
blink::ParseRawNDEFMessage(ConvertNFCDefMessage(payload));
return result;
}
device::mojom::blink::NDEFRecordPtr CreateWellKnownRecord(
const String& type,
const Vector<uint8_t>& payload) {
if (type == "U") {
return CreateUrlRecord(/*absolute=*/false, payload);
} else if (type == "T") {
return CreateTextRecord(payload);
} else if (type == "Sp") {
return CreateSmartPosterRecord(payload);
}
return CreateLocalRecord(type, payload);
}
} // namespace
device::mojom::blink::NDEFMessagePtr ParseRawNDEFMessage(
device::mojom::blink::NDEFRawMessagePtr message) {
if (!message) {
return nullptr;
}
Vector<device::mojom::blink::NDEFRecordPtr> records;
for (const device::mojom::blink::NDEFRawRecordPtr& record : message->data) {
device::mojom::blink::NDEFRecordPtr mojo_record;
switch (record->type_name_format) {
case device::mojom::blink::NSRawTypeNameFormat::kEmpty:
mojo_record = CreateEmptyRecord();
break;
case device::mojom::blink::NSRawTypeNameFormat::kAbsoluteURI:
mojo_record = CreateUrlRecord(/*absolute=*/true, record->payload);
break;
case device::mojom::blink::NSRawTypeNameFormat::kMedia:
mojo_record =
CreateMediaRecord(String::FromUTF8(record->type), record->payload);
break;
case device::mojom::blink::NSRawTypeNameFormat::kExternal:
mojo_record = CreateExternalRecord(String::FromUTF8(record->type),
record->payload);
break;
case device::mojom::blink::NSRawTypeNameFormat::kWellKnown:
mojo_record = CreateWellKnownRecord(String::FromUTF8(record->type),
record->payload);
break;
case device::mojom::blink::NSRawTypeNameFormat::kUnknown:
mojo_record = CreateUnknownRecord(record->payload);
break;
case device::mojom::blink::NSRawTypeNameFormat::kUnchanged:
// Android doesn't support these chunked types so allow these for now.
return nullptr;
}
if (mojo_record) {
mojo_record->id = String::FromUTF8(record->identifier);
records.push_back(std::move(mojo_record));
}
}
return device::mojom::blink::NDEFMessage::New(std::move(records));
}
} // namespace blink