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.
This commit is contained in:
Shannon Booth 2026-06-08 23:20:48 +02:00 committed by Jelle Raaijmakers
parent ec3ea7ed25
commit 5ef34ecb7a
13 changed files with 250 additions and 390 deletions

View file

@ -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;

View file

@ -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)

View file

@ -199,7 +199,7 @@ Optional<String> 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())

View file

@ -0,0 +1,165 @@
/*
* Copyright (c) 2026-present, the Ladybird developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/ByteString.h>
#include <AK/String.h>
#include <LibURL/Host.h>
#include <LibURL/Parser.h>
#include <LibURL/PublicSuffixData.h>
#include <libpsl.h>
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<NormalizedDomain> normalized_domain_for_host(Host const& host)
{
if (!host.is_domain())
return OptionalNone {};
auto domain = host.get<String>().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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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));
}
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2026-present, the Ladybird developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Forward.h>
#include <AK/Optional.h>
#include <LibURL/Forward.h>
namespace URL {
class PublicSuffixData {
public:
static bool is_matching_public_suffix(StringView host);
static bool is_matching_public_suffix(Host const& host);
static Optional<String> find_matching_public_suffix(StringView string);
static Optional<String> find_matching_public_suffix(Host const& host);
static Optional<String> find_matching_registrable_domain(StringView string);
static Optional<String> find_matching_registrable_domain(Host const& host);
};
}

View file

@ -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.

View file

@ -63,7 +63,7 @@ Optional<URL::URL> sanitize_url(StringView location, Optional<SearchEngine> 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 };

View file

@ -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)

View file

@ -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",

View file

@ -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}
)

View file

@ -1,322 +0,0 @@
#!/usr/bin/env python3
# Copyright (c) 2023, Cameron Youell <cameronyouell@gmail.com>
# Copyright (c) 2025, ayeteadoe <ayeteadoe@gmail.com>
#
# SPDX-License-Identifier: BSD-2-Clause
import argparse
from pathlib import Path
def generate_header_file(output_path: Path) -> None:
content = """#pragma once
#include <AK/Forward.h>
#include <AK/Trie.h>
#include <AK/Variant.h>
#include <LibURL/Forward.h>
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<String> find_matching_public_suffix(StringView string);
Optional<String> find_matching_public_suffix(Host const& host);
Optional<String> find_matching_registrable_domain(StringView string);
Optional<String> 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 <AK/String.h>
#include <AK/BinarySearch.h>
#include <AK/Vector.h>
#include <LibURL/Host.h>
#include <LibURL/Parser.h>
#include <LibURL/PublicSuffixData.h>
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<StringView> serialized_domain(Host const& host)
{
if (!host.is_domain())
return OptionalNone {};
return host.get<String>().bytes_as_string_view();
}
struct NormalizedDomain {
StringView host;
StringView trailing_dot;
};
static Optional<NormalizedDomain> 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<NormalizedDomain> 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<String> 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<String> {};
return MUST(return_string_builder.to_string());
}
Optional<String> 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<String> 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<String> 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<String> 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<String> 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()

View file

@ -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 {});
}

View file

@ -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"