From bcdbdbb5b3efe2d67677f434e2b4ee0fee1e121b Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sat, 23 May 2026 22:14:22 +0200 Subject: [PATCH] LibWeb: Add adblock-rust FFI hooks Add adblock-rust to a dedicated LibWeb content blocker Rust crate and expose FFI entry points for creating an engine, matching network requests, and producing cosmetic CSS. Export a string result type for returning generated CSS to C++ callers. Regenerate the Flatpak cargo source list for the new Rust dependencies. --- Cargo.lock | 240 +++++++++++++++++- Cargo.toml | 1 + Libraries/LibWeb/CMakeLists.txt | 15 +- .../LibWeb/ContentBlocker/Rust/Cargo.toml | 19 ++ Libraries/LibWeb/ContentBlocker/Rust/build.rs | 40 +++ .../LibWeb/ContentBlocker/Rust/cbindgen.toml | 26 ++ .../LibWeb/ContentBlocker/Rust/src/lib.rs | 216 ++++++++++++++++ Meta/CMake/flatpak/cargo-sources.json | 192 ++++++++++++++ 8 files changed, 741 insertions(+), 8 deletions(-) create mode 100644 Libraries/LibWeb/ContentBlocker/Rust/Cargo.toml create mode 100644 Libraries/LibWeb/ContentBlocker/Rust/build.rs create mode 100644 Libraries/LibWeb/ContentBlocker/Rust/cbindgen.toml create mode 100644 Libraries/LibWeb/ContentBlocker/Rust/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index a370ff4a24..5d00418372 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,41 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adblock" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db6fe7702c798519299bcbe011ff6f1dc9e3aedc1f3171178cddf71e1736bf95" +dependencies = [ + "addr", + "arrayvec", + "base64", + "bitflags", + "flatbuffers", + "idna", + "itertools", + "memchr", + "percent-encoding", + "precomputed-hash", + "regex", + "rustc-hash 1.1.0", + "seahash", + "serde", + "serde_json", + "thiserror", + "url", +] + +[[package]] +name = "addr" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a93b8a41dbe230ad5087cc721f8d41611de654542180586b315d9f4cf6b72bef" +dependencies = [ + "psl", + "psl-types", +] + [[package]] name = "aho-corasick" version = "1.1.4" @@ -79,17 +114,32 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "autocfg" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bitflags" version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +dependencies = [ + "serde_core", +] [[package]] name = "bumpalo" @@ -225,7 +275,7 @@ dependencies = [ "hashbrown 0.14.5", "log", "regalloc2", - "rustc-hash", + "rustc-hash 2.1.2", "serde", "smallvec", "target-lexicon", @@ -315,6 +365,12 @@ dependencies = [ "syn", ] +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + [[package]] name = "encoding_rs" version = "0.8.35" @@ -352,12 +408,31 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a043dc74da1e37d6afe657061213aa6f425f855399a11d3463c6ecccc4dfda1f" +[[package]] +name = "flatbuffers" +version = "25.12.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35f6839d7b3b98adde531effaf34f0c2badc6f4735d26fe74709d8e513a96ef3" +dependencies = [ + "bitflags", + "rustc_version", +] + [[package]] name = "foldhash" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + [[package]] name = "getrandom" version = "0.4.2" @@ -481,6 +556,46 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5fdcc9ac77c6d74ff5cf6e65ef3181d6af32003b16fce3a77fb451d2f695993" +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + [[package]] name = "icu_provider" version = "2.2.0" @@ -504,6 +619,27 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "indexmap" version = "2.13.1" @@ -522,6 +658,15 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.18" @@ -605,6 +750,15 @@ dependencies = [ "target-lexicon", ] +[[package]] +name = "libweb_content_blocker_rust" +version = "0.1.0" +dependencies = [ + "adblock", + "cbindgen", + "serde_json", +] + [[package]] name = "libweb_html_tokenizer" version = "0.1.0" @@ -685,6 +839,12 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + [[package]] name = "potential_utf" version = "0.1.5" @@ -696,6 +856,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + [[package]] name = "prettyplease" version = "0.2.37" @@ -715,6 +881,21 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "psl" +version = "2.1.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27fbd4d9b2eb0b6e0cbd654bce70243f460cbc8dc92df6747e3fb911ce1cd4a8" +dependencies = [ + "psl-types", +] + +[[package]] +name = "psl-types" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" + [[package]] name = "quote" version = "1.0.45" @@ -740,7 +921,7 @@ dependencies = [ "bumpalo", "hashbrown 0.15.5", "log", - "rustc-hash", + "rustc-hash 2.1.2", "smallvec", ] @@ -773,12 +954,27 @@ version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc-hash" version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "1.1.4" @@ -792,6 +988,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + [[package]] name = "semver" version = "1.0.28" @@ -909,6 +1111,26 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tinystr" version = "0.8.3" @@ -971,6 +1193,18 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + [[package]] name = "utf8_iter" version = "1.0.4" @@ -1216,6 +1450,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" dependencies = [ "displaydoc", + "yoke", + "zerofrom", "zerovec", ] diff --git a/Cargo.toml b/Cargo.toml index 1640bcabbe..776b72df96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "Libraries/LibRegex/Rust", "Libraries/LibUnicode/Rust", "Libraries/LibWasm/Rust", + "Libraries/LibWeb/ContentBlocker/Rust", "Libraries/LibWeb/Rust", "Libraries/LibWeb/HTML/Parser/Rust", ] diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index 0fed5c9325..62ea67ae72 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -1254,8 +1254,10 @@ ladybird_lib(LibWeb web EXPLICIT_SYMBOL_EXPORT) target_link_libraries(LibWeb PRIVATE LibCore LibCompress LibCrypto LibJS LibHTTP LibGfx LibIPC LibRegex LibSyntax LibTextCodec LibUnicode LibMedia LibWasm LibXML LibIDL LibURL LibTLS LibRequests LibGC LibSync LibThreading skia ${ANGLE_TARGETS} SDL3::SDL3 LibXml2::LibXml2) import_rust_crate(MANIFEST_PATH Rust/Cargo.toml CRATE_NAME libweb_rust FFI_HEADER RustFFI.h) -target_link_libraries(LibWeb PRIVATE libweb_rust) +import_rust_crate(MANIFEST_PATH ContentBlocker/Rust/Cargo.toml CRATE_NAME libweb_content_blocker_rust FFI_HEADER ContentBlockerRustFFI.h) +target_link_libraries(LibWeb PRIVATE libweb_rust libweb_content_blocker_rust) get_target_property(_libweb_rust_lib libweb_rust IMPORTED_LOCATION) +get_target_property(_libweb_content_blocker_rust_lib libweb_content_blocker_rust IMPORTED_LOCATION) set_property(SOURCE HTML/Parser/HTMLTokenizer.cpp APPEND PROPERTY OBJECT_DEPENDS ${_libweb_rust_lib}) set_property(SOURCE HTML/Parser/HTMLParser.cpp APPEND PROPERTY OBJECT_DEPENDS ${_libweb_rust_lib}) set_property(SOURCE HTML/Parser/SpeculativeHTMLParser.cpp APPEND PROPERTY OBJECT_DEPENDS ${_libweb_rust_lib}) @@ -1266,6 +1268,7 @@ endif() if(NOT BUILD_SHARED_LIBS) add_custom_command(TARGET LibWeb POST_BUILD COMMAND ${CMAKE_AR} -x $ + COMMAND ${CMAKE_AR} -x $ COMMAND ${CMAKE_AR} -qS $ *.o COMMAND ${CMAKE_RANLIB} $ COMMAND ${CMAKE_COMMAND} -E remove -f *.o @@ -1274,14 +1277,14 @@ if(NOT BUILD_SHARED_LIBS) ) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/rust_merge_tmp) - # POST_BUILD steps don't track inputs, so when libweb_rust.a changes + # POST_BUILD steps don't track inputs, so when a Rust archive changes # ninja leaves liblagom-web.a alone and the merged archive ends up with - # stale Rust objects. Force both FFI bridge files to depend on the - # Rust archive: when it changes, they recompile, LibWeb re-archives, and - # POST_BUILD re-merges the fresh Rust objects. - get_target_property(_libweb_rust_lib libweb_rust IMPORTED_LOCATION) + # stale Rust objects. Force the C++ FFI bridge files to depend on the + # Rust archives: when they change, LibWeb re-archives, and POST_BUILD + # re-merges the fresh Rust objects. set_property(SOURCE CSS/Parser/RustTokenizer.cpp APPEND PROPERTY OBJECT_DEPENDS ${_libweb_rust_lib}) set_property(SOURCE HTML/Parser/HTMLEncodingDetection.cpp APPEND PROPERTY OBJECT_DEPENDS ${_libweb_rust_lib}) + set_property(SOURCE Loader/ContentBlocker.cpp APPEND PROPERTY OBJECT_DEPENDS ${_libweb_content_blocker_rust_lib}) endif() if (HAS_FONTCONFIG) diff --git a/Libraries/LibWeb/ContentBlocker/Rust/Cargo.toml b/Libraries/LibWeb/ContentBlocker/Rust/Cargo.toml new file mode 100644 index 0000000000..b93a74d794 --- /dev/null +++ b/Libraries/LibWeb/ContentBlocker/Rust/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "libweb_content_blocker_rust" +version = "0.1.0" +edition = "2024" + +[lib] +crate-type = ["staticlib"] + +# After changing dependencies, regenerate the Flatpak sources: +# python3 Meta/CMake/flatpak/generate-cargo-sources.py +[dependencies] +adblock = "0.12.5" +serde_json = "1.0" + +[build-dependencies] +cbindgen = "0.29" + +[lints] +workspace = true diff --git a/Libraries/LibWeb/ContentBlocker/Rust/build.rs b/Libraries/LibWeb/ContentBlocker/Rust/build.rs new file mode 100644 index 0000000000..8a2c7e400d --- /dev/null +++ b/Libraries/LibWeb/ContentBlocker/Rust/build.rs @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2026-present, the Ladybird developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +use std::env; +use std::error::Error; +use std::path::PathBuf; + +fn main() -> Result<(), Box> { + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR")?); + let out_dir = PathBuf::from(env::var("OUT_DIR")?); + + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=cbindgen.toml"); + println!("cargo:rerun-if-env-changed=FFI_OUTPUT_DIR"); + println!("cargo:rerun-if-changed=src"); + + let ffi_out_dir = env::var("FFI_OUTPUT_DIR") + .map(PathBuf::from) + .unwrap_or_else(|_| out_dir.clone()); + + cbindgen::generate(manifest_dir).map_or_else( + |error| match error { + cbindgen::Error::ParseSyntaxError { .. } => {} + other => panic!("{other:?}"), + }, + |bindings| { + let header_path = out_dir.join("ContentBlockerRustFFI.h"); + bindings.write_to_file(&header_path); + + if ffi_out_dir != out_dir { + bindings.write_to_file(ffi_out_dir.join("ContentBlockerRustFFI.h")); + } + }, + ); + + Ok(()) +} diff --git a/Libraries/LibWeb/ContentBlocker/Rust/cbindgen.toml b/Libraries/LibWeb/ContentBlocker/Rust/cbindgen.toml new file mode 100644 index 0000000000..61e56a9a92 --- /dev/null +++ b/Libraries/LibWeb/ContentBlocker/Rust/cbindgen.toml @@ -0,0 +1,26 @@ +language = "C++" +header = """/* + * Copyright (c) 2026-present, the Ladybird developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */""" +pragma_once = true +include_version = true +namespaces = ["Web", "ContentBlocking", "FFI"] +line_length = 120 +tab_width = 4 +no_includes = true +sys_includes = ["stdint.h", "stddef.h"] +usize_is_size_t = true + +[parse] +parse_deps = false + +[parse.expand] +all_features = false + +[export] +include = ["ContentBlockerString"] + +[export.mangle] +rename_types = "PascalCase" diff --git a/Libraries/LibWeb/ContentBlocker/Rust/src/lib.rs b/Libraries/LibWeb/ContentBlocker/Rust/src/lib.rs new file mode 100644 index 0000000000..9b8d62e8cf --- /dev/null +++ b/Libraries/LibWeb/ContentBlocker/Rust/src/lib.rs @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2026-present, the Ladybird developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +use std::ffi::c_void; +use std::panic::{AssertUnwindSafe, catch_unwind}; + +#[repr(C)] +pub struct ContentBlockerString { + data: *mut u8, + length: usize, +} + +fn abort_on_panic R, R>(f: F) -> R { + match catch_unwind(AssertUnwindSafe(f)) { + Ok(result) => result, + Err(payload) => { + let message = if let Some(message) = payload.downcast_ref::<&str>() { + (*message).to_string() + } else if let Some(message) = payload.downcast_ref::() { + message.clone() + } else { + "unknown panic".to_string() + }; + eprintln!("Rust panic at FFI boundary: {message}"); + std::process::abort(); + } + } +} + +unsafe fn bytes_from_raw<'a>(bytes: *const u8, len: usize) -> Option<&'a [u8]> { + if len == 0 { + return Some(&[]); + } + if bytes.is_null() { + return None; + } + Some(unsafe { std::slice::from_raw_parts(bytes, len) }) +} + +unsafe fn string_from_raw<'a>(bytes: *const u8, len: usize) -> Option<&'a str> { + let bytes = unsafe { bytes_from_raw(bytes, len)? }; + std::str::from_utf8(bytes).ok() +} + +unsafe fn engine_from_raw<'a>(engine: *const c_void) -> Option<&'a adblock::engine::Engine> { + if engine.is_null() { + return None; + } + Some(unsafe { &*engine.cast::() }) +} + +fn string_to_ffi(string: String) -> ContentBlockerString { + if string.is_empty() { + return ContentBlockerString { + data: std::ptr::null_mut(), + length: 0, + }; + } + + let bytes = Box::leak(string.into_bytes().into_boxed_slice()); + let data = bytes.as_mut_ptr(); + let length = bytes.len(); + + ContentBlockerString { data, length } +} + +fn cosmetic_css_for_url( + engine: &adblock::engine::Engine, + url: &str, + classes: impl IntoIterator>, + ids: impl IntoIterator>, +) -> String { + let resources = engine.url_cosmetic_resources(url); + let mut selectors = resources.hide_selectors; + + if !resources.generichide { + selectors.extend(engine.hidden_class_id_selectors(classes, ids, &resources.exceptions)); + } + + let mut selector_styles: Vec<_> = selectors + .into_iter() + .map(|selector| (selector, "display: none !important".to_string())) + .collect(); + + for action in resources.procedural_actions { + let Ok(filter) = serde_json::from_str::(&action) + else { + continue; + }; + if let Some((selector, style)) = filter.as_css() { + selector_styles.push((selector, style)); + } + } + + selector_styles.sort_unstable(); + selector_styles.dedup(); + + let mut css = String::new(); + for (selector, style) in selector_styles { + css.push_str(&selector); + css.push_str(" { "); + css.push_str(&style); + if !style.trim_end().ends_with(';') { + css.push(';'); + } + css.push_str(" }\n"); + } + css +} + +/// # Safety +/// - `rules` and `rules_len` must point to a valid UTF-8 string +/// - The returned pointer must be freed with `rust_content_blocker_free` +#[unsafe(no_mangle)] +pub unsafe extern "C" fn rust_content_blocker_create(rules: *const u8, rules_len: usize) -> *mut c_void { + abort_on_panic(|| { + let Some(rules) = (unsafe { string_from_raw(rules, rules_len) }) else { + return std::ptr::null_mut(); + }; + + let engine = adblock::engine::Engine::from_rules(rules.lines(), adblock::lists::ParseOptions::default()); + Box::into_raw(Box::new(engine)).cast() + }) +} + +/// # Safety +/// - `engine` must be null or a valid pointer returned by `rust_content_blocker_create` +#[unsafe(no_mangle)] +pub unsafe extern "C" fn rust_content_blocker_free(engine: *mut c_void) { + abort_on_panic(|| { + if !engine.is_null() { + drop(unsafe { Box::from_raw(engine.cast::()) }); + } + }); +} + +/// # Safety +/// - `engine` must be null or a valid pointer returned by `rust_content_blocker_create` +/// - String pointers and lengths must point to valid UTF-8 strings +#[unsafe(no_mangle)] +pub unsafe extern "C" fn rust_content_blocker_matches( + engine: *const c_void, + url: *const u8, + url_len: usize, + source_url: *const u8, + source_url_len: usize, + request_type: *const u8, + request_type_len: usize, +) -> bool { + abort_on_panic(|| { + let Some(engine) = (unsafe { engine_from_raw(engine) }) else { + return false; + }; + let Some(url) = (unsafe { string_from_raw(url, url_len) }) else { + return false; + }; + let Some(source_url) = (unsafe { string_from_raw(source_url, source_url_len) }) else { + return false; + }; + let Some(request_type) = (unsafe { string_from_raw(request_type, request_type_len) }) else { + return false; + }; + + let Ok(request) = adblock::request::Request::new(url, source_url, request_type) else { + return false; + }; + + engine.check_network_request(&request).matched + }) +} + +/// # Safety +/// - `engine` must be null or a valid pointer returned by `rust_content_blocker_create` +/// - String pointers and lengths must point to valid UTF-8 strings +/// - The returned string must be freed with `rust_content_blocker_free_string` +#[unsafe(no_mangle)] +pub unsafe extern "C" fn rust_content_blocker_cosmetic_css( + engine: *const c_void, + url: *const u8, + url_len: usize, + classes: *const u8, + classes_len: usize, + ids: *const u8, + ids_len: usize, +) -> ContentBlockerString { + abort_on_panic(|| { + let Some(engine) = (unsafe { engine_from_raw(engine) }) else { + return string_to_ffi(String::new()); + }; + let Some(url) = (unsafe { string_from_raw(url, url_len) }) else { + return string_to_ffi(String::new()); + }; + let Some(classes) = (unsafe { string_from_raw(classes, classes_len) }) else { + return string_to_ffi(String::new()); + }; + let Some(ids) = (unsafe { string_from_raw(ids, ids_len) }) else { + return string_to_ffi(String::new()); + }; + + string_to_ffi(cosmetic_css_for_url(engine, url, classes.lines(), ids.lines())) + }) +} + +/// # Safety +/// - `data` and `length` must match a string returned by `rust_content_blocker_cosmetic_css` +#[unsafe(no_mangle)] +pub unsafe extern "C" fn rust_content_blocker_free_string(data: *mut u8, length: usize) { + abort_on_panic(|| { + if !data.is_null() { + drop(unsafe { Box::from_raw(std::ptr::slice_from_raw_parts_mut(data, length)) }); + } + }); +} diff --git a/Meta/CMake/flatpak/cargo-sources.json b/Meta/CMake/flatpak/cargo-sources.json index 018b011cfa..857643e7dc 100644 --- a/Meta/CMake/flatpak/cargo-sources.json +++ b/Meta/CMake/flatpak/cargo-sources.json @@ -5,6 +5,20 @@ "tag": "v0.5.1", "dest": "corrosion" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/adblock/adblock-0.12.5.crate", + "sha256": "db6fe7702c798519299bcbe011ff6f1dc9e3aedc1f3171178cddf71e1736bf95", + "dest": "cargo/vendor/adblock-0.12.5" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/addr/addr-0.15.6.crate", + "sha256": "a93b8a41dbe230ad5087cc721f8d41611de654542180586b315d9f4cf6b72bef", + "dest": "cargo/vendor/addr-0.15.6" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -68,6 +82,13 @@ "sha256": "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1", "dest": "cargo/vendor/arbitrary-1.4.2" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/arrayvec/arrayvec-0.7.6.crate", + "sha256": "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50", + "dest": "cargo/vendor/arrayvec-0.7.6" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -75,6 +96,13 @@ "sha256": "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8", "dest": "cargo/vendor/autocfg-1.5.0" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/base64/base64-0.22.1.crate", + "sha256": "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6", + "dest": "cargo/vendor/base64-0.22.1" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -236,6 +264,13 @@ "sha256": "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0", "dest": "cargo/vendor/displaydoc-0.2.5" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/either/either-1.16.0.crate", + "sha256": "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e", + "dest": "cargo/vendor/either-1.16.0" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -271,6 +306,13 @@ "sha256": "a043dc74da1e37d6afe657061213aa6f425f855399a11d3463c6ecccc4dfda1f", "dest": "cargo/vendor/fastrand-2.4.0" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/flatbuffers/flatbuffers-25.12.19.crate", + "sha256": "35f6839d7b3b98adde531effaf34f0c2badc6f4735d26fe74709d8e513a96ef3", + "dest": "cargo/vendor/flatbuffers-25.12.19" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -278,6 +320,13 @@ "sha256": "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2", "dest": "cargo/vendor/foldhash-0.1.5" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/form_urlencoded/form_urlencoded-1.2.2.crate", + "sha256": "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf", + "dest": "cargo/vendor/form_urlencoded-1.2.2" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -362,6 +411,34 @@ "sha256": "d5fdcc9ac77c6d74ff5cf6e65ef3181d6af32003b16fce3a77fb451d2f695993", "dest": "cargo/vendor/icu_locale_data-2.2.0" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/icu_normalizer/icu_normalizer-2.2.0.crate", + "sha256": "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4", + "dest": "cargo/vendor/icu_normalizer-2.2.0" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/icu_normalizer_data/icu_normalizer_data-2.2.0.crate", + "sha256": "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38", + "dest": "cargo/vendor/icu_normalizer_data-2.2.0" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/icu_properties/icu_properties-2.2.0.crate", + "sha256": "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de", + "dest": "cargo/vendor/icu_properties-2.2.0" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/icu_properties_data/icu_properties_data-2.2.0.crate", + "sha256": "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14", + "dest": "cargo/vendor/icu_properties_data-2.2.0" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -376,6 +453,20 @@ "sha256": "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954", "dest": "cargo/vendor/id-arena-2.3.0" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/idna/idna-1.1.0.crate", + "sha256": "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de", + "dest": "cargo/vendor/idna-1.1.0" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/idna_adapter/idna_adapter-1.2.2.crate", + "sha256": "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714", + "dest": "cargo/vendor/idna_adapter-1.2.2" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -390,6 +481,13 @@ "sha256": "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695", "dest": "cargo/vendor/is_terminal_polyfill-1.70.2" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/itertools/itertools-0.13.0.crate", + "sha256": "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186", + "dest": "cargo/vendor/itertools-0.13.0" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -488,6 +586,13 @@ "sha256": "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe", "dest": "cargo/vendor/once_cell_polyfill-1.70.2" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/percent-encoding/percent-encoding-2.3.2.crate", + "sha256": "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220", + "dest": "cargo/vendor/percent-encoding-2.3.2" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -495,6 +600,13 @@ "sha256": "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564", "dest": "cargo/vendor/potential_utf-0.1.5" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/precomputed-hash/precomputed-hash-0.1.1.crate", + "sha256": "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c", + "dest": "cargo/vendor/precomputed-hash-0.1.1" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -509,6 +621,20 @@ "sha256": "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934", "dest": "cargo/vendor/proc-macro2-1.0.106" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/psl/psl-2.1.210.crate", + "sha256": "27fbd4d9b2eb0b6e0cbd654bce70243f460cbc8dc92df6747e3fb911ce1cd4a8", + "dest": "cargo/vendor/psl-2.1.210" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/psl-types/psl-types-2.0.11.crate", + "sha256": "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac", + "dest": "cargo/vendor/psl-types-2.0.11" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -551,6 +677,13 @@ "sha256": "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a", "dest": "cargo/vendor/regex-syntax-0.8.10" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/rustc-hash/rustc-hash-1.1.0.crate", + "sha256": "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2", + "dest": "cargo/vendor/rustc-hash-1.1.0" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -558,6 +691,13 @@ "sha256": "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe", "dest": "cargo/vendor/rustc-hash-2.1.2" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/rustc_version/rustc_version-0.4.1.crate", + "sha256": "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92", + "dest": "cargo/vendor/rustc_version-0.4.1" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -565,6 +705,13 @@ "sha256": "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190", "dest": "cargo/vendor/rustix-1.1.4" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/seahash/seahash-4.1.0.crate", + "sha256": "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b", + "dest": "cargo/vendor/seahash-4.1.0" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -656,6 +803,20 @@ "sha256": "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd", "dest": "cargo/vendor/tempfile-3.27.0" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/thiserror/thiserror-1.0.69.crate", + "sha256": "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52", + "dest": "cargo/vendor/thiserror-1.0.69" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/thiserror-impl/thiserror-impl-1.0.69.crate", + "sha256": "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1", + "dest": "cargo/vendor/thiserror-impl-1.0.69" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -705,6 +866,13 @@ "sha256": "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853", "dest": "cargo/vendor/unicode-xid-0.2.6" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/url/url-2.5.8.crate", + "sha256": "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed", + "dest": "cargo/vendor/url-2.5.8" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -897,6 +1065,8 @@ { "type": "shell", "commands": [ + "echo '{\"files\": {}, \"package\": \"db6fe7702c798519299bcbe011ff6f1dc9e3aedc1f3171178cddf71e1736bf95\"}' > cargo/vendor/adblock-0.12.5/.cargo-checksum.json", + "echo '{\"files\": {}, \"package\": \"a93b8a41dbe230ad5087cc721f8d41611de654542180586b315d9f4cf6b72bef\"}' > cargo/vendor/addr-0.15.6/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301\"}' > cargo/vendor/aho-corasick-1.1.4/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923\"}' > cargo/vendor/allocator-api2-0.2.21/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d\"}' > cargo/vendor/anstream-1.0.0/.cargo-checksum.json", @@ -906,7 +1076,9 @@ "echo '{\"files\": {}, \"package\": \"291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d\"}' > cargo/vendor/anstyle-wincon-3.0.11/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c\"}' > cargo/vendor/anyhow-1.0.102/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1\"}' > cargo/vendor/arbitrary-1.4.2/.cargo-checksum.json", + "echo '{\"files\": {}, \"package\": \"7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50\"}' > cargo/vendor/arrayvec-0.7.6/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8\"}' > cargo/vendor/autocfg-1.5.0/.cargo-checksum.json", + "echo '{\"files\": {}, \"package\": \"72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6\"}' > cargo/vendor/base64-0.22.1/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af\"}' > cargo/vendor/bitflags-2.11.0/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb\"}' > cargo/vendor/bumpalo-3.20.2/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"5abbd6eeda6885048d357edc66748eea6e0268e3dd11f326fff5bd248d779c26\"}' > cargo/vendor/calendrical_calculations-0.2.4/.cargo-checksum.json", @@ -930,12 +1102,15 @@ "echo '{\"files\": {}, \"package\": \"4d55612bebcf16ff7306c8a6f5bdb6d45662b8aa1ee058ecce8807ad87db719b\"}' > cargo/vendor/cranelift-module-0.116.1/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"b8dee82f3f1f2c4cba9177f1cc5e350fe98764379bcd29340caa7b01f85076c7\"}' > cargo/vendor/cranelift-native-0.116.1/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0\"}' > cargo/vendor/displaydoc-0.2.5/.cargo-checksum.json", + "echo '{\"files\": {}, \"package\": \"91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e\"}' > cargo/vendor/either-1.16.0/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3\"}' > cargo/vendor/encoding_rs-0.8.35/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f\"}' > cargo/vendor/equivalent-1.0.2/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb\"}' > cargo/vendor/errno-0.3.14/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649\"}' > cargo/vendor/fallible-iterator-0.3.0/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"a043dc74da1e37d6afe657061213aa6f425f855399a11d3463c6ecccc4dfda1f\"}' > cargo/vendor/fastrand-2.4.0/.cargo-checksum.json", + "echo '{\"files\": {}, \"package\": \"35f6839d7b3b98adde531effaf34f0c2badc6f4735d26fe74709d8e513a96ef3\"}' > cargo/vendor/flatbuffers-25.12.19/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2\"}' > cargo/vendor/foldhash-0.1.5/.cargo-checksum.json", + "echo '{\"files\": {}, \"package\": \"cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf\"}' > cargo/vendor/form_urlencoded-1.2.2/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555\"}' > cargo/vendor/getrandom-0.4.2/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f\"}' > cargo/vendor/gimli-0.31.1/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1\"}' > cargo/vendor/hashbrown-0.14.5/.cargo-checksum.json", @@ -948,10 +1123,17 @@ "echo '{\"files\": {}, \"package\": \"d5a396343c7208121dc86e35623d3dfe19814a7613cfd14964994cdc9c9a2e26\"}' > cargo/vendor/icu_locale-2.2.0/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29\"}' > cargo/vendor/icu_locale_core-2.2.0/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"d5fdcc9ac77c6d74ff5cf6e65ef3181d6af32003b16fce3a77fb451d2f695993\"}' > cargo/vendor/icu_locale_data-2.2.0/.cargo-checksum.json", + "echo '{\"files\": {}, \"package\": \"c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4\"}' > cargo/vendor/icu_normalizer-2.2.0/.cargo-checksum.json", + "echo '{\"files\": {}, \"package\": \"da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38\"}' > cargo/vendor/icu_normalizer_data-2.2.0/.cargo-checksum.json", + "echo '{\"files\": {}, \"package\": \"bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de\"}' > cargo/vendor/icu_properties-2.2.0/.cargo-checksum.json", + "echo '{\"files\": {}, \"package\": \"8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14\"}' > cargo/vendor/icu_properties_data-2.2.0/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421\"}' > cargo/vendor/icu_provider-2.2.0/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954\"}' > cargo/vendor/id-arena-2.3.0/.cargo-checksum.json", + "echo '{\"files\": {}, \"package\": \"3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de\"}' > cargo/vendor/idna-1.1.0/.cargo-checksum.json", + "echo '{\"files\": {}, \"package\": \"cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714\"}' > cargo/vendor/idna_adapter-1.2.2/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff\"}' > cargo/vendor/indexmap-2.13.1/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695\"}' > cargo/vendor/is_terminal_polyfill-1.70.2/.cargo-checksum.json", + "echo '{\"files\": {}, \"package\": \"413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186\"}' > cargo/vendor/itertools-0.13.0/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682\"}' > cargo/vendor/itoa-1.0.18/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"2ceaf4c6c48465bead8cb6a0b7c4ee0c86ecbb31239032b9c66ab9a08d2f3ee1\"}' > cargo/vendor/ixdtf-0.6.5/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2\"}' > cargo/vendor/leb128fmt-0.1.0/.cargo-checksum.json", @@ -966,17 +1148,24 @@ "echo '{\"files\": {}, \"package\": \"071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841\"}' > cargo/vendor/num-traits-0.2.19/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50\"}' > cargo/vendor/once_cell-1.21.4/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe\"}' > cargo/vendor/once_cell_polyfill-1.70.2/.cargo-checksum.json", + "echo '{\"files\": {}, \"package\": \"9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220\"}' > cargo/vendor/percent-encoding-2.3.2/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564\"}' > cargo/vendor/potential_utf-0.1.5/.cargo-checksum.json", + "echo '{\"files\": {}, \"package\": \"925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c\"}' > cargo/vendor/precomputed-hash-0.1.1/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b\"}' > cargo/vendor/prettyplease-0.2.37/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934\"}' > cargo/vendor/proc-macro2-1.0.106/.cargo-checksum.json", + "echo '{\"files\": {}, \"package\": \"27fbd4d9b2eb0b6e0cbd654bce70243f460cbc8dc92df6747e3fb911ce1cd4a8\"}' > cargo/vendor/psl-2.1.210/.cargo-checksum.json", + "echo '{\"files\": {}, \"package\": \"33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac\"}' > cargo/vendor/psl-types-2.0.11/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924\"}' > cargo/vendor/quote-1.0.45/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf\"}' > cargo/vendor/r-efi-6.0.0/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"dc06e6b318142614e4a48bc725abbf08ff166694835c43c9dae5a9009704639a\"}' > cargo/vendor/regalloc2-0.11.2/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276\"}' > cargo/vendor/regex-1.12.3/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f\"}' > cargo/vendor/regex-automata-0.4.14/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a\"}' > cargo/vendor/regex-syntax-0.8.10/.cargo-checksum.json", + "echo '{\"files\": {}, \"package\": \"08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2\"}' > cargo/vendor/rustc-hash-1.1.0/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe\"}' > cargo/vendor/rustc-hash-2.1.2/.cargo-checksum.json", + "echo '{\"files\": {}, \"package\": \"cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92\"}' > cargo/vendor/rustc_version-0.4.1/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190\"}' > cargo/vendor/rustix-1.1.4/.cargo-checksum.json", + "echo '{\"files\": {}, \"package\": \"1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b\"}' > cargo/vendor/seahash-4.1.0/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd\"}' > cargo/vendor/semver-1.0.28/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e\"}' > cargo/vendor/serde-1.0.228/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad\"}' > cargo/vendor/serde_core-1.0.228/.cargo-checksum.json", @@ -990,6 +1179,8 @@ "echo '{\"files\": {}, \"package\": \"728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2\"}' > cargo/vendor/synstructure-0.13.2/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca\"}' > cargo/vendor/target-lexicon-0.13.5/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd\"}' > cargo/vendor/tempfile-3.27.0/.cargo-checksum.json", + "echo '{\"files\": {}, \"package\": \"b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52\"}' > cargo/vendor/thiserror-1.0.69/.cargo-checksum.json", + "echo '{\"files\": {}, \"package\": \"4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1\"}' > cargo/vendor/thiserror-impl-1.0.69/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d\"}' > cargo/vendor/tinystr-0.8.3/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863\"}' > cargo/vendor/toml-0.9.12+spec-1.1.0/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347\"}' > cargo/vendor/toml_datetime-0.7.5+spec-1.1.0/.cargo-checksum.json", @@ -997,6 +1188,7 @@ "echo '{\"files\": {}, \"package\": \"756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db\"}' > cargo/vendor/toml_writer-1.1.1+spec-1.1.0/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75\"}' > cargo/vendor/unicode-ident-1.0.24/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853\"}' > cargo/vendor/unicode-xid-0.2.6/.cargo-checksum.json", + "echo '{\"files\": {}, \"package\": \"ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed\"}' > cargo/vendor/url-2.5.8/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be\"}' > cargo/vendor/utf8_iter-1.0.4/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821\"}' > cargo/vendor/utf8parse-0.2.2/.cargo-checksum.json", "echo '{\"files\": {}, \"package\": \"9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5\"}' > cargo/vendor/wasip2-1.0.2+wasi-0.2.9/.cargo-checksum.json",