1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 14:27:35 +00:00

LibWeb/URL: Add spec links and comments

This commit is contained in:
Linus Groh 2023-04-12 23:20:27 +02:00 committed by Andreas Kling
parent 6e14dde97d
commit 28cf8ba92e

View file

@ -110,131 +110,156 @@ bool URL::can_parse(JS::VM&, String const& url, Optional<String> const& base)
return true; return true;
} }
// https://url.spec.whatwg.org/#dom-url-href
WebIDL::ExceptionOr<String> URL::href() const WebIDL::ExceptionOr<String> URL::href() const
{ {
auto& vm = realm().vm(); auto& vm = realm().vm();
// return the serialization of thiss URL. // The href getter steps and the toJSON() method steps are to return the serialization of thiss URL.
return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(m_url.serialize())); return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(m_url.serialize()));
} }
// https://url.spec.whatwg.org/#dom-url-tojson
WebIDL::ExceptionOr<String> URL::to_json() const WebIDL::ExceptionOr<String> URL::to_json() const
{ {
auto& vm = realm().vm(); auto& vm = realm().vm();
// return the serialization of thiss URL. // The href getter steps and the toJSON() method steps are to return the serialization of thiss URL.
return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(m_url.serialize())); return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(m_url.serialize()));
} }
// https://url.spec.whatwg.org/#ref-for-dom-url-href②
WebIDL::ExceptionOr<void> URL::set_href(String const& href) WebIDL::ExceptionOr<void> URL::set_href(String const& href)
{ {
auto& vm = realm().vm(); auto& vm = realm().vm();
// 1. Let parsedURL be the result of running the basic URL parser on the given value. // 1. Let parsedURL be the result of running the basic URL parser on the given value.
AK::URL parsed_url = href; AK::URL parsed_url = href;
// 2. If parsedURL is failure, then throw a TypeError. // 2. If parsedURL is failure, then throw a TypeError.
if (!parsed_url.is_valid()) if (!parsed_url.is_valid())
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Invalid URL"sv }; return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Invalid URL"sv };
// 3. Set thiss URL to parsedURL. // 3. Set thiss URL to parsedURL.
m_url = move(parsed_url); m_url = move(parsed_url);
// 4. Empty thiss query objects list. // 4. Empty thiss query objects list.
m_query->m_list.clear(); m_query->m_list.clear();
// 5. Let query be thiss URLs query. // 5. Let query be thiss URLs query.
auto& query = m_url.query(); auto& query = m_url.query();
// 6. If query is non-null, then set thiss query objects list to the result of parsing query. // 6. If query is non-null, then set thiss query objects list to the result of parsing query.
if (!query.is_null()) if (!query.is_null())
m_query->m_list = TRY_OR_THROW_OOM(vm, url_decode(query)); m_query->m_list = TRY_OR_THROW_OOM(vm, url_decode(query));
return {}; return {};
} }
// https://url.spec.whatwg.org/#dom-url-origin
WebIDL::ExceptionOr<String> URL::origin() const WebIDL::ExceptionOr<String> URL::origin() const
{ {
auto& vm = realm().vm(); auto& vm = realm().vm();
// return the serialization of thiss URLs origin. // The origin getter steps are to return the serialization of thiss URLs origin. [HTML]
return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(m_url.serialize_origin())); return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(m_url.serialize_origin()));
} }
// https://url.spec.whatwg.org/#dom-url-protocol
WebIDL::ExceptionOr<String> URL::protocol() const WebIDL::ExceptionOr<String> URL::protocol() const
{ {
auto& vm = realm().vm(); auto& vm = realm().vm();
// return thiss URLs scheme, followed by U+003A (:). // The protocol getter steps are to return thiss URLs scheme, followed by U+003A (:).
return TRY_OR_THROW_OOM(vm, String::formatted("{}:", m_url.scheme())); return TRY_OR_THROW_OOM(vm, String::formatted("{}:", m_url.scheme()));
} }
// https://url.spec.whatwg.org/#ref-for-dom-url-protocol%E2%91%A0
WebIDL::ExceptionOr<void> URL::set_protocol(String const& protocol) WebIDL::ExceptionOr<void> URL::set_protocol(String const& protocol)
{ {
auto& vm = realm().vm(); auto& vm = realm().vm();
// basic URL parse the given value, followed by U+003A (:), with thiss URL as url and scheme start state as state override. // The protocol setter steps are to basic URL parse the given value, followed by U+003A (:), with thiss URL as
// url and scheme start state as state override.
auto result_url = URLParser::parse(TRY_OR_THROW_OOM(vm, String::formatted("{}:", protocol)), {}, m_url, URLParser::State::SchemeStart); auto result_url = URLParser::parse(TRY_OR_THROW_OOM(vm, String::formatted("{}:", protocol)), {}, m_url, URLParser::State::SchemeStart);
if (result_url.is_valid()) if (result_url.is_valid())
m_url = move(result_url); m_url = move(result_url);
return {}; return {};
} }
// https://url.spec.whatwg.org/#dom-url-username
WebIDL::ExceptionOr<String> URL::username() const WebIDL::ExceptionOr<String> URL::username() const
{ {
auto& vm = realm().vm(); auto& vm = realm().vm();
// return thiss URLs username. // The username getter steps are to return thiss URLs username.
return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(m_url.username())); return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(m_url.username()));
} }
// https://url.spec.whatwg.org/#ref-for-dom-url-username%E2%91%A0
void URL::set_username(String const& username) void URL::set_username(String const& username)
{ {
// 1. If thiss URL cannot have a username/password/port, then return. // 1. If thiss URL cannot have a username/password/port, then return.
if (m_url.cannot_have_a_username_or_password_or_port()) if (m_url.cannot_have_a_username_or_password_or_port())
return; return;
// 2. Set the username given thiss URL and the given value. // 2. Set the username given thiss URL and the given value.
m_url.set_username(AK::URL::percent_encode(username, AK::URL::PercentEncodeSet::Userinfo)); m_url.set_username(AK::URL::percent_encode(username, AK::URL::PercentEncodeSet::Userinfo));
} }
// https://url.spec.whatwg.org/#dom-url-password
WebIDL::ExceptionOr<String> URL::password() const WebIDL::ExceptionOr<String> URL::password() const
{ {
auto& vm = realm().vm(); auto& vm = realm().vm();
// return thiss URLs password. // The password getter steps are to return thiss URLs password.
return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(m_url.password())); return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(m_url.password()));
} }
// https://url.spec.whatwg.org/#ref-for-dom-url-password%E2%91%A0
void URL::set_password(String const& password) void URL::set_password(String const& password)
{ {
// 1. If thiss URL cannot have a username/password/port, then return. // 1. If thiss URL cannot have a username/password/port, then return.
if (m_url.cannot_have_a_username_or_password_or_port()) if (m_url.cannot_have_a_username_or_password_or_port())
return; return;
// 2. Set the password given thiss URL and the given value. // 2. Set the password given thiss URL and the given value.
m_url.set_password(AK::URL::percent_encode(password, AK::URL::PercentEncodeSet::Userinfo)); m_url.set_password(AK::URL::percent_encode(password, AK::URL::PercentEncodeSet::Userinfo));
} }
// https://url.spec.whatwg.org/#dom-url-host
WebIDL::ExceptionOr<String> URL::host() const WebIDL::ExceptionOr<String> URL::host() const
{ {
auto& vm = realm().vm(); auto& vm = realm().vm();
// 1. Let url be thiss URL. // 1. Let url be thiss URL.
auto& url = m_url; auto& url = m_url;
// 2. If urls host is null, then return the empty string. // 2. If urls host is null, then return the empty string.
if (url.host().is_null()) if (url.host().is_null())
return String {}; return String {};
// 3. If urls port is null, return urls host, serialized. // 3. If urls port is null, return urls host, serialized.
if (!url.port().has_value()) if (!url.port().has_value())
return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(url.host())); return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(url.host()));
// 4. Return urls host, serialized, followed by U+003A (:) and urls port, serialized. // 4. Return urls host, serialized, followed by U+003A (:) and urls port, serialized.
return TRY_OR_THROW_OOM(vm, String::formatted("{}:{}", url.host(), *url.port())); return TRY_OR_THROW_OOM(vm, String::formatted("{}:{}", url.host(), *url.port()));
} }
// https://url.spec.whatwg.org/#dom-url-hostref-for-dom-url-host%E2%91%A0
void URL::set_host(String const& host) void URL::set_host(String const& host)
{ {
// 1. If thiss URLs cannot-be-a-base-URL is true, then return. // 1. If thiss URLs cannot-be-a-base-URL is true, then return.
if (m_url.cannot_be_a_base_url()) if (m_url.cannot_be_a_base_url())
return; return;
// 2. Basic URL parse the given value with thiss URL as url and host state as state override. // 2. Basic URL parse the given value with thiss URL as url and host state as state override.
auto result_url = URLParser::parse(host, {}, m_url, URLParser::State::Host); auto result_url = URLParser::parse(host, {}, m_url, URLParser::State::Host);
if (result_url.is_valid()) if (result_url.is_valid())
m_url = move(result_url); m_url = move(result_url);
} }
// https://url.spec.whatwg.org/#dom-url-hostname
WebIDL::ExceptionOr<String> URL::hostname() const WebIDL::ExceptionOr<String> URL::hostname() const
{ {
auto& vm = realm().vm(); auto& vm = realm().vm();
@ -242,21 +267,25 @@ WebIDL::ExceptionOr<String> URL::hostname() const
// 1. If thiss URLs host is null, then return the empty string. // 1. If thiss URLs host is null, then return the empty string.
if (m_url.host().is_null()) if (m_url.host().is_null())
return String {}; return String {};
// 2. Return thiss URLs host, serialized. // 2. Return thiss URLs host, serialized.
return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(m_url.host())); return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(m_url.host()));
} }
// https://url.spec.whatwg.org/#ref-for-dom-url-hostname①
void URL::set_hostname(String const& hostname) void URL::set_hostname(String const& hostname)
{ {
// 1. If thiss URLs cannot-be-a-base-URL is true, then return. // 1. If thiss URLs cannot-be-a-base-URL is true, then return.
if (m_url.cannot_be_a_base_url()) if (m_url.cannot_be_a_base_url())
return; return;
// 2. Basic URL parse the given value with thiss URL as url and hostname state as state override. // 2. Basic URL parse the given value with thiss URL as url and hostname state as state override.
auto result_url = URLParser::parse(hostname, {}, m_url, URLParser::State::Hostname); auto result_url = URLParser::parse(hostname, {}, m_url, URLParser::State::Hostname);
if (result_url.is_valid()) if (result_url.is_valid())
m_url = move(result_url); m_url = move(result_url);
} }
// https://url.spec.whatwg.org/#ref-for-dom-url-hostname①
WebIDL::ExceptionOr<String> URL::port() const WebIDL::ExceptionOr<String> URL::port() const
{ {
auto& vm = realm().vm(); auto& vm = realm().vm();
@ -269,6 +298,7 @@ WebIDL::ExceptionOr<String> URL::port() const
return TRY_OR_THROW_OOM(vm, String::formatted("{}", *m_url.port())); return TRY_OR_THROW_OOM(vm, String::formatted("{}", *m_url.port()));
} }
// https://url.spec.whatwg.org/#ref-for-dom-url-port%E2%91%A0
void URL::set_port(String const& port) void URL::set_port(String const& port)
{ {
// 1. If thiss URL cannot have a username/password/port, then return. // 1. If thiss URL cannot have a username/password/port, then return.
@ -278,39 +308,43 @@ void URL::set_port(String const& port)
// 2. If the given value is the empty string, then set thiss URLs port to null. // 2. If the given value is the empty string, then set thiss URLs port to null.
if (port.is_empty()) { if (port.is_empty()) {
m_url.set_port({}); m_url.set_port({});
return;
} }
// 3. Otherwise, basic URL parse the given value with thiss URL as url and port state as state override. // 3. Otherwise, basic URL parse the given value with thiss URL as url and port state as state override.
auto result_url = URLParser::parse(port, {}, m_url, URLParser::State::Port); else {
if (result_url.is_valid()) auto result_url = URLParser::parse(port, {}, m_url, URLParser::State::Port);
m_url = move(result_url); if (result_url.is_valid())
m_url = move(result_url);
}
} }
// https://url.spec.whatwg.org/#dom-url-pathname
WebIDL::ExceptionOr<String> URL::pathname() const WebIDL::ExceptionOr<String> URL::pathname() const
{ {
auto& vm = realm().vm(); auto& vm = realm().vm();
// 1. If thiss URLs cannot-be-a-base-URL is true, then return thiss URLs path[0]. // The pathname getter steps are to return the result of URL path serializing thiss URL.
// 2. If thiss URLs path is empty, then return the empty string.
// 3. Return U+002F (/), followed by the strings in thiss URLs path (including empty strings), if any, separated from each other by U+002F (/).
return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(m_url.path())); return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(m_url.path()));
} }
// https://url.spec.whatwg.org/#ref-for-dom-url-pathname%E2%91%A0
void URL::set_pathname(String const& pathname) void URL::set_pathname(String const& pathname)
{ {
// FIXME: These steps no longer match the speci.
// 1. If thiss URLs cannot-be-a-base-URL is true, then return. // 1. If thiss URLs cannot-be-a-base-URL is true, then return.
if (m_url.cannot_be_a_base_url()) if (m_url.cannot_be_a_base_url())
return; return;
// 2. Empty thiss URLs path. // 2. Empty thiss URLs path.
auto url = m_url; // We copy the URL here to follow other browser's behaviour of reverting the path change if the parse failed. auto url = m_url; // We copy the URL here to follow other browser's behaviour of reverting the path change if the parse failed.
url.set_paths({}); url.set_paths({});
// 3. Basic URL parse the given value with thiss URL as url and path start state as state override. // 3. Basic URL parse the given value with thiss URL as url and path start state as state override.
auto result_url = URLParser::parse(pathname, {}, move(url), URLParser::State::PathStart); auto result_url = URLParser::parse(pathname, {}, move(url), URLParser::State::PathStart);
if (result_url.is_valid()) if (result_url.is_valid())
m_url = move(result_url); m_url = move(result_url);
} }
// https://url.spec.whatwg.org/#dom-url-search
WebIDL::ExceptionOr<String> URL::search() const WebIDL::ExceptionOr<String> URL::search() const
{ {
auto& vm = realm().vm(); auto& vm = realm().vm();
@ -318,44 +352,61 @@ WebIDL::ExceptionOr<String> URL::search() const
// 1. If thiss URLs query is either null or the empty string, then return the empty string. // 1. If thiss URLs query is either null or the empty string, then return the empty string.
if (m_url.query().is_null() || m_url.query().is_empty()) if (m_url.query().is_null() || m_url.query().is_empty())
return String {}; return String {};
// 2. Return U+003F (?), followed by thiss URLs query. // 2. Return U+003F (?), followed by thiss URLs query.
return TRY_OR_THROW_OOM(vm, String::formatted("?{}", m_url.query())); return TRY_OR_THROW_OOM(vm, String::formatted("?{}", m_url.query()));
} }
// https://url.spec.whatwg.org/#ref-for-dom-url-search%E2%91%A0
WebIDL::ExceptionOr<void> URL::set_search(String const& search) WebIDL::ExceptionOr<void> URL::set_search(String const& search)
{ {
auto& vm = realm().vm(); auto& vm = realm().vm();
// 1. Let url be thiss URL. // 1. Let url be thiss URL.
auto& url = m_url; auto& url = m_url;
// If the given value is the empty string, set urls query to null, empty thiss query objects list, and then return.
// 2. If the given value is the empty string:
if (search.is_empty()) { if (search.is_empty()) {
// 1. Set urls query to null.
url.set_query({}); url.set_query({});
// 2. Empty thiss query objects list.
m_query->m_list.clear(); m_query->m_list.clear();
// FIXME: 3. Potentially strip trailing spaces from an opaque path with this.
// 4. Return.
return {}; return {};
} }
// 2. Let input be the given value with a single leading U+003F (?) removed, if any.
// 3. Let input be the given value with a single leading U+003F (?) removed, if any.
auto search_as_string_view = search.bytes_as_string_view(); auto search_as_string_view = search.bytes_as_string_view();
auto input = search_as_string_view.substring_view(search_as_string_view.starts_with('?')); auto input = search_as_string_view.substring_view(search_as_string_view.starts_with('?'));
// 3. Set urls query to the empty string.
// 4. Set urls query to the empty string.
auto url_copy = url; // We copy the URL here to follow other browser's behaviour of reverting the search change if the parse failed. auto url_copy = url; // We copy the URL here to follow other browser's behaviour of reverting the search change if the parse failed.
url_copy.set_query(DeprecatedString::empty()); url_copy.set_query(DeprecatedString::empty());
// 4. Basic URL parse input with url as url and query state as state override.
// 5. Basic URL parse input with url as url and query state as state override.
auto result_url = URLParser::parse(input, {}, move(url_copy), URLParser::State::Query); auto result_url = URLParser::parse(input, {}, move(url_copy), URLParser::State::Query);
if (result_url.is_valid()) { if (result_url.is_valid()) {
m_url = move(result_url); m_url = move(result_url);
// 5. Set thiss query objects list to the result of parsing input.
// 6. Set thiss query objects list to the result of parsing input.
m_query->m_list = TRY_OR_THROW_OOM(vm, url_decode(input)); m_query->m_list = TRY_OR_THROW_OOM(vm, url_decode(input));
} }
return {}; return {};
} }
// https://url.spec.whatwg.org/#dom-url-searchparams
URLSearchParams const* URL::search_params() const URLSearchParams const* URL::search_params() const
{ {
// The searchParams getter steps are to return thiss query object.
return m_query; return m_query;
} }
// https://url.spec.whatwg.org/#dom-url-hash
WebIDL::ExceptionOr<String> URL::hash() const WebIDL::ExceptionOr<String> URL::hash() const
{ {
auto& vm = realm().vm(); auto& vm = realm().vm();
@ -363,23 +414,33 @@ WebIDL::ExceptionOr<String> URL::hash() const
// 1. If thiss URLs fragment is either null or the empty string, then return the empty string. // 1. If thiss URLs fragment is either null or the empty string, then return the empty string.
if (m_url.fragment().is_null() || m_url.fragment().is_empty()) if (m_url.fragment().is_null() || m_url.fragment().is_empty())
return String {}; return String {};
// 2. Return U+0023 (#), followed by thiss URLs fragment. // 2. Return U+0023 (#), followed by thiss URLs fragment.
return TRY_OR_THROW_OOM(vm, String::formatted("#{}", m_url.fragment())); return TRY_OR_THROW_OOM(vm, String::formatted("#{}", m_url.fragment()));
} }
// https://url.spec.whatwg.org/#ref-for-dom-url-hash%E2%91%A0
void URL::set_hash(String const& hash) void URL::set_hash(String const& hash)
{ {
// 1. If the given value is the empty string, then set thiss URLs fragment to null and return. // 1. If the given value is the empty string:
if (hash.is_empty()) { if (hash.is_empty()) {
// 1. Set thiss URLs fragment to null.
m_url.set_fragment({}); m_url.set_fragment({});
// FIXME: 2. Potentially strip trailing spaces from an opaque path with this.
// 3. Return.
return; return;
} }
// 2. Let input be the given value with a single leading U+0023 (#) removed, if any. // 2. Let input be the given value with a single leading U+0023 (#) removed, if any.
auto hash_as_string_view = hash.bytes_as_string_view(); auto hash_as_string_view = hash.bytes_as_string_view();
auto input = hash_as_string_view.substring_view(hash_as_string_view.starts_with('#')); auto input = hash_as_string_view.substring_view(hash_as_string_view.starts_with('#'));
// 3. Set thiss URLs fragment to the empty string. // 3. Set thiss URLs fragment to the empty string.
auto url = m_url; // We copy the URL here to follow other browser's behaviour of reverting the hash change if the parse failed. auto url = m_url; // We copy the URL here to follow other browser's behaviour of reverting the hash change if the parse failed.
url.set_fragment(DeprecatedString::empty()); url.set_fragment(DeprecatedString::empty());
// 4. Basic URL parse input with thiss URL as url and fragment state as state override. // 4. Basic URL parse input with thiss URL as url and fragment state as state override.
auto result_url = URLParser::parse(input, {}, move(url), URLParser::State::Fragment); auto result_url = URLParser::parse(input, {}, move(url), URLParser::State::Fragment);
if (result_url.is_valid()) if (result_url.is_valid())
@ -392,29 +453,30 @@ HTML::Origin url_origin(AK::URL const& url)
// FIXME: We should probably have an extended version of AK::URL for LibWeb instead of standalone functions like this. // FIXME: We should probably have an extended version of AK::URL for LibWeb instead of standalone functions like this.
// The origin of a URL url is the origin returned by running these steps, switching on urls scheme: // The origin of a URL url is the origin returned by running these steps, switching on urls scheme:
// "blob" // -> "blob"
if (url.scheme() == "blob"sv) { if (url.scheme() == "blob"sv) {
// FIXME: Support 'blob://' URLs // FIXME: Support 'blob://' URLs
return HTML::Origin {}; return HTML::Origin {};
} }
// "ftp" // -> "ftp"
// "http" // -> "http"
// "https" // -> "https"
// "ws" // -> "ws"
// "wss" // -> "wss"
if (url.scheme().is_one_of("ftp"sv, "http"sv, "https"sv, "ws"sv, "wss"sv)) { if (url.scheme().is_one_of("ftp"sv, "http"sv, "https"sv, "ws"sv, "wss"sv)) {
// Return the tuple origin (urls scheme, urls host, urls port, null). // Return the tuple origin (urls scheme, urls host, urls port, null).
return HTML::Origin(url.scheme(), url.host(), url.port().value_or(0)); return HTML::Origin(url.scheme(), url.host(), url.port().value_or(0));
} }
// "file" // -> "file"
if (url.scheme() == "file"sv) { if (url.scheme() == "file"sv) {
// Unfortunate as it is, this is left as an exercise to the reader. When in doubt, return a new opaque origin. // Unfortunate as it is, this is left as an exercise to the reader. When in doubt, return a new opaque origin.
// Note: We must return an origin with the `file://' protocol for `file://' iframes to work from `file://' pages. // Note: We must return an origin with the `file://' protocol for `file://' iframes to work from `file://' pages.
return HTML::Origin(url.scheme(), DeprecatedString(), 0); return HTML::Origin(url.scheme(), DeprecatedString(), 0);
} }
// -> Otherwise
// Return a new opaque origin. // Return a new opaque origin.
return HTML::Origin {}; return HTML::Origin {};
} }