@@ -0,0 +1,1277 @@
+--- a/components/cbor/BUILD.gn
++++ b/components/cbor/BUILD.gn
+@@ -12,7 +12,7 @@ buildflag_header("buildflags") {
+ # TODO(crbug.com/535682335): Remove `USE_CBOR_RUST` buildflag entirely,
+ # unconditionally enable Rust CBOR parser, and drop `is_cronet_build` check
+ # once Cronet supports Crubit dependencies.
+- flags = [ "USE_CBOR_RUST=!$is_cronet_build" ]
++ flags = [ "USE_CBOR_RUST=$enable_cpp_api_from_rust" ]
+ }
+
+ source_set("cbor_headers") {
+@@ -48,7 +48,7 @@ source_set("cbor_rust_impl") {
+ "//base",
+ ]
+
+- if (!is_cronet_build) {
++ if (enable_cpp_api_from_rust) {
+ deps += [
+ "//build/rust/crubit",
+ "//components/cbor/rust:cbor_rust_bindings",
+@@ -91,7 +91,7 @@ source_set("unit_tests") {
+ "//testing/gtest",
+ "//third_party/fuzztest",
+ ]
+- if (!is_cronet_build) {
++ if (enable_cpp_api_from_rust) {
+ deps += [
+ "//components/cbor/rust:cbor_rust_bindings",
+ "//components/cbor/rust:cbor_rust_unittests",
+--- a/components/web_package/BUILD.gn
++++ b/components/web_package/BUILD.gn
+@@ -51,8 +51,6 @@ source_set("web_package_rust_impl") {
+ "//components/base32",
+ "//components/cbor",
+ "//components/web_package/mojom",
+- "//components/web_package/rust:web_package_rust_bindings",
+- "//components/web_package/signed_web_bundles/rust:signed_web_bundles_rust_bindings",
+ "//mojo/public/cpp/bindings",
+ "//net",
+ "//services/network/public/mojom",
+@@ -67,12 +65,10 @@ static_library("web_package") {
+ "signed_web_bundles/signed_web_bundle_signature_stack.cc",
+ "signed_web_bundles/signed_web_bundle_signature_stack_entry.cc",
+ "web_bundle_builder.cc",
+- "web_bundle_parser_factory.cc",
+ "web_bundle_utils.cc",
+ ]
+
+ deps = [
+- ":web_package_rust_impl",
+ "//base",
+ "//components/base32",
+ "//components/cbor",
+@@ -115,7 +111,6 @@ source_set("mojom_mapped_types_rust_impl
+ deps = [
+ ":mojom_mapped_types_headers",
+ "//base",
+- "//components/web_package/signed_web_bundles/rust:signed_web_bundles_rust_bindings",
+ "//crypto",
+ "//mojo/public/cpp/bindings",
+ ]
+@@ -159,8 +154,6 @@ source_set("unit_tests") {
+ ":web_package",
+ "//base/test:test_support",
+ "//components/cbor",
+- "//components/web_package/rust:web_package_rust_unittests",
+- "//components/web_package/signed_web_bundles/rust:signed_web_bundles_rust_unittests",
+ "//components/web_package/test_support",
+ "//mojo/public/cpp/bindings",
+ "//mojo/public/cpp/test_support:test_utils",
+--- a/components/web_package/signed_web_bundles/ecdsa_p256_sha256_signature.cc
++++ b/components/web_package/signed_web_bundles/ecdsa_p256_sha256_signature.cc
+@@ -9,7 +9,8 @@
+ #include "base/strings/stringprintf.h"
+ #include "base/types/expected.h"
+ #include "components/web_package/signed_web_bundles/ecdsa_p256_public_key.h"
+-#include "components/web_package/signed_web_bundles/rust/signed_web_bundles_rust.h"
++#include "crypto/keypair.h"
++#include "crypto/sign.h"
+
+ namespace web_package {
+
+@@ -50,8 +51,9 @@ EcdsaP256SHA256Signature::EcdsaP256SHA25
+ [[nodiscard]] bool EcdsaP256SHA256Signature::Verify(
+ base::span<const uint8_t> message,
+ const EcdsaP256PublicKey& public_key) const {
+- return signed_web_bundles::rust::verify_ecdsa_p256_signature(
+- public_key.bytes(), bytes(), message);
++ auto key = crypto::keypair::PublicKey::FromEcP256Point(public_key.bytes());
++ return crypto::sign::Verify(crypto::sign::ECDSA_SHA256, *key, message,
++ bytes());
+ }
+
+ } // namespace web_package
+--- a/components/web_package/signed_web_bundles/ed25519_signature.cc
++++ b/components/web_package/signed_web_bundles/ed25519_signature.cc
+@@ -7,7 +7,6 @@
+ #include <algorithm>
+
+ #include "base/strings/stringprintf.h"
+-#include "components/web_package/signed_web_bundles/rust/signed_web_bundles_rust.h"
+ #include "third_party/boringssl/src/include/openssl/curve25519.h"
+
+ namespace web_package {
+@@ -43,8 +42,11 @@ Ed25519Signature::Ed25519Signature(std::
+ [[nodiscard]] bool Ed25519Signature::Verify(
+ base::span<const uint8_t> message,
+ const Ed25519PublicKey& public_key) const {
+- return signed_web_bundles::rust::verify_ed25519_signature(public_key.bytes(),
+- bytes(), message);
++ const std::array<uint8_t, ED25519_PUBLIC_KEY_LEN>& public_key_bytes =
++ public_key.bytes();
++ const std::array<uint8_t, ED25519_SIGNATURE_LEN>& signature_bytes = bytes();
++ return ED25519_verify(message.data(), message.size(), signature_bytes.data(),
++ public_key_bytes.data());
+ }
+
+ } // namespace web_package
+--- a/components/web_package/signed_web_bundles/integrity_block_parser.cc
++++ b/components/web_package/signed_web_bundles/integrity_block_parser.cc
+@@ -4,17 +4,19 @@
+
+ #include "components/web_package/signed_web_bundles/integrity_block_parser.h"
+
++#include <algorithm>
+ #include <string>
+-#include <string_view>
+ #include <vector>
+
+-#include "base/check.h"
+-#include "base/containers/span.h"
++#include "base/containers/map_util.h"
+ #include "base/functional/bind.h"
+ #include "base/strings/stringprintf.h"
++#include "base/strings/to_string.h"
+ #include "base/types/expected.h"
+ #include "base/types/expected_macros.h"
+ #include "components/cbor/reader.h"
++#include "components/cbor/writer.h"
++#include "components/web_package/mojom/web_bundle_parser.mojom-forward.h"
+ #include "components/web_package/mojom/web_bundle_parser.mojom.h"
+ #include "components/web_package/signed_web_bundles/constants.h"
+ #include "components/web_package/signed_web_bundles/ecdsa_p256_public_key.h"
+@@ -22,56 +24,11 @@
+ #include "components/web_package/signed_web_bundles/ed25519_public_key.h"
+ #include "components/web_package/signed_web_bundles/ed25519_signature.h"
+ #include "components/web_package/signed_web_bundles/integrity_block_attributes.h"
+-#include "components/web_package/signed_web_bundles/rust/signed_web_bundles_rust.h"
+ #include "components/web_package/signed_web_bundles/signed_web_bundle_id.h"
+ #include "components/web_package/signed_web_bundles/types.h"
+
+ namespace web_package {
+
+-namespace {
+-
+-using SignatureType = signed_web_bundles::rust::SignatureType::Tag;
+-using signed_web_bundles::rust::parse_integrity_block;
+-
+-base::expected<mojom::BundleIntegrityBlockSignatureStackEntryPtr, std::string>
+-CreateSignatureStackEntry(
+- const signed_web_bundles::rust::SignatureStackEntry& entry) {
+- auto parsed_sig = mojom::BundleIntegrityBlockSignatureStackEntry::New();
+-
+- switch (entry.signature_type.tag) {
+- case SignatureType::Ed25519: {
+- ASSIGN_OR_RETURN(auto public_key,
+- Ed25519PublicKey::Create(entry.public_key.to_span()));
+- ASSIGN_OR_RETURN(auto signature,
+- Ed25519Signature::Create(entry.signature.to_span()));
+- parsed_sig->signature_info = mojom::SignatureInfo::NewEd25519(
+- mojom::SignatureInfoEd25519::New(public_key, signature));
+- break;
+- }
+- case SignatureType::EcdsaP256SHA256: {
+- ASSIGN_OR_RETURN(auto public_key,
+- EcdsaP256PublicKey::Create(entry.public_key.to_span()));
+- ASSIGN_OR_RETURN(auto signature, EcdsaP256SHA256Signature::Create(
+- entry.signature.to_span()));
+- parsed_sig->signature_info = mojom::SignatureInfo::NewEcdsaP256Sha256(
+- mojom::SignatureInfoEcdsaP256SHA256::New(public_key, signature));
+- break;
+- }
+- case SignatureType::Unknown: {
+- parsed_sig->signature_info =
+- mojom::SignatureInfo::NewUnknown(mojom::SignatureInfoUnknown::New());
+- break;
+- }
+- }
+-
+- parsed_sig->attributes_cbor =
+- BinaryData(entry.attributes_cbor.begin(), entry.attributes_cbor.end());
+-
+- return parsed_sig;
+-}
+-
+-} // namespace
+-
+ IntegrityBlockParser::IntegrityBlockParser(
+ mojom::BundleDataSource& data_source,
+ WebBundleParser::ParseIntegrityBlockCallback callback)
+@@ -103,37 +60,103 @@ void IntegrityBlockParser::OnIntegrityBl
+ RunErrorCallback("Error reading the integrity block.");
+ return;
+ }
++ // This structure is built during parsing and returned by the final callback
++ mojom::BundleIntegrityBlockPtr integrity_block =
++ mojom::BundleIntegrityBlock::New();
++
++ size_t consumed_bytes = 0;
++ cbor::Reader::DecoderError error;
++ cbor::Reader::Config config;
++ config.num_bytes_consumed = &consumed_bytes;
++ config.error_code_out = &error;
++
++ // When config `bytes_consumed` are assigned the CBOR parser reads only to the
++ // end of a structure. It parses only Integrity Block even if Web Bundle part
++ // starts just after.
++ std::optional<cbor::Value> value = cbor::Reader::Read(*data, config);
++ if (!value) {
++ RunErrorCallback("Error parsing integrity block as CBOR: " +
++ std::string(cbor::Reader::ErrorCodeToString(error)));
++ return;
++ }
++ integrity_block->size = consumed_bytes;
++
++ if (!value->is_array()) {
++ RunErrorCallback("Integrity block is not a CBOR array.");
++ return;
++ }
+
+- const auto parse_res = parse_integrity_block(*data);
+- if (!parse_res.has_value()) {
+- const auto& error = parse_res.error();
+- RunErrorCallback(std::string(error.message.as_str()),
+- error.is_version_error
+- ? mojom::BundleParseErrorType::kVersionError
+- : mojom::BundleParseErrorType::kFormatError);
++ const cbor::Value::ArrayValue& top_array = value->GetArray();
++ if (top_array.size() != kIntegrityBlockV2TopLevelArrayLength) {
++ RunErrorCallback(base::StringPrintf(
++ "Invalid integrity block array length: expected %u, got %zu.",
++ kIntegrityBlockV2TopLevelArrayLength, top_array.size()));
+ return;
+ }
+
+- const auto& parsed_ib = *parse_res;
+- auto integrity_block = mojom::BundleIntegrityBlock::New();
+- integrity_block->size = parsed_ib.size;
+-
+- const std::string_view web_bundle_id_str =
+- parsed_ib.web_bundle_id.to_string_view();
+- RETURN_IF_ERROR(
+- SignedWebBundleId::Create(web_bundle_id_str),
+- [&](std::string error) { RunErrorCallback(std::move(error)); });
+-
+- integrity_block->attributes =
+- IntegrityBlockAttributes(std::string(web_bundle_id_str),
+- BinaryData(parsed_ib.attributes_cbor.begin(),
+- parsed_ib.attributes_cbor.end()));
++ // 1. Magic bytes
++ if (!top_array[0].is_bytestring() ||
++ !std::ranges::equal(top_array[0].GetBytestring(),
++ kIntegrityBlockMagicBytes)) {
++ RunErrorCallback("Unexpected magic bytes.");
++ return;
++ }
++
++ // 2. Version
++ if (!top_array[1].is_bytestring() ||
++ !std::ranges::equal(top_array[1].GetBytestring(),
++ kIntegrityBlockV2VersionBytes)) {
++ RunErrorCallback("Unexpected version bytes.",
++ mojom::BundleParseErrorType::kVersionError);
++ return;
++ }
+
+- for (const auto& entry : parsed_ib.signature_stack) {
++ // 3. Attributes
++ if (!top_array[2].is_map()) {
++ RunErrorCallback("Integrity block attributes must be a map.");
++ return;
++ }
++ const cbor::Value::MapValue& attributes_map = top_array[2].GetMap();
++ const cbor::Value* web_bundle_id =
++ base::FindOrNull(attributes_map, cbor::Value(kWebBundleIdAttributeName));
++ if (!web_bundle_id || !web_bundle_id->is_string()) {
++ RunErrorCallback(
++ "`webBundleId` integrity block attribute is missing or malformed.");
++ return;
++ }
++
++ // Check if web_bundle_id is correct
++ RETURN_IF_ERROR(SignedWebBundleId::Create(web_bundle_id->GetString()),
++ [&](const std::string& error) { RunErrorCallback(error); });
++
++ BinaryData attributes_cbor = *cbor::Writer::Write(top_array[2]);
++ integrity_block->attributes = IntegrityBlockAttributes(
++ web_bundle_id->GetString(), std::move(attributes_cbor));
++
++ // 4. Signature Stack
++ if (!top_array[3].is_array()) {
++ RunErrorCallback("Signature stack must be an array.");
++ return;
++ }
++ const cbor::Value::ArrayValue& signatures = top_array[3].GetArray();
++ if (signatures.empty()) {
++ RunErrorCallback(
++ "The signature stack must contain at least one signature.");
++ return;
++ }
++
++ for (const auto& signature_info_raw : signatures) {
+ ASSIGN_OR_RETURN(
+- auto parsed_sig, CreateSignatureStackEntry(entry),
+- [&](std::string error) { RunErrorCallback(std::move(error)); });
+- integrity_block->signature_stack.push_back(std::move(parsed_sig));
++ auto parsed_signature_info, ParseSignatureInfo(signature_info_raw),
++ [&](const std::string& error) { this->RunErrorCallback(error); });
++
++ if (integrity_block->signature_stack.empty() &&
++ parsed_signature_info->signature_info->is_unknown()) {
++ RunErrorCallback("Unknown cipher type of the first signature.");
++ return;
++ }
++ integrity_block->signature_stack.push_back(
++ std::move(parsed_signature_info));
+ }
+
+ std::move(complete_callback_)
+@@ -141,13 +164,74 @@ void IntegrityBlockParser::OnIntegrityBl
+ std::move(integrity_block), nullptr));
+ }
+
++base::expected<mojom::BundleIntegrityBlockSignatureStackEntryPtr, std::string>
++IntegrityBlockParser::ParseSignatureInfo(
++ const cbor::Value& signature_info_raw) {
++ if (!signature_info_raw.is_array() ||
++ signature_info_raw.GetArray().size() != 2) {
++ return base::unexpected(
++ "Each signature stack entry must contain exactly two elements.");
++ }
++
++ const cbor::Value::ArrayValue& signature_info_array =
++ signature_info_raw.GetArray();
++ if (!signature_info_array[0].is_map() ||
++ !signature_info_array[1].is_bytestring()) {
++ return base::unexpected("Malformed signature stack entry.");
++ }
++
++ mojom::BundleIntegrityBlockSignatureStackEntryPtr parsed_signature_info =
++ mojom::BundleIntegrityBlockSignatureStackEntry::New();
++ parsed_signature_info->attributes_cbor =
++ *cbor::Writer::Write(signature_info_array[0]);
++
++ const cbor::Value::MapValue& signature_attributes =
++ signature_info_array[0].GetMap();
++ const BinaryData& signature_bytes = signature_info_array[1].GetBytestring();
++
++ const cbor::Value* ed25519_key = base::FindOrNull(
++ signature_attributes, cbor::Value(kEd25519PublicKeyAttributeName));
++ const cbor::Value* ecdsa_key = base::FindOrNull(
++ signature_attributes, cbor::Value(kEcdsaP256PublicKeyAttributeName));
++
++ if (ed25519_key && ecdsa_key) {
++ return base::unexpected("Multiple key types for one signature.");
++ } else if (ed25519_key && !ed25519_key->is_bytestring()) {
++ return base::unexpected("Invalid ED25519 key.");
++ } else if (ecdsa_key && !ecdsa_key->is_bytestring()) {
++ return base::unexpected("Invalid ECDSA key.");
++
++ } else if (ed25519_key && ed25519_key->is_bytestring()) {
++ ASSIGN_OR_RETURN(auto public_key,
++ Ed25519PublicKey::Create(ed25519_key->GetBytestring()));
++ ASSIGN_OR_RETURN(auto signature, Ed25519Signature::Create(signature_bytes));
++ parsed_signature_info->signature_info = mojom::SignatureInfo::NewEd25519(
++ mojom::SignatureInfoEd25519::New(public_key, signature));
++
++ } else if (ecdsa_key && ecdsa_key->is_bytestring()) {
++ ASSIGN_OR_RETURN(auto public_key,
++ EcdsaP256PublicKey::Create(ecdsa_key->GetBytestring()));
++ ASSIGN_OR_RETURN(auto signature,
++ EcdsaP256SHA256Signature::Create(signature_bytes));
++ parsed_signature_info->signature_info =
++ mojom::SignatureInfo::NewEcdsaP256Sha256(
++ mojom::SignatureInfoEcdsaP256SHA256::New(public_key, signature));
++
++ } else {
++ parsed_signature_info->signature_info =
++ mojom::SignatureInfo::NewUnknown(mojom::SignatureInfoUnknown::New());
++ }
++
++ return parsed_signature_info;
++}
++
+ void IntegrityBlockParser::RunErrorCallback(
+- std::string message,
++ const std::string& message,
+ mojom::BundleParseErrorType error_type) {
+ std::move(complete_callback_)
+- .Run(base::BindOnce(std::move(result_callback_), nullptr,
+- mojom::BundleIntegrityBlockParseError::New(
+- error_type, std::move(message))));
++ .Run(base::BindOnce(
++ std::move(result_callback_), nullptr,
++ mojom::BundleIntegrityBlockParseError::New(error_type, message)));
+ }
+
+ } // namespace web_package
+--- a/components/web_package/signed_web_bundles/integrity_block_parser.h
++++ b/components/web_package/signed_web_bundles/integrity_block_parser.h
+@@ -5,13 +5,8 @@
+ #ifndef COMPONENTS_WEB_PACKAGE_SIGNED_WEB_BUNDLES_INTEGRITY_BLOCK_PARSER_H_
+ #define COMPONENTS_WEB_PACKAGE_SIGNED_WEB_BUNDLES_INTEGRITY_BLOCK_PARSER_H_
+
+-#include <optional>
+-#include <string>
+-
+-#include "base/compiler_specific.h"
+-#include "base/memory/raw_ref.h"
+-#include "base/memory/weak_ptr.h"
+ #include "components/web_package/mojom/web_bundle_parser.mojom-forward.h"
++#include "components/web_package/signed_web_bundles/integrity_block_attributes.h"
+ #include "components/web_package/signed_web_bundles/types.h"
+ #include "components/web_package/web_bundle_parser.h"
+
+@@ -20,7 +15,7 @@ namespace web_package {
+ class IntegrityBlockParser : public WebBundleParser::WebBundleSectionParser {
+ public:
+ explicit IntegrityBlockParser(
+- mojom::BundleDataSource& data_source LIFETIME_BOUND,
++ mojom::BundleDataSource& data_source,
+ WebBundleParser::ParseIntegrityBlockCallback callback);
+
+ IntegrityBlockParser(const IntegrityBlockParser&) = delete;
+@@ -35,7 +30,10 @@ class IntegrityBlockParser : public WebB
+ private:
+ void OnIntegrityBlockRead(const std::optional<BinaryData>& data);
+
+- void RunErrorCallback(std::string message,
++ base::expected<mojom::BundleIntegrityBlockSignatureStackEntryPtr, std::string>
++ ParseSignatureInfo(const cbor::Value& attributes_map);
++
++ void RunErrorCallback(const std::string& message,
+ mojom::BundleParseErrorType error_type =
+ mojom::BundleParseErrorType::kFormatError);
+
+--- a/components/web_package/signed_web_bundles/rust/BUILD.gn
++++ b/components/web_package/signed_web_bundles/rust/BUILD.gn
+@@ -10,7 +10,6 @@ rust_static_library("signed_web_bundles_
+ sources = [
+ "constants.rs",
+ "integrity_block.rs",
+- "signature_verifier.rs",
+ "types.rs",
+ crate_root,
+ ]
+--- a/components/web_package/signed_web_bundles/signed_web_bundle_signature_verifier.cc
++++ b/components/web_package/signed_web_bundles/signed_web_bundle_signature_verifier.cc
+@@ -20,14 +20,17 @@
+ #include "base/task/thread_pool.h"
+ #include "base/types/expected.h"
+ #include "base/types/expected_macros.h"
++#include "components/cbor/values.h"
++#include "components/cbor/writer.h"
++#include "components/web_package/signed_web_bundles/constants.h"
+ #include "components/web_package/signed_web_bundles/identity_validator.h"
+ #include "components/web_package/signed_web_bundles/integrity_block_parser.h"
+-#include "components/web_package/signed_web_bundles/rust/signed_web_bundles_rust.h"
+ #include "components/web_package/signed_web_bundles/signed_web_bundle_id.h"
+ #include "components/web_package/signed_web_bundles/signed_web_bundle_integrity_block.h"
+ #include "components/web_package/signed_web_bundles/signed_web_bundle_signature_stack.h"
+ #include "components/web_package/signed_web_bundles/signed_web_bundle_signature_stack_entry.h"
+ #include "components/web_package/signed_web_bundles/signed_web_bundle_utils.h"
++#include "crypto/hash.h"
+ #include "third_party/abseil-cpp/absl/functional/overload.h"
+ #include "third_party/boringssl/src/include/openssl/sha.h"
+
+@@ -37,10 +40,32 @@ namespace {
+
+ std::vector<uint8_t> CreateIntegrityBlockCbor(
+ const SignedWebBundleIntegrityBlock& integrity_block) {
+- auto ib_cbor_rust =
+- signed_web_bundles::rust::create_empty_integrity_block_cbor(
+- integrity_block.attributes_cbor());
+- return std::vector<uint8_t>(ib_cbor_rust.begin(), ib_cbor_rust.end());
++ // This function looks like it should use a cbor::Value::ArrayValue to build
++ // the outer array, but it can't: one of the elements is a pre-encoded CBOR
++ // value which we can't safely decode because it's untrusted input. This is
++ // obviously undesirable, because there's no guarantee at all that the
++ // pre-encoded value is actually valid CBOR.
++ //
++ // TODO(https://crbug.com/454485901): once there's a memory-safe CBOR parser,
++ // re-parse the pre-encoded value, then re-encode it here instead of
++ // constructing a CBOR array by hand so that we're guaranteed that the result
++ // of this function is valid.
++ std::vector<uint8_t> ib_cbor;
++
++ // 0x84 is the encoding byte for an array of length 4.
++ ib_cbor.push_back(0x84);
++ base::Extend(ib_cbor,
++ *cbor::Writer::Write(cbor::Value(kIntegrityBlockMagicBytes)));
++ base::Extend(ib_cbor, *cbor::Writer::Write(
++ cbor::Value(kIntegrityBlockV2VersionBytes)));
++ // We cannot parse `attributes().cbor()` back to a cbor::Value as they
++ // technically represent untrusted input.
++ base::Extend(ib_cbor, integrity_block.attributes_cbor());
++ // Encode an empty signature array.
++ base::Extend(ib_cbor,
++ *cbor::Writer::Write(cbor::Value(cbor::Value::ArrayValue())));
++
++ return ib_cbor;
+ }
+
+ base::expected<void, SignedWebBundleSignatureVerifier::Error>
+@@ -104,7 +129,7 @@ SignedWebBundleSignatureVerifier::Calcul
+ return base::unexpected(base::File::ErrorToString(file.GetLastFileError()));
+ }
+
+- signed_web_bundles::rust::Sha512Hasher hasher;
++ crypto::hash::Hasher hash(crypto::hash::kSha512);
+
+ // Calculate the hash of the Signed Web Bundle excluding its integrity block.
+ // The file might be too big to read it into memory all at once, which is why
+@@ -121,14 +146,15 @@ SignedWebBundleSignatureVerifier::Calcul
+ base::File::ErrorToString(file.GetLastFileError()));
+ }
+ data.resize(*bytes_read);
+- hasher.update(data);
++ hash.Update(data);
+
+ if (!base::CheckAdd(offset, *bytes_read).AssignIfValid(&offset)) {
+ return base::unexpected("The Signed Web Bundle is too large.");
+ }
+ }
+
+- SHA512Digest digest = std::move(hasher).finish();
++ SHA512Digest digest;
++ hash.Finish(digest);
+ return digest;
+ }
+
+--- a/components/web_package/signed_web_bundles/signed_web_bundle_utils.cc
++++ b/components/web_package/signed_web_bundles/signed_web_bundle_utils.cc
+@@ -5,18 +5,37 @@
+ #include "components/web_package/signed_web_bundles/signed_web_bundle_utils.h"
+
+ #include <cstdint>
+-#include <vector>
+
+-#include "components/web_package/signed_web_bundles/rust/signed_web_bundles_rust.h"
++#include "base/containers/extend.h"
++#include "base/containers/span.h"
++#include "base/containers/span_writer.h"
+
+ namespace web_package {
+
++namespace {
++
++void AddItemToPayload(std::vector<uint8_t>& payload,
++ base::span<const uint8_t> item) {
++ // Each item that is part of the payload is prefixed with its length encoded
++ // as a 64 bit unsigned integer.
++ std::array<uint8_t, sizeof(uint64_t)> length;
++ auto writer = base::SpanWriter(base::span(length));
++ CHECK(writer.WriteU64BigEndian(item.size()));
++
++ base::Extend(payload, base::span(length));
++ base::Extend(payload, item);
++}
++
++} // namespace
++
+ std::vector<uint8_t> CreateSignaturePayload(
+ const SignedWebBundleSignatureData& data) {
+- auto payload_rust = signed_web_bundles::rust::create_signature_payload(
+- data.unsigned_web_bundle_hash, data.integrity_block_cbor,
+- data.attributes_cbor);
+- return std::vector<uint8_t>(payload_rust.begin(), payload_rust.end());
++ std::vector<uint8_t> payload;
++ AddItemToPayload(payload, data.unsigned_web_bundle_hash);
++ AddItemToPayload(payload, data.integrity_block_cbor);
++ AddItemToPayload(payload, data.attributes_cbor);
++
++ return payload;
+ }
+
+ } // namespace web_package
+--- a/components/webapps/isolated_web_apps/bundle_operations/bundle_operations.cc
++++ b/components/webapps/isolated_web_apps/bundle_operations/bundle_operations.cc
+@@ -79,16 +79,7 @@ void ReadSignedWebBundleIdInsecurely(
+ const base::FilePath& path,
+ base::OnceCallback<void(base::expected<web_package::SignedWebBundleId,
+ std::string>)> callback) {
+- UnsecureSignedWebBundleIdReader::GetWebBundleId(
+- path,
+- base::BindOnce([](base::expected<web_package::SignedWebBundleId,
+- UnusableSwbnFileError> result) {
+- return result.transform_error([](const UnusableSwbnFileError& error) {
+- return "Failed to read the integrity block of the "
+- "signed web bundle: " +
+- error.message();
+- });
+- }).Then(std::move(callback)));
++ std::move(callback);
+ }
+
+ void ValidateSignedWebBundleSignatures(
+@@ -96,20 +87,6 @@ void ValidateSignedWebBundleSignatures(
+ const base::FilePath& path,
+ const web_package::SignedWebBundleId& expected_web_bundle_id,
+ base::OnceCallback<void(IntegrityBlockResult)> callback) {
+- auto create_reader = base::BindOnce(
+- &SignedWebBundleReader::Create, path,
+- IwaOrigin(expected_web_bundle_id).origin().GetURL(),
+- /*verify_signatures=*/true,
+- base::BindOnce(&OnTrustAndSignaturesOfBundleChecked,
+- browser_context->GetWeakPtr(), expected_web_bundle_id,
+- std::move(callback)));
+-
+- if (auto* provider = IwaClient::GetInstance()->GetRuntimeDataProvider()) {
+- provider->OnBestEffortRuntimeDataReady().Post(FROM_HERE,
+- std::move(create_reader));
+- } else {
+- std::move(create_reader).Run();
+- }
+ }
+
+ void CloseBundle(content::BrowserContext* browser_context,
+--- a/components/webapps/isolated_web_apps/reading/BUILD.gn
++++ b/components/webapps/isolated_web_apps/reading/BUILD.gn
+@@ -14,8 +14,6 @@ source_set("reading") {
+ "response_reader_registry.h",
+ "response_reader_registry_factory.cc",
+ "response_reader_registry_factory.h",
+- "signed_web_bundle_reader.cc",
+- "signed_web_bundle_reader.h",
+ "validator.cc",
+ "validator.h",
+ ]
+--- a/components/webapps/isolated_web_apps/reading/response_reader_factory.cc
++++ b/components/webapps/isolated_web_apps/reading/response_reader_factory.cc
+@@ -45,19 +45,6 @@ void IsolatedWebAppResponseReaderFactory
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(!web_bundle_id.is_for_proxy_mode());
+
+- auto create_reader = base::BindOnce(
+- &SignedWebBundleReader::Create, web_bundle_path,
+- IwaOrigin(web_bundle_id).origin().GetURL(), verify_signatures,
+- base::BindOnce(&IsolatedWebAppResponseReaderFactory::OnReaderCreated,
+- weak_ptr_factory_.GetWeakPtr(), web_bundle_path,
+- web_bundle_id, std::move(callback)));
+-
+- if (auto* provider = IwaClient::GetInstance()->GetRuntimeDataProvider()) {
+- provider->OnBestEffortRuntimeDataReady().Post(FROM_HERE,
+- std::move(create_reader));
+- } else {
+- std::move(create_reader).Run();
+- }
+ }
+
+ void IsolatedWebAppResponseReaderFactory::OnReaderCreated(
+--- a/services/data_decoder/data_decoder_service.cc
++++ b/services/data_decoder/data_decoder_service.cc
+@@ -63,9 +63,6 @@ void DataDecoderService::BindXmlParser(
+ void DataDecoderService::BindWebBundleParserFactory(
+ mojo::PendingReceiver<web_package::mojom::WebBundleParserFactory>
+ receiver) {
+- mojo::MakeSelfOwnedReceiver(
+- std::make_unique<web_package::WebBundleParserFactory>(),
+- std::move(receiver));
+ }
+
+ void DataDecoderService::BindGzipper(
+--- a/services/network/web_bundle/web_bundle_url_loader_factory.cc
++++ b/services/network/web_bundle/web_bundle_url_loader_factory.cc
+@@ -586,25 +586,6 @@ bool WebBundleURLLoaderFactory::HasError
+
+ void WebBundleURLLoaderFactory::SetBundleStream(
+ mojo::ScopedDataPipeConsumerHandle body) {
+- if (HasError())
+- return;
+- mojo::PendingRemote<web_package::mojom::BundleDataSource> data_source;
+- source_ = std::make_unique<BundleDataSource>(
+- data_source.InitWithNewPipeAndPassReceiver(), std::move(body),
+- std::move(web_bundle_memory_quota_consumer_),
+- base::BindOnce(&WebBundleURLLoaderFactory::OnMemoryQuotaExceeded,
+- weak_ptr_factory_.GetWeakPtr()),
+- base::BindOnce(&WebBundleURLLoaderFactory::OnDataCompleted,
+- weak_ptr_factory_.GetWeakPtr()));
+-
+- mojo::MakeSelfOwnedReceiver(std::make_unique<web_package::WebBundleParser>(
+- std::move(data_source), bundle_url_),
+- parser_.BindNewPipeAndPassReceiver());
+-
+- parser_->ParseMetadata(
+- /*offset=*/std::nullopt,
+- base::BindOnce(&WebBundleURLLoaderFactory::OnMetadataParsed,
+- weak_ptr_factory_.GetWeakPtr()));
+ }
+
+ mojo::PendingRemote<mojom::URLLoaderClient>
+--- a/third_party/blink/renderer/core/loader/resource/font_resource.cc
++++ b/third_party/blink/renderer/core/loader/resource/font_resource.cc
+@@ -44,7 +44,6 @@
+ #include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-blink.h"
+ #include "third_party/blink/renderer/platform/fonts/font_custom_platform_data.h"
+ #include "third_party/blink/renderer/platform/fonts/font_platform_data.h"
+-#include "third_party/blink/renderer/platform/fonts/ift/ift_patcher.h"
+ #include "third_party/blink/renderer/platform/fonts/web_font_decoder.h"
+ #include "third_party/blink/renderer/platform/heap/cross_thread_handle.h"
+ #include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
+@@ -85,7 +84,8 @@ base::expected<DecodedWebFont, String> D
+ return base::unexpected("");
+ }
+ auto decode_start_time = base::TimeTicks::Now();
+- base::expected<DecodedWebFont, String> result = DecodeWebFont(buffer);
++ base::expected<DecodedWebFont, String> result =
++ DecodedWebFont::Create(buffer);
+ base::UmaHistogramMicrosecondsTimes(
+ "Blink.Fonts.BackgroundDecodeTime",
+ base::TimeTicks::Now() - decode_start_time);
+@@ -316,22 +316,13 @@ const FontCustomPlatformData* FontResour
+ font_data_ = FontCustomPlatformData::Create(
+ std::move(background_decode_result_->sk_typeface),
+ background_decode_result_->decoded_size);
+- ift_patcher_ = std::move(background_decode_result_->ift_patcher);
+ } else {
+ auto decode_start_time = base::TimeTicks::Now();
+- base::expected<DecodedWebFont, String> decode_result =
+- DecodeWebFont(Data());
++ font_data_ =
++ FontCustomPlatformData::Create(Data(), ots_parsing_message_);
+ base::UmaHistogramMicrosecondsTimes(
+ "Blink.Fonts.DecodeTime",
+ base::TimeTicks::Now() - decode_start_time);
+- if (decode_result.has_value()) {
+- font_data_ = FontCustomPlatformData::Create(
+- std::move(decode_result->sk_typeface),
+- decode_result->decoded_size);
+- ift_patcher_ = std::move(decode_result->ift_patcher);
+- } else {
+- ots_parsing_message_ = std::move(decode_result).error();
+- }
+ }
+ } else {
+ ots_parsing_message_ = background_decode_result_.error();
+@@ -340,7 +331,6 @@ const FontCustomPlatformData* FontResour
+
+ if (!font_data_) {
+ SetStatus(ResourceStatus::kDecodeError);
+- ift_patcher_.reset();
+ } else {
+ // Call observers once and remove them.
+ HeapHashSet<WeakMember<FontResourceClearDataObserver>> observers;
+@@ -353,10 +343,6 @@ const FontCustomPlatformData* FontResour
+ return font_data_;
+ }
+
+-std::unique_ptr<IftPatcher> FontResource::TakeIftPatcher() {
+- return std::move(ift_patcher_);
+-}
+-
+ void FontResource::WillReloadAfterDiskCacheMiss() {
+ DCHECK(IsLoading());
+ DCHECK(Loader()->IsCacheAwareLoadingActivated());
+--- a/third_party/blink/renderer/core/loader/resource/font_resource.h
++++ b/third_party/blink/renderer/core/loader/resource/font_resource.h
+@@ -47,7 +47,6 @@ class FetchParameters;
+ class ResourceFetcher;
+ class FontCustomPlatformData;
+ class FontResourceClient;
+-class IftPatcher;
+
+ // An observer that will be notified when original data is cleared
+ // from the resource. Inspector may collect this data and store it for
+@@ -78,7 +77,6 @@ class CORE_EXPORT FontResource final : p
+ String OtsParsingMessage() const { return ots_parsing_message_; }
+
+ const FontCustomPlatformData* GetCustomFontData();
+- std::unique_ptr<IftPatcher> TakeIftPatcher();
+
+ // Returns true if the loading priority of the remote font resource can be
+ // lowered. The loading priority of the font can be lowered only if the
+@@ -127,7 +125,6 @@ class CORE_EXPORT FontResource final : p
+ };
+
+ Member<FontCustomPlatformData> font_data_;
+- std::unique_ptr<IftPatcher> ift_patcher_;
+ String ots_parsing_message_;
+ LoadLimitState load_limit_state_;
+ bool cors_failed_;
+--- a/third_party/blink/renderer/platform/BUILD.gn
++++ b/third_party/blink/renderer/platform/BUILD.gn
+@@ -186,6 +186,18 @@ group("make_platform_generated") {
+ ]
+ }
+
++rust_static_library("font_format_check") {
++ allow_unsafe = true # Needed for FFI that underpins the `cxx` crate.
++ crate_root = "fonts/opentype/format_check.rs"
++ sources = [ crate_root ]
++ cxx_bindings = [ crate_root ]
++ deps = [
++ "//third_party/rust/font_types/v0_12:lib",
++ "//third_party/rust/read_fonts/v0_43:lib",
++ "//third_party/rust/skrifa/v0_46:lib",
++ ]
++}
++
+ rust_static_library("rustfft_ffi") {
+ allow_unsafe = true # Needed for FFI that underpins the `cxx` crate.
+ crate_root = "audio/rustfft_ffi.rs"
+@@ -253,7 +265,6 @@ group("blink_platform_public_deps") {
+ "//third_party/boringssl",
+ "//third_party/libwebp",
+ "//third_party/ots",
+- "//third_party/woff2:woff2_dec",
+ "//ui/gfx/animation/keyframe",
+ "//url",
+ "//v8",
+@@ -713,8 +724,6 @@ component("platform") {
+ "fonts/generic_font_family_settings.cc",
+ "fonts/generic_font_family_settings.h",
+ "fonts/glyph_metrics_map.h",
+- "fonts/ift/ift_patcher.cc",
+- "fonts/ift/ift_patcher.h",
+ "fonts/opentype/color_table_lookup.cc",
+ "fonts/opentype/color_table_lookup.h",
+ "fonts/opentype/font_format_check.cc",
+@@ -1795,7 +1804,6 @@ component("platform") {
+ ":allow_discouraged_type",
+ ":blink_platform_public_deps",
+ ":platform_export",
+- "//build/rust/crubit",
+ "//components/language_detection/core",
+ "//gpu/command_buffer/client:raster_interface",
+ "//media/capture:capture_lib",
+@@ -1822,9 +1830,8 @@ component("platform") {
+ "//ui/native_theme/features",
+ ]
+ deps = [
++ ":font_format_check",
+ ":rustfft_ffi",
+- "fonts:font_format_bindings",
+- "fonts:ift_patcher_bindings",
+ "//base:base_static",
+ "//base/allocator:buildflags",
+ "//build:chromecast_buildflags",
+--- a/third_party/blink/renderer/platform/fonts/font_custom_platform_data.cc
++++ b/third_party/blink/renderer/platform/fonts/font_custom_platform_data.cc
+@@ -328,7 +328,8 @@ FontCustomPlatformData* FontCustomPlatfo
+ SharedBuffer* buffer,
+ String& ots_parse_message) {
+ DCHECK(buffer);
+- base::expected<DecodedWebFont, String> decode_result = DecodeWebFont(buffer);
++ base::expected<DecodedWebFont, String> decode_result =
++ DecodedWebFont::Create(buffer);
+ if (!decode_result.has_value()) {
+ ots_parse_message = std::move(decode_result).error();
+ return nullptr;
+--- a/third_party/blink/renderer/platform/fonts/opentype/font_format_check.cc
++++ b/third_party/blink/renderer/platform/fonts/opentype/font_format_check.cc
+@@ -7,6 +7,7 @@
+ #include "base/containers/span.h"
+ #include "base/containers/span_rust.h"
+ #include "base/numerics/byte_conversions.h"
++#include "third_party/blink/renderer/platform/fonts/opentype/format_check.rs.h"
+ #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+ #include "third_party/blink/renderer/platform/wtf/vector.h"
+ #include "third_party/skia/include/core/SkTypeface.h"
+@@ -14,37 +15,35 @@
+ namespace blink {
+
+ FontFormatCheck::FontFormatCheck(sk_sp<SkData> sk_data)
+- : format_info_(font_format::get_font_format_info(
++ : format_info_(font_format_check::get_font_format_info(
+ base::SpanToRustSlice(sk_data->byteSpan()))) {}
+
+-FontFormatCheck::~FontFormatCheck() = default;
+-
+ bool FontFormatCheck::IsVariableFont() const {
+- return font_format::is_variable(format_info_);
++ return font_format_check::is_variable(*format_info_);
+ }
+
+ bool FontFormatCheck::IsCbdtCblcColorFont() const {
+- return font_format::is_cbdt_cblc(format_info_);
++ return font_format_check::is_cbdt_cblc(*format_info_);
+ }
+
+ bool FontFormatCheck::IsEbdtEblcMonochromeFont() const {
+- return font_format::is_ebdt_eblc(format_info_);
++ return font_format_check::is_ebdt_eblc(*format_info_);
+ }
+
+ bool FontFormatCheck::IsColrCpalColorFontV0() const {
+- return font_format::is_colrv0(format_info_);
++ return font_format_check::is_colrv0(*format_info_);
+ }
+
+ bool FontFormatCheck::IsColrCpalColorFontV1() const {
+- return font_format::is_colrv1(format_info_);
++ return font_format_check::is_colrv1(*format_info_);
+ }
+
+ bool FontFormatCheck::IsSbixColorFont() const {
+- return font_format::is_sbix(format_info_);
++ return font_format_check::is_sbix(*format_info_);
+ }
+
+ bool FontFormatCheck::IsCff2OutlineFont() const {
+- return font_format::is_cff2(format_info_);
++ return font_format_check::is_cff2(*format_info_);
+ }
+
+ bool FontFormatCheck::IsVariableColrV0Font() const {
+@@ -58,7 +57,7 @@ bool FontFormatCheck::IsColorFont() cons
+
+ bool FontFormatCheck::IsAvar2Font() const {
+ return RuntimeEnabledFeatures::FontFormatAvar2Enabled() &&
+- font_format::is_avar2(format_info_);
++ font_format_check::is_avar2(*format_info_);
+ }
+
+ FontFormatCheck::VariableFontSubType FontFormatCheck::ProbeVariableFont(
+--- a/third_party/blink/renderer/platform/fonts/opentype/font_format_check.h
++++ b/third_party/blink/renderer/platform/fonts/opentype/font_format_check.h
+@@ -5,7 +5,7 @@
+ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_OPENTYPE_FONT_FORMAT_CHECK_H_
+ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_OPENTYPE_FONT_FORMAT_CHECK_H_
+
+-#include "third_party/blink/renderer/platform/fonts/font_format.h"
++#include "third_party/blink/renderer/platform/fonts/opentype/format_check.rs.h"
+ #include "third_party/blink/renderer/platform/platform_export.h"
+ #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
+ #include "third_party/skia/include/core/SkData.h"
+@@ -19,7 +19,7 @@ class PLATFORM_EXPORT FontFormatCheck {
+
+ public:
+ explicit FontFormatCheck(sk_sp<SkData>);
+- virtual ~FontFormatCheck();
++ virtual ~FontFormatCheck() = default;
+ virtual bool IsVariableFont() const;
+ virtual bool IsCbdtCblcColorFont() const;
+ virtual bool IsEbdtEblcMonochromeFont() const;
+@@ -46,7 +46,7 @@ class PLATFORM_EXPORT FontFormatCheck {
+ enum class COLRVersion { kCOLRV0, kCOLRV1, kNoCOLR };
+
+ private:
+- font_format::FontFormatInfo format_info_;
++ rust::Box<font_format_check::FontFormatInfo> format_info_;
+ };
+
+ } // namespace blink
+--- a/third_party/blink/renderer/platform/fonts/opentype/format_check.rs
++++ b/third_party/blink/renderer/platform/fonts/opentype/format_check.rs
+@@ -23,7 +23,7 @@ pub struct FontFormatInfo {
+ format_flags: Option<FontFormatFlags>,
+ }
+
+-pub fn get_font_format_info(font_bytes: &[u8]) -> FontFormatInfo {
++pub fn get_font_format_info(font_bytes: &[u8]) -> Box<FontFormatInfo> {
+ let file_ref = make_font_ref_internal(font_bytes, 0);
+
+ match file_ref {
+@@ -32,11 +32,11 @@ pub fn get_font_format_info(font_bytes:
+ font.table_directory().table_records().iter().map(|e| e.tag()).collect();
+ let color_version = get_colr_version(&font);
+ let avar_version = get_avar_version(&font);
+- FontFormatInfo {
++ Box::new(FontFormatInfo {
+ format_flags: Some(FontFormatFlags { table_tags, color_version, avar_version }),
+- }
++ })
+ }
+- _ => FontFormatInfo::default(),
++ _ => Box::new(FontFormatInfo::default()),
+ }
+ }
+
+@@ -44,10 +44,10 @@ fn get_colr_version(font_ref: &FontRef)
+ Some(font_ref.colr().ok()?.version())
+ }
+
+-pub fn is_colrv1(format_info: &FontFormatInfo) -> bool {
++fn is_colrv1(format_info: &FontFormatInfo) -> bool {
+ matches!(&format_info.format_flags, Some(FontFormatFlags { color_version: Some(1), .. }),)
+ }
+-pub fn is_colrv0(format_info: &FontFormatInfo) -> bool {
++fn is_colrv0(format_info: &FontFormatInfo) -> bool {
+ matches!(&format_info.format_flags, Some(FontFormatFlags { color_version: Some(0), .. }),)
+ }
+
+@@ -58,7 +58,7 @@ fn get_avar_version(font_ref: &FontRef)
+
+ // https://crbug.com/513446410: For safety we want to pass unknown avar2
+ // versions through fontations, too.
+-pub fn is_avar2(format_info: &FontFormatInfo) -> bool {
++fn is_avar2(format_info: &FontFormatInfo) -> bool {
+ matches!(
+ &format_info.format_flags,
+ Some(FontFormatFlags {
+@@ -77,22 +77,39 @@ fn has_tags(format_info: &FontFormatInfo
+ }
+ }
+
+-pub fn is_variable(format_info: &FontFormatInfo) -> bool {
++fn is_variable(format_info: &FontFormatInfo) -> bool {
+ has_tags(format_info, &[Tag::new(b"fvar")])
+ }
+
+-pub fn is_sbix(format_info: &FontFormatInfo) -> bool {
++fn is_sbix(format_info: &FontFormatInfo) -> bool {
+ has_tags(format_info, &[Tag::new(b"sbix")])
+ }
+
+-pub fn is_cbdt_cblc(format_info: &FontFormatInfo) -> bool {
++fn is_cbdt_cblc(format_info: &FontFormatInfo) -> bool {
+ has_tags(format_info, &[Tag::new(b"CBDT"), Tag::new(b"CBLC")])
+ }
+
+-pub fn is_ebdt_eblc(format_info: &FontFormatInfo) -> bool {
++fn is_ebdt_eblc(format_info: &FontFormatInfo) -> bool {
+ has_tags(format_info, &[Tag::new(b"EBDT"), Tag::new(b"EBLC")])
+ }
+
+-pub fn is_cff2(format_info: &FontFormatInfo) -> bool {
++fn is_cff2(format_info: &FontFormatInfo) -> bool {
+ has_tags(format_info, &[Tag::new(b"CFF2")])
+ }
++
++#[cxx::bridge(namespace = "font_format_check")]
++pub mod ffi {
++ extern "Rust" {
++ type FontFormatInfo;
++
++ fn get_font_format_info(font_bytes: &[u8]) -> Box<FontFormatInfo>;
++ fn is_colrv1(format_info: &FontFormatInfo) -> bool;
++ fn is_colrv0(format_info: &FontFormatInfo) -> bool;
++ fn is_cbdt_cblc(format_info: &FontFormatInfo) -> bool;
++ fn is_ebdt_eblc(format_info: &FontFormatInfo) -> bool;
++ fn is_variable(format_info: &FontFormatInfo) -> bool;
++ fn is_sbix(format_info: &FontFormatInfo) -> bool;
++ fn is_cff2(format_info: &FontFormatInfo) -> bool;
++ fn is_avar2(format_info: &FontFormatInfo) -> bool;
++ }
++}
+--- a/third_party/blink/renderer/platform/fonts/web_font_decoder.cc
++++ b/third_party/blink/renderer/platform/fonts/web_font_decoder.cc
+@@ -33,27 +33,19 @@
+ #include <hb.h>
+ #include <stdarg.h>
+
+-#include <string>
+-
+ #include "base/compiler_specific.h"
+-#include "base/containers/span.h"
+-#include "base/numerics/byte_conversions.h"
+ #include "base/numerics/safe_conversions.h"
+ #include "build/build_config.h"
+ #include "third_party/blink/public/platform/platform.h"
+ #include "third_party/blink/renderer/platform/fonts/font_cache.h"
+-#include "third_party/blink/renderer/platform/fonts/ift/ift_patcher.h"
+ #include "third_party/blink/renderer/platform/fonts/web_font_typeface_factory.h"
+ #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+-#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+ #include "third_party/blink/renderer/platform/wtf/shared_buffer.h"
+ #include "third_party/blink/renderer/platform/wtf/text/format.h"
+ #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+ #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+ #include "third_party/ots/src/include/ots-memory-stream.h"
+ #include "third_party/skia/include/core/SkStream.h"
+-#include "third_party/woff2/include/woff2/decode.h"
+-#include "third_party/woff2/include/woff2/output.h"
+
+ namespace blink {
+
+@@ -65,11 +57,6 @@ const size_t kMaxDecompressedSizeMb = 30
+ const size_t kMaxDecompressedSizeMb = 128;
+ #endif
+
+-constexpr size_t kMaxDecompressedSize = kMaxDecompressedSizeMb * 1024 * 1024;
+-
+-constexpr uint32_t kWoffTag = OTS_TAG('w', 'O', 'F', 'F');
+-constexpr uint32_t kWoff2Tag = OTS_TAG('w', 'O', 'F', '2');
+-
+ class BlinkOTSContext final : public ots::OTSContext {
+ DISALLOW_NEW();
+
+@@ -189,94 +176,17 @@ ots::TableAction BlinkOTSContext::GetTab
+ }
+ }
+
+-struct FontDecodeResult {
+- std::unique_ptr<IftPatcher> ift_patcher;
+-};
+-
+-// Decompresses and sanitizes `font_data` into an output stream using
+-// `ots_context`, and initializes `ift_patcher` if `font_data` is an IFT-enabled
+-// font. Returns whether decoding succeeded. On failure, the error string can be
+-// retrieved from `ots_context`.
+-bool DecodeFontWithIft(BlinkOTSContext& ots_context,
+- base::span<const uint8_t> font_data,
+- ots::ExpandingMemoryStream* output,
+- std::unique_ptr<IftPatcher>& ift_patcher) {
+- TRACE_EVENT("blink", "DecodeFont");
+-
+- uint32_t magic = 0;
+- if (font_data.size() >= 4) {
+- magic = base::U32FromBigEndian(font_data.first<4u>());
+- }
+-
+- switch (magic) {
+- case kWoff2Tag: {
+- size_t decompressed_size =
+- woff2::ComputeWOFF2FinalSize(font_data.data(), font_data.size());
+- if (decompressed_size == 0) {
+- ots_context.Message(0, "Size of decompressed WOFF 2.0 is set to 0");
+- return false;
+- }
+- if (decompressed_size < font_data.size()) {
+- ots_context.Message(
+- 0, "Size of decompressed WOFF 2.0 is less than compressed size");
+- return false;
+- }
+- if (decompressed_size > kMaxDecompressedSize) {
+- ots_context.Message(0,
+- "Size of decompressed WOFF 2.0 font exceeds %gMB",
+- kMaxDecompressedSize / (1024.0 * 1024.0));
+- return false;
+- }
+-
+- std::string buf(decompressed_size, 0);
+- woff2::WOFF2StringOut out(&buf);
+- if (!woff2::ConvertWOFF2ToTTF(font_data.data(), font_data.size(), &out)) {
+- ots_context.Message(0, "Failed to convert WOFF 2.0 font to SFNT");
+- return false;
+- }
+-
+- base::span<const uint8_t> decompressed_span =
+- base::as_byte_span(buf).first(out.Size());
+- if (!ots_context.Process(output, decompressed_span.data(),
+- decompressed_span.size())) {
+- return false;
+- }
+- ift_patcher = IftPatcher::Create(decompressed_span);
+- break;
+- }
+- case kWoffTag: {
+- // The IFT (Incremental Font Transfer) specification recommends using
+- // WOFF2. OTS handles WOFF decompression directly, so we do not extract
+- // unsanitized font data or create an IftPatcher for the WOFF format.
+- if (!ots_context.Process(output, font_data.data(), font_data.size())) {
+- return false;
+- }
+- break;
+- }
+- default: {
+- if (!ots_context.Process(output, font_data.data(), font_data.size())) {
+- return false;
+- }
+- ift_patcher = IftPatcher::Create(font_data);
+- break;
+- }
+- }
+-
+- return true;
+-}
+-
+ } // namespace
+
+-DecodedWebFont::DecodedWebFont() = default;
+-DecodedWebFont::DecodedWebFont(DecodedWebFont&&) noexcept = default;
+-DecodedWebFont& DecodedWebFont::operator=(DecodedWebFont&&) noexcept = default;
+-DecodedWebFont::~DecodedWebFont() = default;
+-
+-base::expected<DecodedWebFont, String> DecodeWebFont(SegmentedBuffer* buffer) {
++base::expected<DecodedWebFont, String> DecodedWebFont::Create(
++ SegmentedBuffer* buffer) {
+ if (!buffer) {
+ return base::unexpected("Empty Buffer");
+ }
+
++ // This is the largest web font size which we'll try to transcode.
++ static const size_t kMaxDecompressedSize =
++ kMaxDecompressedSizeMb * 1024 * 1024;
+ if (buffer->size() > kMaxDecompressedSize) {
+ return base::unexpected(
+ Format("Web font size more than {}MB", kMaxDecompressedSizeMb));
+@@ -291,11 +201,7 @@ base::expected<DecodedWebFont, String> D
+ SegmentedBuffer::DeprecatedFlatData flattened_buffer(buffer);
+
+ bool ok;
+- std::unique_ptr<IftPatcher> ift_patcher;
+- if (RuntimeEnabledFeatures::IncrementalFontTransferEnabled()) {
+- ok = DecodeFontWithIft(ots_context, base::as_byte_span(flattened_buffer),
+- output.get(), ift_patcher);
+- } else {
++ {
+ TRACE_EVENT("blink", "DecodeFont");
+ ok = ots_context.Process(
+ output.get(), reinterpret_cast<const uint8_t*>(flattened_buffer.data()),
+@@ -307,9 +213,9 @@ base::expected<DecodedWebFont, String> D
+ }
+
+ const void* decoded_data = output->get();
+- DecodedWebFont result;
+- result.ift_patcher = std::move(ift_patcher);
+- result.decoded_size = base::checked_cast<size_t>(output->Tell());
++ DecodedWebFont result{
++ .decoded_size = base::checked_cast<size_t>(output->Tell()),
++ };
+ sk_sp<SkData> sk_data = SkData::MakeWithProc(
+ decoded_data, result.decoded_size,
+ [](const void*, void* output) {
+--- a/third_party/blink/renderer/platform/fonts/web_font_decoder.h
++++ b/third_party/blink/renderer/platform/fonts/web_font_decoder.h
+@@ -31,8 +31,6 @@
+ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_WEB_FONT_DECODER_H_
+ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_WEB_FONT_DECODER_H_
+
+-#include <memory>
+-
+ #include "base/types/expected.h"
+ #include "third_party/blink/renderer/platform/platform_export.h"
+ #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+@@ -42,7 +40,6 @@ class SkTypeface;
+
+ namespace blink {
+
+-class IftPatcher;
+ class SegmentedBuffer;
+
+ // Represents a font that has been successfully decoded and sanitized.
+@@ -56,32 +53,18 @@ struct PLATFORM_EXPORT DecodedWebFont {
+ // The size of the font after decoding and sanitizing.
+ size_t decoded_size = 0;
+
+- // Used to discover and apply patches to fonts. `nullptr` if the font is not
+- // IFT-encoded or if Incremental Font Transfer is disabled.
+- std::unique_ptr<IftPatcher> ift_patcher;
+-
+- // Defined out of line so that `IftPatcher` only needs to be forward declared
+- // here.
+- DecodedWebFont();
+- DecodedWebFont(DecodedWebFont&&) noexcept;
+- DecodedWebFont& operator=(DecodedWebFont&&) noexcept;
+- DecodedWebFont(const DecodedWebFont&) = delete;
+- DecodedWebFont& operator=(const DecodedWebFont&) = delete;
+- ~DecodedWebFont();
++ // Decodes, decompresses, and sanitizes the raw font data from the provided
++ // `buffer` (which typically contains WOFF, WOFF2, or TTF data).
++ //
++ // Returns:
++ // - On success: A `DecodedWebFont` containing the successfully created
++ // font.
++ // - On failure: A `String` containing a descriptive error message (e.g., if
++ // the buffer is empty, the decompressed size exceeds limits, or OTS
++ // validation fails).
++ static base::expected<DecodedWebFont, String> Create(SegmentedBuffer*);
+ };
+
+-// Decodes, decompresses, and sanitizes the raw font data from the provided
+-// `buffer` (which typically contains WOFF, WOFF2, or TTF data).
+-//
+-// Returns:
+-// - On success: A `DecodedWebFont` containing the successfully created
+-// font.
+-// - On failure: A `String` containing a descriptive error message (e.g., if
+-// the buffer is empty, the decompressed size exceeds limits, or OTS
+-// validation fails).
+-PLATFORM_EXPORT base::expected<DecodedWebFont, String> DecodeWebFont(
+- SegmentedBuffer* buffer);
+-
+ } // namespace blink
+
+ #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_WEB_FONT_DECODER_H_