From 5ef34ecb7a5041b623d3b8a82b590cfd5850009d Mon Sep 17 00:00:00 2001 From: Shannon Booth Date: Mon, 8 Jun 2026 23:20:48 +0200 Subject: [PATCH] LibURL: Use libpsl for public suffix matching Replace the generated public suffix table and custom matcher with a direct LibURL PublicSuffixData implementation backed by libpsl. This drops our PSL download/generator path and uses the same library already used by libcurl. Performance is comparable before and after, while LibURL binary size is smaller. --- Libraries/LibHTTP/Cookie/Cookie.cpp | 2 +- Libraries/LibURL/CMakeLists.txt | 6 +- Libraries/LibURL/Host.cpp | 2 +- Libraries/LibURL/PublicSuffixData.cpp | 165 +++++++++ Libraries/LibURL/PublicSuffixData.h | 25 ++ Libraries/LibWebView/CookieJar.cpp | 2 +- Libraries/LibWebView/URL.cpp | 4 +- Meta/CMake/check_for_dependencies.cmake | 1 + Meta/CMake/flatpak/org.ladybird.Ladybird.json | 32 +- Meta/CMake/public_suffix.cmake | 24 -- .../Generators/generate_public_suffix_data.py | 322 ------------------ Tests/LibURL/TestPublicSuffix.cpp | 50 ++- vcpkg.json | 5 + 13 files changed, 250 insertions(+), 390 deletions(-) create mode 100644 Libraries/LibURL/PublicSuffixData.cpp create mode 100644 Libraries/LibURL/PublicSuffixData.h delete mode 100644 Meta/CMake/public_suffix.cmake delete mode 100644 Meta/Generators/generate_public_suffix_data.py diff --git a/Libraries/LibHTTP/Cookie/Cookie.cpp b/Libraries/LibHTTP/Cookie/Cookie.cpp index 9acb51fb95..1f08c27e21 100644 --- a/Libraries/LibHTTP/Cookie/Cookie.cpp +++ b/Libraries/LibHTTP/Cookie/Cookie.cpp @@ -162,7 +162,7 @@ bool cookie_matches_url(Cookie const& cookie, URL::URL const& url, String const& // the cookie's domain. // - The cookie's domain is not a public suffix, for user agents configured to reject "public suffixes". bool is_not_host_only_and_domain_matches = (!cookie.host_only && domain_matches(retrieval_host_canonical, cookie.domain)) - && !URL::PublicSuffixData::the()->is_matching_public_suffix(cookie.domain); + && !URL::PublicSuffixData::is_matching_public_suffix(cookie.domain); if (!is_host_only_and_has_identical_domain && !is_not_host_only_and_domain_matches) return false; diff --git a/Libraries/LibURL/CMakeLists.txt b/Libraries/LibURL/CMakeLists.txt index a996a95a02..b80226baff 100644 --- a/Libraries/LibURL/CMakeLists.txt +++ b/Libraries/LibURL/CMakeLists.txt @@ -1,17 +1,15 @@ -include(public_suffix) - set(SOURCES Host.cpp Origin.cpp Parser.cpp + PublicSuffixData.cpp RustIntegration.cpp Site.cpp URL.cpp - ${PUBLIC_SUFFIX_SOURCES} ) ladybird_lib(LibURL url) -target_link_libraries(LibURL PRIVATE liburl_rust LibUnicode LibTextCodec LibRegex) +target_link_libraries(LibURL PRIVATE liburl_rust LibUnicode LibTextCodec LibRegex PkgConfig::LIBPSL) import_rust_crate(MANIFEST_PATH Rust/Cargo.toml CRATE_NAME liburl_rust FEATURES allocator FFI_HEADER RustFFI.h) target_link_libraries(liburl_rust INTERFACE LibUnicode LibTextCodec LibRegex) diff --git a/Libraries/LibURL/Host.cpp b/Libraries/LibURL/Host.cpp index 48f28df8b8..77edb85708 100644 --- a/Libraries/LibURL/Host.cpp +++ b/Libraries/LibURL/Host.cpp @@ -199,7 +199,7 @@ Optional Host::public_suffix() const host_without_trailing_dot = host_without_trailing_dot.substring_view(0, host_without_trailing_dot.length() - 1); // FIXME: Unify this logic with registrable domain. - auto public_suffix = PublicSuffixData::the()->find_matching_public_suffix(host_without_trailing_dot); + auto public_suffix = PublicSuffixData::find_matching_public_suffix(host_without_trailing_dot); if (!public_suffix.has_value()) { auto last_dot = host_without_trailing_dot.find_last('.'); if (last_dot.has_value()) diff --git a/Libraries/LibURL/PublicSuffixData.cpp b/Libraries/LibURL/PublicSuffixData.cpp new file mode 100644 index 0000000000..ffff33ef05 --- /dev/null +++ b/Libraries/LibURL/PublicSuffixData.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2026-present, the Ladybird developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include + +#include + +namespace URL { + +static psl_ctx_t const* public_suffix_context() +{ + static auto const* context = psl_builtin(); + VERIFY(context); + return context; +} + +static constexpr auto public_suffix_match_types = PSL_TYPE_ANY | PSL_TYPE_NO_STAR_RULE; + +struct NormalizedDomain { + StringView host; + StringView trailing_dot; +}; + +static Optional normalized_domain_for_host(Host const& host) +{ + if (!host.is_domain()) + return OptionalNone {}; + + auto domain = host.get().bytes_as_string_view().trim("."sv, TrimMode::Left); + if (domain.is_empty()) + return OptionalNone {}; + + auto trailing_dot = ""sv; + if (domain.ends_with('.')) { + trailing_dot = "."sv; + domain = domain.substring_view(0, domain.length() - 1); + if (domain.is_empty()) + return OptionalNone {}; + } + + return NormalizedDomain { domain, trailing_dot }; +} + +static bool is_matching_public_suffix_impl(StringView host) +{ + ByteString lookup_host { host }; + return psl_is_public_suffix2(public_suffix_context(), lookup_host.characters(), public_suffix_match_types); +} + +bool PublicSuffixData::is_matching_public_suffix(StringView host) +{ + if (host.is_empty()) + return false; + + auto parsed_host = Parser::parse_host(host); + if (!parsed_host.has_value()) + return false; + + return is_matching_public_suffix(*parsed_host); +} + +bool PublicSuffixData::is_matching_public_suffix(Host const& host) +{ + auto normalized_domain = normalized_domain_for_host(host); + if (!normalized_domain.has_value()) + return false; + + return is_matching_public_suffix_impl(normalized_domain->host); +} + +static Optional find_matching_public_suffix_impl(StringView host) +{ + auto remaining_host = host; + while (!remaining_host.is_empty()) { + if (is_matching_public_suffix_impl(remaining_host)) + return MUST(String::from_utf8(remaining_host)); + + auto next_label_separator = remaining_host.find('.'); + if (!next_label_separator.has_value()) + return OptionalNone {}; + + remaining_host = remaining_host.substring_view(*next_label_separator + 1); + } + + return OptionalNone {}; +} + +Optional PublicSuffixData::find_matching_public_suffix(StringView string) +{ + if (string.is_empty()) + return {}; + + auto parsed_host = Parser::parse_host(string); + if (!parsed_host.has_value()) + return {}; + + return find_matching_public_suffix(*parsed_host); +} + +Optional PublicSuffixData::find_matching_public_suffix(Host const& host) +{ + auto normalized_domain = normalized_domain_for_host(host); + if (!normalized_domain.has_value()) + return {}; + + auto public_suffix = find_matching_public_suffix_impl(normalized_domain->host); + if (!public_suffix.has_value()) + return {}; + + return MUST(String::formatted("{}{}", public_suffix.value(), normalized_domain->trailing_dot)); +} + +static Optional find_matching_registrable_domain_impl(StringView host) +{ + // find_matching_public_suffix_impl() always returns a tail of host, so it is by construction a suffix of it. + auto public_suffix = find_matching_public_suffix_impl(host); + if (!public_suffix.has_value() || host == *public_suffix) + return {}; + + auto subhost = host.substring_view(0, host.length() - public_suffix->bytes_as_string_view().length()); + subhost = subhost.trim("."sv, TrimMode::Right); + + if (subhost.is_empty()) + return {}; + + size_t start_index = 0; + if (auto index = subhost.find_last('.'); index.has_value()) + start_index = *index + 1; + + return MUST(String::from_utf8(host.substring_view(start_index))); +} + +Optional PublicSuffixData::find_matching_registrable_domain(StringView string) +{ + if (string.is_empty()) + return {}; + + auto parsed_host = Parser::parse_host(string); + if (!parsed_host.has_value()) + return {}; + + return find_matching_registrable_domain(*parsed_host); +} + +Optional PublicSuffixData::find_matching_registrable_domain(Host const& host) +{ + auto normalized_domain = normalized_domain_for_host(host); + if (!normalized_domain.has_value()) + return {}; + + auto registrable_domain = find_matching_registrable_domain_impl(normalized_domain->host); + if (!registrable_domain.has_value()) + return {}; + + return MUST(String::formatted("{}{}", registrable_domain.value(), normalized_domain->trailing_dot)); +} + +} diff --git a/Libraries/LibURL/PublicSuffixData.h b/Libraries/LibURL/PublicSuffixData.h new file mode 100644 index 0000000000..882f844e73 --- /dev/null +++ b/Libraries/LibURL/PublicSuffixData.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2026-present, the Ladybird developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace URL { + +class PublicSuffixData { +public: + static bool is_matching_public_suffix(StringView host); + static bool is_matching_public_suffix(Host const& host); + static Optional find_matching_public_suffix(StringView string); + static Optional find_matching_public_suffix(Host const& host); + static Optional find_matching_registrable_domain(StringView string); + static Optional find_matching_registrable_domain(Host const& host); +}; + +} diff --git a/Libraries/LibWebView/CookieJar.cpp b/Libraries/LibWebView/CookieJar.cpp index e4a3d295be..ef12748c73 100644 --- a/Libraries/LibWebView/CookieJar.cpp +++ b/Libraries/LibWebView/CookieJar.cpp @@ -199,7 +199,7 @@ void CookieJar::set_cookie(URL::URL const& url, HTTP::Cookie::ParsedCookie const return; // 9. If the user agent is configured to reject "public suffixes" and the domain-attribute is a public suffix: - if (URL::PublicSuffixData::the()->is_matching_public_suffix(domain_attribute)) { + if (URL::PublicSuffixData::is_matching_public_suffix(domain_attribute)) { // 1. Let request-host-canonical be the canonicalized request-host. // 2. If request-host fails to be canonicalized then abort this algorithm and ignore the cookie entirely. diff --git a/Libraries/LibWebView/URL.cpp b/Libraries/LibWebView/URL.cpp index cb314be934..540360511c 100644 --- a/Libraries/LibWebView/URL.cpp +++ b/Libraries/LibWebView/URL.cpp @@ -63,7 +63,7 @@ Optional sanitize_url(StringView location, Optional cons if (any_of(RESERVED_TLDS, [&](StringView const& tld) { return domain.byte_count() > tld.length() && domain.ends_with_bytes(tld); })) return url; - auto public_suffix = URL::PublicSuffixData::the()->find_matching_public_suffix(domain); + auto public_suffix = URL::PublicSuffixData::find_matching_public_suffix(domain); if (!public_suffix.has_value() || *public_suffix == domain) { if (append_tld == AppendTLD::Yes) url->set_host(MUST(String::formatted("{}.com", domain))); @@ -248,7 +248,7 @@ static URLParts break_web_url_into_parts(URL::URL const& url, StringView url_str domain = url_without_scheme; } - auto public_suffix = URL::PublicSuffixData::the()->find_matching_public_suffix(domain); + auto public_suffix = URL::PublicSuffixData::find_matching_public_suffix(domain); if (!public_suffix.has_value() || !domain.ends_with(*public_suffix)) return { scheme, domain, remainder }; diff --git a/Meta/CMake/check_for_dependencies.cmake b/Meta/CMake/check_for_dependencies.cmake index 57f67d9be0..fd4ccf1119 100644 --- a/Meta/CMake/check_for_dependencies.cmake +++ b/Meta/CMake/check_for_dependencies.cmake @@ -70,6 +70,7 @@ find_package(SQLite3 REQUIRED) find_package(Threads REQUIRED) find_package(ZLIB REQUIRED) +pkg_check_modules(LIBPSL REQUIRED IMPORTED_TARGET libpsl) pkg_check_modules(libtommath REQUIRED IMPORTED_TARGET libtommath) find_package(unofficial-angle CONFIG) diff --git a/Meta/CMake/flatpak/org.ladybird.Ladybird.json b/Meta/CMake/flatpak/org.ladybird.Ladybird.json index 3b0ce1ace1..6cb36e4697 100644 --- a/Meta/CMake/flatpak/org.ladybird.Ladybird.json +++ b/Meta/CMake/flatpak/org.ladybird.Ladybird.json @@ -237,6 +237,32 @@ "/share/icu" ] }, + { + "name": "libpsl", + "buildsystem": "meson", + "sources": [ + { + "type": "git", + "url": "https://github.com/rockdaboot/libpsl.git", + "tag": "0.21.5" + }, + { + "type": "file", + "url": "https://raw.githubusercontent.com/publicsuffix/list/303b81a2140a3192d336f22af389a28daf6efab6/public_suffix_list.dat", + "sha256": "82d4d57e397177be3a627df27f88dc0ab04f54d081bc94c68f82c3e100855e0b", + "dest": "list/" + } + ], + "config-opts": [ + "-Ddocs=false", + "-Druntime=libicu", + "-Dtests=false", + "--libdir=/app/lib" + ], + "cleanup": [ + "/bin" + ] + }, { "name": "libxml2", "buildsystem": "cmake-ninja", @@ -727,12 +753,6 @@ "name": "Ladybird", "buildsystem": "cmake-ninja", "sources": [ - { - "type": "file", - "url": "https://raw.githubusercontent.com/publicsuffix/list/32c68ce9d52e12df5b914b2b248cb893147301f7/public_suffix_list.dat", - "dest": "Caches/PublicSuffix/", - "sha256": "e79e372bcc6fcdb51f7a31e3c0c504530838432669af2ac544d2491de0a86030" - }, { "type": "file", "url": "https://raw.githubusercontent.com/chromium/chromium/aa04f175415addb04bb78936b0d8b973fbd8ea61/net/http/transport_security_state_static.json", diff --git a/Meta/CMake/public_suffix.cmake b/Meta/CMake/public_suffix.cmake deleted file mode 100644 index 9a5f78dc12..0000000000 --- a/Meta/CMake/public_suffix.cmake +++ /dev/null @@ -1,24 +0,0 @@ -include(${CMAKE_CURRENT_LIST_DIR}/utils.cmake) - -set(PUBLIC_SUFFIX_PATH "${LADYBIRD_CACHE_DIR}/PublicSuffix" CACHE PATH "Download location for PublicSuffix files") -set(PUBLIC_SUFFIX_DATA_URL "https://raw.githubusercontent.com/publicsuffix/list/master/public_suffix_list.dat") -set(PUBLIC_SUFFIX_DATA_PATH "${PUBLIC_SUFFIX_PATH}/public_suffix_list.dat") -set(PUBLIC_SUFFIX_DATA_HEADER PublicSuffixData.h) -set(PUBLIC_SUFFIX_DATA_IMPLEMENTATION PublicSuffixData.cpp) -if (ENABLE_NETWORK_DOWNLOADS) - download_file("${PUBLIC_SUFFIX_DATA_URL}" "${PUBLIC_SUFFIX_DATA_PATH}") -else() - message(STATUS "Skipping download of ${PUBLIC_SUFFIX_DATA_URL}, expecting it to be in ${PUBLIC_SUFFIX_DATA_PATH}") -endif() -invoke_py_generator( - "PublicSuffixData" - "generate_public_suffix_data.py" - "${PUBLIC_SUFFIX_PATH}/" - "${PUBLIC_SUFFIX_DATA_HEADER}" - "${PUBLIC_SUFFIX_DATA_IMPLEMENTATION}" - arguments -p "${PUBLIC_SUFFIX_DATA_PATH}" -) -set(PUBLIC_SUFFIX_SOURCES - ${PUBLIC_SUFFIX_DATA_HEADER} - ${PUBLIC_SUFFIX_DATA_IMPLEMENTATION} -) diff --git a/Meta/Generators/generate_public_suffix_data.py b/Meta/Generators/generate_public_suffix_data.py deleted file mode 100644 index e31aa69e91..0000000000 --- a/Meta/Generators/generate_public_suffix_data.py +++ /dev/null @@ -1,322 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2023, Cameron Youell -# Copyright (c) 2025, ayeteadoe -# -# SPDX-License-Identifier: BSD-2-Clause - -import argparse - -from pathlib import Path - - -def generate_header_file(output_path: Path) -> None: - content = """#pragma once - -#include -#include -#include -#include - -namespace URL { - -class PublicSuffixData { -protected: - PublicSuffixData(); - -public: - PublicSuffixData(PublicSuffixData const&) = delete; - PublicSuffixData& operator=(PublicSuffixData const&) = delete; - - static PublicSuffixData* the() - { - static PublicSuffixData* s_the; - if (!s_the) - s_the = new PublicSuffixData; - return s_the; - } - - bool is_matching_public_suffix(StringView host); - bool is_matching_public_suffix(Host const& host); - Optional find_matching_public_suffix(StringView string); - Optional find_matching_public_suffix(Host const& host); - Optional find_matching_registrable_domain(StringView string); - Optional find_matching_registrable_domain(Host const& host); - -}; - -} - -""" - - with open(output_path, "w", encoding="utf-8") as f: - f.write(content) - - -def canonicalize_public_suffix_rule(rule: str) -> str: - def canonicalize_label(label: str) -> str: - if label == "*": - return label - - prefix = "" - if label.startswith("!"): - prefix = "!" - label = label[1:] - - return prefix + label.encode("idna").decode("ascii").lower() - - return ".".join(canonicalize_label(label) for label in rule.split(".")) - - -def generate_implementation_file(input_path: Path, output_path: Path) -> None: - content = """#include -#include -#include -#include -#include -#include - -namespace URL { - -static constexpr auto s_public_suffixes = Array {""" - - reversed_lines = [] - with open(input_path, "r", encoding="utf-8") as f: - for line in f: - line = line.strip() - - if line.startswith("//") or not line: - continue - - line = canonicalize_public_suffix_rule(line) - reversed_line = ".".join(line.split(".")[::-1]) - reversed_lines.append(reversed_line) - - reversed_lines.sort() - - for item in reversed_lines: - content += f'\n "{item}"sv,' - - content += """ -}; - -PublicSuffixData::PublicSuffixData() -{ -} - -static bool is_reversed_public_suffix(StringView reversed_host) -{ - return binary_search(s_public_suffixes, reversed_host); -} - -static Optional serialized_domain(Host const& host) -{ - if (!host.is_domain()) - return OptionalNone {}; - - return host.get().bytes_as_string_view(); -} - -struct NormalizedDomain { - StringView host; - StringView trailing_dot; -}; - -static Optional normalize_domain_for_matching(StringView host) -{ - host = host.trim("."sv, TrimMode::Left); - if (host.is_empty()) - return OptionalNone {}; - - auto trailing_dot = ""sv; - if (host.ends_with('.')) { - trailing_dot = "."sv; - host = host.substring_view(0, host.length() - 1); - if (host.is_empty()) - return OptionalNone {}; - } - - return NormalizedDomain { host, trailing_dot }; -} - -static Optional normalized_domain_for_host(Host const& host) -{ - auto domain = serialized_domain(host); - if (!domain.has_value()) - return OptionalNone {}; - - return normalize_domain_for_matching(*domain); -} - -static bool is_matching_public_suffix_impl(StringView host) -{ - // Empty labels are kept so that inputs such as "com." do not match the bare "com" entry. - auto labels = host.split_view('.', SplitBehavior::KeepEmpty); - labels.reverse(); - - StringBuilder reversed_host; - reversed_host.join('.', labels); - - return is_reversed_public_suffix(reversed_host.string_view()); -} - -bool PublicSuffixData::is_matching_public_suffix(StringView host) -{ - if (host.is_empty()) - return false; - - auto parsed_host = Parser::parse_host(host); - if (!parsed_host.has_value()) - return false; - - return is_matching_public_suffix(*parsed_host); -} - -bool PublicSuffixData::is_matching_public_suffix(Host const& host) -{ - auto normalized_domain = normalized_domain_for_host(host); - if (!normalized_domain.has_value()) - return false; - - return is_matching_public_suffix_impl(normalized_domain->host); -} - -static Optional find_matching_public_suffix_impl(StringView host) -{ - auto input = host.split_view('.'); - input.reverse(); - - StringBuilder overall_search_string; - StringBuilder search_string; - for (auto part : input) { - search_string.clear(); - search_string.append(overall_search_string.string_view()); - search_string.append(part); - - if (is_reversed_public_suffix(search_string.string_view())) { - overall_search_string.append(part); - overall_search_string.append('.'); - continue; - } - - search_string.clear(); - search_string.append(overall_search_string.string_view()); - search_string.append('.'); - - if (is_reversed_public_suffix(search_string.string_view())) { - overall_search_string.append(part); - overall_search_string.append('.'); - continue; - } - - break; - } - - auto view = overall_search_string.string_view().split_view('.'); - view.reverse(); - - StringBuilder return_string_builder; - return_string_builder.join('.', view); - if (return_string_builder.is_empty()) - return Optional {}; - return MUST(return_string_builder.to_string()); -} - -Optional PublicSuffixData::find_matching_public_suffix(StringView string) -{ - if (string.is_empty()) - return {}; - - auto parsed_host = Parser::parse_host(string); - if (!parsed_host.has_value()) - return {}; - - return find_matching_public_suffix(*parsed_host); -} - -Optional PublicSuffixData::find_matching_public_suffix(Host const& host) -{ - auto normalized_domain = normalized_domain_for_host(host); - if (!normalized_domain.has_value()) - return {}; - - auto public_suffix = find_matching_public_suffix_impl(normalized_domain->host); - if (!public_suffix.has_value()) - return {}; - - return MUST(String::formatted("{}{}", public_suffix.value(), normalized_domain->trailing_dot)); -} - -// https://github.com/publicsuffix/list/wiki/Format#algorithm -static Optional find_matching_registrable_domain_impl(StringView host) -{ - // The registered or registrable domain is the public suffix plus one additional label. - auto public_suffix = find_matching_public_suffix_impl(host); - if (!public_suffix.has_value() || !host.ends_with(*public_suffix)) - return {}; - - if (host == *public_suffix) - return {}; - - auto subhost = host.substring_view(0, host.length() - public_suffix->bytes_as_string_view().length()); - subhost = subhost.trim("."sv, TrimMode::Right); - - if (subhost.is_empty()) - return {}; - - size_t start_index = 0; - if (auto index = subhost.find_last('.'); index.has_value()) - start_index = *index + 1; - - return MUST(String::from_utf8(host.substring_view(start_index))); -} - -Optional PublicSuffixData::find_matching_registrable_domain(StringView string) -{ - if (string.is_empty()) - return {}; - - auto parsed_host = Parser::parse_host(string); - if (!parsed_host.has_value()) - return {}; - - return find_matching_registrable_domain(*parsed_host); -} - -Optional PublicSuffixData::find_matching_registrable_domain(Host const& host) -{ - auto normalized_domain = normalized_domain_for_host(host); - if (!normalized_domain.has_value()) - return {}; - - auto registrable_domain = find_matching_registrable_domain_impl(normalized_domain->host); - if (!registrable_domain.has_value()) - return {}; - - return MUST(String::formatted("{}{}", registrable_domain.value(), normalized_domain->trailing_dot)); -} - -} - -""" - - with open(output_path, "w", encoding="utf-8") as f: - f.write(content) - - -def main(): - parser = argparse.ArgumentParser(description="Generate public suffix data files", add_help=False) - parser.add_argument("--help", action="help", help="Show this help message and exit") - parser.add_argument("-h", "--generated-header-path", required=True, help="Path to the header file to generate") - parser.add_argument( - "-c", "--generated-implementation-path", required=True, help="Path to the implementation file to generate" - ) - parser.add_argument("-p", "--public-suffix-list-path", required=True, help="Path to the public suffix list") - args = parser.parse_args() - - generate_header_file(Path(args.generated_header_path)) - generate_implementation_file(Path(args.public_suffix_list_path), Path(args.generated_implementation_path)) - - -if __name__ == "__main__": - main() diff --git a/Tests/LibURL/TestPublicSuffix.cpp b/Tests/LibURL/TestPublicSuffix.cpp index e547d5873c..591b0fab45 100644 --- a/Tests/LibURL/TestPublicSuffix.cpp +++ b/Tests/LibURL/TestPublicSuffix.cpp @@ -49,18 +49,16 @@ TEST_CASE(public_suffix_matching_for_psl_rules) { "www.xn--55qx5d.cn"sv, false, "xn--55qx5d.cn"sv, "www.xn--55qx5d.cn"sv }, }; - auto* public_suffix_data = URL::PublicSuffixData::the(); - for (auto const& test_case : test_cases) { - EXPECT_EQ(public_suffix_data->is_matching_public_suffix(test_case.input), test_case.is_public_suffix); - EXPECT_EQ(public_suffix_data->find_matching_public_suffix(test_case.input), test_case.public_suffix); - EXPECT_EQ(public_suffix_data->find_matching_registrable_domain(test_case.input), test_case.registrable_domain); + EXPECT_EQ(URL::PublicSuffixData::is_matching_public_suffix(test_case.input), test_case.is_public_suffix); + EXPECT_EQ(URL::PublicSuffixData::find_matching_public_suffix(test_case.input), test_case.public_suffix); + EXPECT_EQ(URL::PublicSuffixData::find_matching_registrable_domain(test_case.input), test_case.registrable_domain); auto host = URL::Parser::parse_host(test_case.input); VERIFY(host.has_value()); - EXPECT_EQ(public_suffix_data->is_matching_public_suffix(*host), test_case.is_public_suffix); - EXPECT_EQ(public_suffix_data->find_matching_public_suffix(*host), test_case.public_suffix); - EXPECT_EQ(public_suffix_data->find_matching_registrable_domain(*host), test_case.registrable_domain); + EXPECT_EQ(URL::PublicSuffixData::is_matching_public_suffix(*host), test_case.is_public_suffix); + EXPECT_EQ(URL::PublicSuffixData::find_matching_public_suffix(*host), test_case.public_suffix); + EXPECT_EQ(URL::PublicSuffixData::find_matching_registrable_domain(*host), test_case.registrable_domain); EXPECT_EQ(host->public_suffix(), test_case.public_suffix); EXPECT_EQ(host->registrable_domain(), test_case.registrable_domain); } @@ -90,18 +88,16 @@ TEST_CASE(public_suffix_matching_without_psl_rule) { "sub.example.إختبار"sv, "xn--kgbechtv"sv, "example.xn--kgbechtv"sv }, }; - auto* public_suffix_data = URL::PublicSuffixData::the(); - for (auto const& test_case : test_cases) { - EXPECT(!public_suffix_data->is_matching_public_suffix(test_case.input)); - EXPECT_EQ(public_suffix_data->find_matching_public_suffix(test_case.input), OptionalNone {}); - EXPECT_EQ(public_suffix_data->find_matching_registrable_domain(test_case.input), OptionalNone {}); + EXPECT(!URL::PublicSuffixData::is_matching_public_suffix(test_case.input)); + EXPECT_EQ(URL::PublicSuffixData::find_matching_public_suffix(test_case.input), OptionalNone {}); + EXPECT_EQ(URL::PublicSuffixData::find_matching_registrable_domain(test_case.input), OptionalNone {}); auto host = URL::Parser::parse_host(test_case.input); VERIFY(host.has_value()); - EXPECT(!public_suffix_data->is_matching_public_suffix(*host)); - EXPECT_EQ(public_suffix_data->find_matching_public_suffix(*host), OptionalNone {}); - EXPECT_EQ(public_suffix_data->find_matching_registrable_domain(*host), OptionalNone {}); + EXPECT(!URL::PublicSuffixData::is_matching_public_suffix(*host)); + EXPECT_EQ(URL::PublicSuffixData::find_matching_public_suffix(*host), OptionalNone {}); + EXPECT_EQ(URL::PublicSuffixData::find_matching_registrable_domain(*host), OptionalNone {}); EXPECT_EQ(host->public_suffix(), test_case.host_public_suffix); EXPECT_EQ(host->registrable_domain(), test_case.host_registrable_domain); } @@ -119,14 +115,12 @@ TEST_CASE(invalid_hosts) }; // Above inputs are not valid hosts, so should not be able to be parsed or matched in the PSL. - auto* public_suffix_data = URL::PublicSuffixData::the(); - for (auto const& input : raw_invalid_inputs) { auto host = URL::Parser::parse_host(input); EXPECT(!host.has_value()); - EXPECT(!public_suffix_data->is_matching_public_suffix(input)); - EXPECT_EQ(public_suffix_data->find_matching_public_suffix(input), OptionalNone {}); - EXPECT_EQ(public_suffix_data->find_matching_registrable_domain(input), OptionalNone {}); + EXPECT(!URL::PublicSuffixData::is_matching_public_suffix(input)); + EXPECT_EQ(URL::PublicSuffixData::find_matching_public_suffix(input), OptionalNone {}); + EXPECT_EQ(URL::PublicSuffixData::find_matching_registrable_domain(input), OptionalNone {}); } } @@ -137,18 +131,16 @@ TEST_CASE(public_suffix_matching_for_ip_addresses) "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"sv, }; - auto* public_suffix_data = URL::PublicSuffixData::the(); - for (auto const& input : test_cases) { - EXPECT(!public_suffix_data->is_matching_public_suffix(input)); - EXPECT_EQ(public_suffix_data->find_matching_public_suffix(input), OptionalNone {}); - EXPECT_EQ(public_suffix_data->find_matching_registrable_domain(input), OptionalNone {}); + EXPECT(!URL::PublicSuffixData::is_matching_public_suffix(input)); + EXPECT_EQ(URL::PublicSuffixData::find_matching_public_suffix(input), OptionalNone {}); + EXPECT_EQ(URL::PublicSuffixData::find_matching_registrable_domain(input), OptionalNone {}); auto host = URL::Parser::parse_host(input); VERIFY(host.has_value()); - EXPECT(!public_suffix_data->is_matching_public_suffix(*host)); - EXPECT_EQ(public_suffix_data->find_matching_public_suffix(*host), OptionalNone {}); - EXPECT_EQ(public_suffix_data->find_matching_registrable_domain(*host), OptionalNone {}); + EXPECT(!URL::PublicSuffixData::is_matching_public_suffix(*host)); + EXPECT_EQ(URL::PublicSuffixData::find_matching_public_suffix(*host), OptionalNone {}); + EXPECT_EQ(URL::PublicSuffixData::find_matching_registrable_domain(*host), OptionalNone {}); EXPECT_EQ(host->public_suffix(), OptionalNone {}); EXPECT_EQ(host->registrable_domain(), OptionalNone {}); } diff --git a/vcpkg.json b/vcpkg.json index 7494963a9f..d288728e80 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -99,6 +99,7 @@ "name": "libproxy", "platform": "!(android | bsd)" }, + "libpsl", { "name": "libedit", "platform": "!windows & !android" @@ -349,6 +350,10 @@ "name": "libproxy", "version": "0.4.18#3" }, + { + "name": "libpsl", + "version": "0.21.5#1" + }, { "name": "libtommath", "version": "1.3.0#2"