LibURL/Pattern: Implement matching a URLPattern

This commit is contained in:
Shannon Booth 2025-03-18 20:05:14 +13:00 committed by Tim Flynn
commit 6b1fa3ecd0
Notes: github-actions[bot] 2025-04-06 12:27:06 +00:00
3 changed files with 270 additions and 46 deletions

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibURL/Parser.h>
#include <LibURL/Pattern/Canonicalization.h>
#include <LibURL/Pattern/ConstructorStringParser.h>
#include <LibURL/Pattern/Pattern.h>
@ -163,10 +164,232 @@ PatternErrorOr<Pattern> Pattern::create(Input const& input, Optional<String> con
}
// https://urlpattern.spec.whatwg.org/#url-pattern-match
PatternErrorOr<Optional<Result>> Pattern::match(Input const&, Optional<String> const&) const
PatternErrorOr<Optional<Result>> Pattern::match(Variant<String, Init, URL> const& input, Optional<String> const& base_url_string) const
{
dbgln("FIXME: Implement URL::Pattern::match");
return OptionalNone {};
// 1. Let protocol be the empty string.
String protocol;
// 2. Let username be the empty string.
String username;
// 3. Let password be the empty string.
String password;
// 4. Let hostname be the empty string.
String hostname;
// 5. Let port be the empty string.
String port;
// 6. Let pathname be the empty string.
String pathname;
// 7. Let search be the empty string.
String search;
// 8. Let hash be the empty string.
String hash;
// 9. Let inputs be an empty list.
Vector<Input> inputs;
// 10. If input is a URL, then append the serialization of input to inputs.
if (auto const* input_url = input.get_pointer<URL>()) {
inputs.append(input_url->serialize());
}
// 11. Otherwise, append input to inputs.
else {
inputs.append(input.downcast<Input>());
}
// 12. If input is a URLPatternInit then:
if (auto const* input_init = input.get_pointer<Init>()) {
// 1. If baseURLString was given, throw a TypeError.
if (base_url_string.has_value())
return ErrorInfo { "Base URL cannot be provided when URLPatternInput is provided"_string };
// 2. Let applyResult be the result of process a URLPatternInit given input, "url", protocol, username, password,
// hostname, port, pathname, search, and hash. If this throws an exception, catch it, and return null.
auto apply_result_or_error = process_a_url_pattern_init(*input_init, PatternProcessType::URL,
protocol, username, password, hostname, port, pathname, search, hash);
if (apply_result_or_error.is_error())
return OptionalNone {};
auto apply_result = apply_result_or_error.release_value();
// 3. Set protocol to applyResult["protocol"].
protocol = apply_result.protocol.value();
// 4. Set username to applyResult["username"].
username = apply_result.username.value();
// 5. Set password to applyResult["password"].
password = apply_result.password.value();
// 6. Set hostname to applyResult["hostname"].
hostname = apply_result.hostname.value();
// 7. Set port to applyResult["port"].
port = apply_result.port.value();
// 8. Set pathname to applyResult["pathname"].
pathname = apply_result.pathname.value();
// 9. Set search to applyResult["search"].
search = apply_result.search.value();
// 10. Set hash to applyResult["hash"].
hash = apply_result.hash.value();
}
// 13. Otherwise:
else {
// 1. Let url be input.
auto url_or_string = input.downcast<URL, String>();
// 2. If input is a USVString:
if (auto const* input_string = input.get_pointer<String>()) {
// 1. Let baseURL be null.
Optional<URL> base_url;
// 2. If baseURLString was given, then:
if (base_url_string.has_value()) {
// 1. Set baseURL to the result of running the basic URL parser on baseURLString.
base_url = Parser::basic_parse(base_url_string.value());
// 2. If baseURL is failure, return null.
if (!base_url.has_value())
return OptionalNone {};
// 3. Append baseURLString to inputs.
inputs.append(base_url_string.value());
}
// 3. Set url to the result of running the basic URL parser on input with baseURL.
// 4. If url is failure, return null.
auto maybe_url = Parser::basic_parse(*input_string, base_url);
if (!maybe_url.has_value())
return OptionalNone {};
url_or_string = maybe_url.release_value();
}
// 3. Assert: url is a URL.
VERIFY(url_or_string.has<URL>());
auto& url = url_or_string.get<URL>();
// 4. Set protocol to urls scheme.
protocol = url.scheme();
// 5. Set username to urls username.
username = url.username();
// 6. Set password to urls password.
password = url.password();
// 7. Set hostname to urls host, serialized, or the empty string if the value is null.
if (url.host().has_value())
hostname = url.host()->serialize();
else
hostname = String {};
// 8. Set port to urls port, serialized, or the empty string if the value is null.
if (url.port().has_value())
port = String::number(url.port().value());
else
port = String {};
// 9. Set pathname to the result of URL path serializing url.
pathname = url.serialize_path();
// 10. Set search to urls query or the empty string if the value is null.
search = url.query().value_or(String {});
// 11. Set hash to urls fragment or the empty string if the value is null.
hash = url.fragment().value_or(String {});
}
// 14. Let protocolExecResult be RegExpBuiltinExec(urlPatterns protocol component's regular expression, protocol).
auto protocol_exec_result = m_protocol_component.regular_expression->match(protocol);
if (!protocol_exec_result.success)
return OptionalNone {};
// 15. Let usernameExecResult be RegExpBuiltinExec(urlPatterns username component's regular expression, username).
auto username_exec_result = m_username_component.regular_expression->match(username);
if (!username_exec_result.success)
return OptionalNone {};
// 16. Let passwordExecResult be RegExpBuiltinExec(urlPatterns password component's regular expression, password).
auto password_exec_result = m_password_component.regular_expression->match(password);
if (!password_exec_result.success)
return OptionalNone {};
// 17. Let hostnameExecResult be RegExpBuiltinExec(urlPatterns hostname component's regular expression, hostname).
auto hostname_exec_result = m_hostname_component.regular_expression->match(hostname);
if (!hostname_exec_result.success)
return OptionalNone {};
// 18. Let portExecResult be RegExpBuiltinExec(urlPatterns port component's regular expression, port).
auto port_exec_result = m_port_component.regular_expression->match(port);
if (!port_exec_result.success)
return OptionalNone {};
// 19. Let pathnameExecResult be RegExpBuiltinExec(urlPatterns pathname component's regular expression, pathname).
auto pathname_exec_result = m_pathname_component.regular_expression->match(pathname);
if (!pathname_exec_result.success)
return OptionalNone {};
// 20. Let searchExecResult be RegExpBuiltinExec(urlPatterns search component's regular expression, search).
auto search_exec_result = m_search_component.regular_expression->match(search);
if (!search_exec_result.success)
return OptionalNone {};
// 21. Let hashExecResult be RegExpBuiltinExec(urlPatterns hash component's regular expression, hash).
auto hash_exec_result = m_hash_component.regular_expression->match(hash);
if (!hash_exec_result.success)
return OptionalNone {};
// 22. If protocolExecResult, usernameExecResult, passwordExecResult, hostnameExecResult, portExecResult,
// pathnameExecResult, searchExecResult, or hashExecResult are null then return null.
// NOTE: Done in steps above at point of exec.
// 23. Let result be a new URLPatternResult.
Result result;
// 24. Set result["inputs"] to inputs.
result.inputs = move(inputs);
// 25. Set result["protocol"] to the result of creating a component match result given urlPatterns protocol
// component, protocol, and protocolExecResult.
result.protocol = m_protocol_component.create_match_result(protocol, protocol_exec_result);
// 26. Set result["username"] to the result of creating a component match result given urlPatterns username
// component, username, and usernameExecResult.
result.username = m_username_component.create_match_result(username, username_exec_result);
// 27. Set result["password"] to the result of creating a component match result given urlPatterns password
// component, password, and passwordExecResult.
result.password = m_password_component.create_match_result(password, password_exec_result);
// 28. Set result["hostname"] to the result of creating a component match result given urlPatterns hostname
// component, hostname, and hostnameExecResult.
result.hostname = m_hostname_component.create_match_result(hostname, hostname_exec_result);
// 29. Set result["port"] to the result of creating a component match result given urlPatterns port component,
// port, and portExecResult.
result.port = m_port_component.create_match_result(port, port_exec_result);
// 30. Set result["pathname"] to the result of creating a component match result given urlPatterns pathname
// component, pathname, and pathnameExecResult.
result.pathname = m_pathname_component.create_match_result(pathname, pathname_exec_result);
// 31. Set result["search"] to the result of creating a component match result given urlPatterns search component,
// search, and searchExecResult.
result.search = m_search_component.create_match_result(search, search_exec_result);
// 32. Set result["hash"] to the result of creating a component match result given urlPatterns hash component,
// hash, and hashExecResult.
result.hash = m_hash_component.create_match_result(hash, hash_exec_result);
// 33. Return result.
return result;
}
// https://urlpattern.spec.whatwg.org/#url-pattern-has-regexp-groups

View file

@ -13,6 +13,7 @@
#include <LibURL/Pattern/Component.h>
#include <LibURL/Pattern/Init.h>
#include <LibURL/Pattern/PatternError.h>
#include <LibURL/URL.h>
namespace URL::Pattern {
@ -44,7 +45,7 @@ class Pattern {
public:
static PatternErrorOr<Pattern> create(Input const&, Optional<String> const& base_url = {}, IgnoreCase = IgnoreCase::No);
PatternErrorOr<Optional<Result>> match(Input const&, Optional<String> const& base_url_string) const;
PatternErrorOr<Optional<Result>> match(Variant<String, Init, URL> const&, Optional<String> const& base_url_string) const;
bool has_regexp_groups() const;