diff --git a/AK/URL.cpp b/AK/URL.cpp index 1770a9e9d90..eb7da264d15 100644 --- a/AK/URL.cpp +++ b/AK/URL.cpp @@ -101,12 +101,18 @@ void URL::set_password(DeprecatedString password, ApplyPercentEncoding apply_per m_valid = compute_validity(); } -void URL::set_host(DeprecatedString host) +void URL::set_host(Host host) { m_host = move(host); m_valid = compute_validity(); } +// https://url.spec.whatwg.org/#concept-host-serializer +ErrorOr URL::serialized_host() const +{ + return URLParser::serialize_host(m_host); +} + void URL::set_port(Optional port) { if (port == default_port_for_scheme(m_scheme)) { @@ -157,7 +163,7 @@ bool URL::cannot_have_a_username_or_password_or_port() const { // A URL cannot have a username/password/port if its host is null or the empty string, or its scheme is "file". // FIXME: The spec does not mention anything to do with 'cannot be a base URL'. - return m_host.is_null() || m_host.is_empty() || m_cannot_be_a_base_url || m_scheme == "file"sv; + return m_host.has() || m_host == String {} || m_cannot_be_a_base_url || m_scheme == "file"sv; } // FIXME: This is by no means complete. @@ -192,7 +198,7 @@ bool URL::compute_validity() const } // NOTE: A file URL's host should be the empty string for localhost, not null. - if (m_scheme == "file" && m_host.is_null()) + if (m_scheme == "file" && m_host.has()) return false; return true; @@ -227,7 +233,7 @@ URL URL::create_with_file_scheme(DeprecatedString const& path, DeprecatedString url.set_scheme("file"); // NOTE: If the hostname is localhost (or null, which implies localhost), it should be set to the empty string. // This is because a file URL always needs a non-null hostname. - url.set_host(hostname.is_null() || hostname == "localhost" ? DeprecatedString::empty() : hostname); + url.set_host(hostname.is_null() || hostname == "localhost" ? String {} : String::from_deprecated_string(hostname).release_value_but_fixme_should_propagate_errors()); url.set_paths(lexical_path.parts()); if (path.ends_with('/')) url.append_slash(); @@ -243,7 +249,8 @@ URL URL::create_with_help_scheme(DeprecatedString const& path, DeprecatedString url.set_scheme("help"); // NOTE: If the hostname is localhost (or null, which implies localhost), it should be set to the empty string. // This is because a file URL always needs a non-null hostname. - url.set_host(hostname.is_null() || hostname == "localhost" ? DeprecatedString::empty() : hostname); + url.set_host(hostname.is_null() || hostname == "localhost" ? String {} : String::from_deprecated_string(hostname).release_value_but_fixme_should_propagate_errors()); + url.set_paths(lexical_path.parts()); if (path.ends_with('/')) url.append_slash(); @@ -309,7 +316,7 @@ DeprecatedString URL::serialize(ExcludeFragment exclude_fragment) const output.append(':'); // 2. If url’s host is non-null: - if (!m_host.is_null()) { + if (!m_host.has()) { // 1. Append "//" to output. output.append("//"sv); @@ -329,7 +336,7 @@ DeprecatedString URL::serialize(ExcludeFragment exclude_fragment) const } // 3. Append url’s host, serialized, to output. - output.append(m_host); + output.append(serialized_host().release_value_but_fixme_should_propagate_errors()); // 4. If url’s port is non-null, append U+003A (:) followed by url’s port, serialized, to output. if (m_port.has_value()) @@ -342,7 +349,7 @@ DeprecatedString URL::serialize(ExcludeFragment exclude_fragment) const if (cannot_be_a_base_url()) { output.append(m_paths[0]); } else { - if (m_host.is_null() && m_paths.size() > 1 && m_paths[0].is_empty()) + if (m_host.has() && m_paths.size() > 1 && m_paths[0].is_empty()) output.append("/."sv); for (auto& segment : m_paths) { output.append('/'); @@ -379,9 +386,9 @@ DeprecatedString URL::serialize_for_display() const builder.append(m_scheme); builder.append(':'); - if (!m_host.is_null()) { + if (!m_host.has()) { builder.append("//"sv); - builder.append(m_host); + builder.append(serialized_host().release_value_but_fixme_should_propagate_errors()); if (m_port.has_value()) builder.appendff(":{}", *m_port); } @@ -389,7 +396,7 @@ DeprecatedString URL::serialize_for_display() const if (cannot_be_a_base_url()) { builder.append(m_paths[0]); } else { - if (m_host.is_null() && m_paths.size() > 1 && m_paths[0].is_empty()) + if (m_host.has() && m_paths.size() > 1 && m_paths[0].is_empty()) builder.append("/."sv); for (auto& segment : m_paths) { builder.append('/'); @@ -437,7 +444,7 @@ DeprecatedString URL::serialize_origin() const StringBuilder builder; builder.append(m_scheme); builder.append("://"sv); - builder.append(m_host); + builder.append(serialized_host().release_value_but_fixme_should_propagate_errors()); if (m_port.has_value()) builder.appendff(":{}", *m_port); return builder.to_deprecated_string(); diff --git a/AK/URL.h b/AK/URL.h index c5dd2b2c4dd..12f6d646afa 100644 --- a/AK/URL.h +++ b/AK/URL.h @@ -79,7 +79,8 @@ public: DeprecatedString const& scheme() const { return m_scheme; } DeprecatedString username(ApplyPercentDecoding = ApplyPercentDecoding::Yes) const; DeprecatedString password(ApplyPercentDecoding = ApplyPercentDecoding::Yes) const; - DeprecatedString const& host() const { return m_host; } + Host const& host() const { return m_host; } + ErrorOr serialized_host() const; DeprecatedString basename(ApplyPercentDecoding = ApplyPercentDecoding::Yes) const; DeprecatedString query(ApplyPercentDecoding = ApplyPercentDecoding::No) const; DeprecatedString fragment(ApplyPercentDecoding = ApplyPercentDecoding::Yes) const; @@ -101,7 +102,7 @@ public: void set_scheme(DeprecatedString); void set_username(DeprecatedString, ApplyPercentEncoding = ApplyPercentEncoding::Yes); void set_password(DeprecatedString, ApplyPercentEncoding = ApplyPercentEncoding::Yes); - void set_host(DeprecatedString); + void set_host(Host); void set_port(Optional); void set_paths(Vector, ApplyPercentEncoding = ApplyPercentEncoding::Yes); void set_query(DeprecatedString, ApplyPercentEncoding = ApplyPercentEncoding::Yes); @@ -178,7 +179,7 @@ private: DeprecatedString m_password; // A URL’s host is null or a host. It is initially null. - DeprecatedString m_host; + Host m_host; // A URL’s port is either null or a 16-bit unsigned integer that identifies a networking port. It is initially null. Optional m_port; diff --git a/AK/URLParser.cpp b/AK/URLParser.cpp index 1a634b0ae62..d932a3a1e4c 100644 --- a/AK/URLParser.cpp +++ b/AK/URLParser.cpp @@ -32,7 +32,7 @@ static void report_validation_error(SourceLocation const& location = SourceLocat dbgln_if(URL_PARSER_DEBUG, "URLParser::basic_parse: Validation error! {}", location); } -static Optional parse_opaque_host(StringView input) +static Optional parse_opaque_host(StringView input) { auto forbidden_host_characters_excluding_percent = "\0\t\n\r #/:<>?@[\\]^|"sv; for (auto character : forbidden_host_characters_excluding_percent) { @@ -43,7 +43,7 @@ static Optional parse_opaque_host(StringView input) } // FIXME: If input contains a code point that is not a URL code point and not U+0025 (%), validation error. // FIXME: If input contains a U+0025 (%) and the two code points following it are not ASCII hex digits, validation error. - return URL::percent_encode(input, URL::PercentEncodeSet::C0Control); + return String::from_deprecated_string(URL::percent_encode(input, URL::PercentEncodeSet::C0Control)).release_value_but_fixme_should_propagate_errors(); } struct ParsedIPv4Number { @@ -549,7 +549,7 @@ static bool ends_in_a_number_checker(StringView input) // https://url.spec.whatwg.org/#concept-host-parser // NOTE: This is a very bare-bones implementation. -static Optional parse_host(StringView input, bool is_not_special = false) +static Optional parse_host(StringView input, bool is_not_special = false) { // 1. If input starts with U+005B ([), then: if (input.starts_with('[')) { @@ -563,10 +563,7 @@ static Optional parse_host(StringView input, bool is_not_speci auto address = parse_ipv6_address(input.substring_view(1, input.length() - 2)); if (!address.has_value()) return {}; - - StringBuilder output; - serialize_ipv6_address(*address, output); - return output.to_deprecated_string(); + return address.release_value(); } // 2. If isNotSpecial is true, then return the result of opaque-host parsing input. @@ -581,12 +578,16 @@ static Optional parse_host(StringView input, bool is_not_speci // FIXME: 5. Let asciiDomain be the result of running domain to ASCII on domain. // FIXME: 6. If asciiDomain is failure, then return failure. - auto& ascii_domain = domain; + auto ascii_domain_or_error = String::from_deprecated_string(domain); + if (ascii_domain_or_error.is_error()) + return {}; + + auto ascii_domain = ascii_domain_or_error.release_value(); // 7. If asciiDomain contains a forbidden domain code point, domain-invalid-code-point validation error, return failure. auto forbidden_host_characters = "\0\t\n\r #%/:<>?@[\\]^|"sv; for (auto character : forbidden_host_characters) { - if (ascii_domain.view().contains(character)) { + if (ascii_domain.bytes_as_string_view().contains(character)) { report_validation_error(); return {}; } @@ -598,11 +599,7 @@ static Optional parse_host(StringView input, bool is_not_speci if (!ipv4_host.has_value()) return {}; - auto result = serialize_ipv4_address(*ipv4_host); - if (result.is_error()) - return {}; - - return result.release_value().to_deprecated_string(); + return ipv4_host.release_value(); } // 9. Return asciiDomain. @@ -880,7 +877,7 @@ URL URLParser::basic_parse(StringView raw_input, Optional const& base_url, return *url; // 4. If url’s scheme is "file" and its host is an empty host, then return. - if (url->scheme() == "file"sv && url->host().is_empty()) + if (url->scheme() == "file"sv && url->host() == String {}) return *url; } @@ -1319,7 +1316,7 @@ URL URLParser::basic_parse(StringView raw_input, Optional const& base_url, url->m_scheme = "file"; // 2. Set url’s host to the empty string. - url->m_host = ""; + url->m_host = String {}; // 3. If c is U+002F (/) or U+005C (\), then: if (code_point == '/' || code_point == '\\') { @@ -1422,7 +1419,7 @@ URL URLParser::basic_parse(StringView raw_input, Optional const& base_url, // 2. Otherwise, if buffer is the empty string, then: else if (buffer.is_empty()) { // 1. Set url’s host to the empty string. - url->m_host = ""; + url->m_host = String {}; // 2. If state override is given, then return. if (state_override.has_value()) @@ -1442,8 +1439,8 @@ URL URLParser::basic_parse(StringView raw_input, Optional const& base_url, return {}; // 3. If host is "localhost", then set host to the empty string. - if (host.value() == "localhost") - host = ""; + if (host.value().has() && host.value().get() == "localhost"sv) + host = String {}; // 4. Set url’s host to host. url->m_host = host.release_value(); @@ -1498,7 +1495,7 @@ URL URLParser::basic_parse(StringView raw_input, Optional const& base_url, continue; } // 5. Otherwise, if state override is given and url’s host is null, append the empty string to url’s path. - else if (state_override.has_value() && url->host().is_empty()) { + else if (state_override.has_value() && url->host().has()) { url->append_slash(); } break; diff --git a/Ladybird/LocationEdit.cpp b/Ladybird/LocationEdit.cpp index 00260f864e7..a9d9b9ba4d9 100644 --- a/Ladybird/LocationEdit.cpp +++ b/Ladybird/LocationEdit.cpp @@ -49,7 +49,7 @@ void LocationEdit::highlight_location() if (url.is_valid() && !hasFocus()) { if (url.scheme() == "http" || url.scheme() == "https" || url.scheme() == "gemini") { int host_start = (url.scheme().length() + 3) - cursorPosition(); - auto host_length = url.host().length(); + auto host_length = url.serialized_host().release_value_but_fixme_should_propagate_errors().bytes().size(); // FIXME: Maybe add a generator to use https://publicsuffix.org/list/public_suffix_list.dat // for now just highlight the whole host diff --git a/Ladybird/WebSocketImplQt.cpp b/Ladybird/WebSocketImplQt.cpp index afa76aefa92..882a7a03943 100644 --- a/Ladybird/WebSocketImplQt.cpp +++ b/Ladybird/WebSocketImplQt.cpp @@ -53,7 +53,7 @@ void WebSocketImplQt::connect(WebSocket::ConnectionInfo const& connection_info) if (connection_info.is_secure()) { auto ssl_socket = make(); ssl_socket->connectToHostEncrypted( - qstring_from_ak_deprecated_string(connection_info.url().host()), + qstring_from_ak_string(connection_info.url().serialized_host().release_value_but_fixme_should_propagate_errors()), connection_info.url().port_or_default()); QObject::connect(ssl_socket.ptr(), &QSslSocket::alertReceived, [this](QSsl::AlertLevel level, QSsl::AlertType, QString const&) { if (level == QSsl::AlertLevel::Fatal) @@ -63,7 +63,7 @@ void WebSocketImplQt::connect(WebSocket::ConnectionInfo const& connection_info) } else { m_socket = make(); m_socket->connectToHost( - qstring_from_ak_deprecated_string(connection_info.url().host()), + qstring_from_ak_string(connection_info.url().serialized_host().release_value_but_fixme_should_propagate_errors()), connection_info.url().port_or_default()); } diff --git a/Tests/AK/TestURL.cpp b/Tests/AK/TestURL.cpp index a97abe28a66..78790f27754 100644 --- a/Tests/AK/TestURL.cpp +++ b/Tests/AK/TestURL.cpp @@ -22,7 +22,7 @@ TEST_CASE(basic) URL url("http://www.serenityos.org"sv); EXPECT_EQ(url.is_valid(), true); EXPECT_EQ(url.scheme(), "http"); - EXPECT_EQ(url.host(), "www.serenityos.org"); + EXPECT_EQ(MUST(url.serialized_host()), "www.serenityos.org"); EXPECT_EQ(url.port_or_default(), 80); EXPECT_EQ(url.serialize_path(), "/"); EXPECT(url.query().is_null()); @@ -32,7 +32,7 @@ TEST_CASE(basic) URL url("https://www.serenityos.org/index.html"sv); EXPECT_EQ(url.is_valid(), true); EXPECT_EQ(url.scheme(), "https"); - EXPECT_EQ(url.host(), "www.serenityos.org"); + EXPECT_EQ(MUST(url.serialized_host()), "www.serenityos.org"); EXPECT_EQ(url.port_or_default(), 443); EXPECT_EQ(url.serialize_path(), "/index.html"); EXPECT(url.query().is_null()); @@ -42,7 +42,7 @@ TEST_CASE(basic) URL url("https://www.serenityos.org1/index.html"sv); EXPECT_EQ(url.is_valid(), true); EXPECT_EQ(url.scheme(), "https"); - EXPECT_EQ(url.host(), "www.serenityos.org1"); + EXPECT_EQ(MUST(url.serialized_host()), "www.serenityos.org1"); EXPECT_EQ(url.port_or_default(), 443); EXPECT_EQ(url.serialize_path(), "/index.html"); EXPECT(url.query().is_null()); @@ -52,7 +52,7 @@ TEST_CASE(basic) URL url("https://localhost:1234/~anon/test/page.html"sv); EXPECT_EQ(url.is_valid(), true); EXPECT_EQ(url.scheme(), "https"); - EXPECT_EQ(url.host(), "localhost"); + EXPECT_EQ(MUST(url.serialized_host()), "localhost"); EXPECT_EQ(url.port_or_default(), 1234); EXPECT_EQ(url.serialize_path(), "/~anon/test/page.html"); EXPECT(url.query().is_null()); @@ -62,7 +62,7 @@ TEST_CASE(basic) URL url("http://www.serenityos.org/index.html?#"sv); EXPECT_EQ(url.is_valid(), true); EXPECT_EQ(url.scheme(), "http"); - EXPECT_EQ(url.host(), "www.serenityos.org"); + EXPECT_EQ(MUST(url.serialized_host()), "www.serenityos.org"); EXPECT_EQ(url.port_or_default(), 80); EXPECT_EQ(url.serialize_path(), "/index.html"); EXPECT_EQ(url.query(), ""); @@ -72,7 +72,7 @@ TEST_CASE(basic) URL url("http://www.serenityos.org/index.html?foo=1&bar=2"sv); EXPECT_EQ(url.is_valid(), true); EXPECT_EQ(url.scheme(), "http"); - EXPECT_EQ(url.host(), "www.serenityos.org"); + EXPECT_EQ(MUST(url.serialized_host()), "www.serenityos.org"); EXPECT_EQ(url.port_or_default(), 80); EXPECT_EQ(url.serialize_path(), "/index.html"); EXPECT_EQ(url.query(), "foo=1&bar=2"); @@ -82,7 +82,7 @@ TEST_CASE(basic) URL url("http://www.serenityos.org/index.html#fragment"sv); EXPECT_EQ(url.is_valid(), true); EXPECT_EQ(url.scheme(), "http"); - EXPECT_EQ(url.host(), "www.serenityos.org"); + EXPECT_EQ(MUST(url.serialized_host()), "www.serenityos.org"); EXPECT_EQ(url.port_or_default(), 80); EXPECT_EQ(url.serialize_path(), "/index.html"); EXPECT(url.query().is_null()); @@ -92,7 +92,7 @@ TEST_CASE(basic) URL url("http://www.serenityos.org/index.html?foo=1&bar=2&baz=/?#frag/ment?test#"sv); EXPECT_EQ(url.is_valid(), true); EXPECT_EQ(url.scheme(), "http"); - EXPECT_EQ(url.host(), "www.serenityos.org"); + EXPECT_EQ(MUST(url.serialized_host()), "www.serenityos.org"); EXPECT_EQ(url.port_or_default(), 80); EXPECT_EQ(url.serialize_path(), "/index.html"); EXPECT_EQ(url.query(), "foo=1&bar=2&baz=/?"); @@ -128,7 +128,7 @@ TEST_CASE(file_url_with_hostname) URL url("file://courage/my/file"sv); EXPECT(url.is_valid()); EXPECT_EQ(url.scheme(), "file"); - EXPECT_EQ(url.host(), "courage"); + EXPECT_EQ(MUST(url.serialized_host()), "courage"); EXPECT_EQ(url.port_or_default(), 0); EXPECT_EQ(url.serialize_path(), "/my/file"); EXPECT_EQ(url.serialize(), "file://courage/my/file"); @@ -141,7 +141,7 @@ TEST_CASE(file_url_with_localhost) URL url("file://localhost/my/file"sv); EXPECT(url.is_valid()); EXPECT_EQ(url.scheme(), "file"); - EXPECT_EQ(url.host(), ""); + EXPECT_EQ(MUST(url.serialized_host()), ""); EXPECT_EQ(url.serialize_path(), "/my/file"); EXPECT_EQ(url.serialize(), "file:///my/file"); } @@ -151,7 +151,7 @@ TEST_CASE(file_url_without_hostname) URL url("file:///my/file"sv); EXPECT(url.is_valid()); EXPECT_EQ(url.scheme(), "file"); - EXPECT_EQ(url.host(), ""); + EXPECT_EQ(MUST(url.serialized_host()), ""); EXPECT_EQ(url.serialize_path(), "/my/file"); EXPECT_EQ(url.serialize(), "file:///my/file"); } @@ -205,7 +205,7 @@ TEST_CASE(about_url) URL url("about:blank"sv); EXPECT(url.is_valid()); EXPECT_EQ(url.scheme(), "about"); - EXPECT(url.host().is_null()); + EXPECT(url.host().has()); EXPECT_EQ(url.serialize_path(), "blank"); EXPECT(url.query().is_null()); EXPECT(url.fragment().is_null()); @@ -217,7 +217,7 @@ TEST_CASE(mailto_url) URL url("mailto:mail@example.com"sv); EXPECT(url.is_valid()); EXPECT_EQ(url.scheme(), "mailto"); - EXPECT(url.host().is_null()); + EXPECT(url.host().has()); EXPECT_EQ(url.port_or_default(), 0); EXPECT_EQ(url.path_segment_count(), 1u); EXPECT_EQ(url.path_segment_at_index(0), "mail@example.com"); @@ -231,7 +231,7 @@ TEST_CASE(data_url) URL url("data:text/html,test"sv); EXPECT(url.is_valid()); EXPECT_EQ(url.scheme(), "data"); - EXPECT(url.host().is_null()); + EXPECT(url.host().has()); EXPECT_EQ(url.data_mime_type(), "text/html"); EXPECT_EQ(url.data_payload(), "test"); EXPECT(!url.data_payload_is_base64()); @@ -243,7 +243,7 @@ TEST_CASE(data_url_default_mime_type) URL url("data:,test"sv); EXPECT(url.is_valid()); EXPECT_EQ(url.scheme(), "data"); - EXPECT(url.host().is_null()); + EXPECT(url.host().has()); EXPECT_EQ(url.data_mime_type(), "text/plain"); EXPECT_EQ(url.data_payload(), "test"); EXPECT(!url.data_payload_is_base64()); @@ -255,7 +255,7 @@ TEST_CASE(data_url_encoded) URL url("data:text/html,Hello%20friends%2C%0X%X0"sv); EXPECT(url.is_valid()); EXPECT_EQ(url.scheme(), "data"); - EXPECT(url.host().is_null()); + EXPECT(url.host().has()); EXPECT_EQ(url.data_mime_type(), "text/html"); EXPECT_EQ(url.data_payload(), "Hello friends,%0X%X0"); EXPECT(!url.data_payload_is_base64()); @@ -267,7 +267,7 @@ TEST_CASE(data_url_base64_encoded) URL url("data:text/html;base64,test"sv); EXPECT(url.is_valid()); EXPECT_EQ(url.scheme(), "data"); - EXPECT(url.host().is_null()); + EXPECT(url.host().has()); EXPECT_EQ(url.data_mime_type(), "text/html"); EXPECT_EQ(url.data_payload(), "test"); EXPECT(url.data_payload_is_base64()); @@ -279,7 +279,7 @@ TEST_CASE(data_url_base64_encoded_default_mime_type) URL url("data:;base64,test"sv); EXPECT(url.is_valid()); EXPECT_EQ(url.scheme(), "data"); - EXPECT(url.host().is_null()); + EXPECT(url.host().has()); EXPECT_EQ(url.data_mime_type(), "text/plain"); EXPECT_EQ(url.data_payload(), "test"); EXPECT(url.data_payload_is_base64()); @@ -291,7 +291,7 @@ TEST_CASE(data_url_base64_encoded_with_whitespace) URL url("data: text/html ; bAsE64 , test with whitespace "sv); EXPECT(url.is_valid()); EXPECT_EQ(url.scheme(), "data"); - EXPECT(url.host().is_null()); + EXPECT(url.host().has()); EXPECT_EQ(url.data_mime_type(), "text/html"); EXPECT_EQ(url.data_payload(), " test with whitespace "); EXPECT(url.data_payload_is_base64()); @@ -303,7 +303,7 @@ TEST_CASE(data_url_base64_encoded_with_inline_whitespace) URL url("data:text/javascript;base64,%20ZD%20Qg%0D%0APS%20An%20Zm91cic%0D%0A%207%20"sv); EXPECT(url.is_valid()); EXPECT_EQ(url.scheme(), "data"); - EXPECT(url.host().is_null()); + EXPECT(url.host().has()); EXPECT_EQ(url.data_mime_type(), "text/javascript"); EXPECT(url.data_payload_is_base64()); EXPECT_EQ(url.data_payload(), " ZD Qg\r\nPS An Zm91cic\r\n 7 "sv); @@ -371,7 +371,7 @@ TEST_CASE(complete_url) URL url = base_url.complete_url("test.html"sv); EXPECT(url.is_valid()); EXPECT_EQ(url.scheme(), "http"); - EXPECT_EQ(url.host(), "serenityos.org"); + EXPECT_EQ(MUST(url.serialized_host()), "serenityos.org"); EXPECT_EQ(url.serialize_path(), "/test.html"); EXPECT(url.query().is_null()); EXPECT(url.query().is_null()); @@ -445,6 +445,7 @@ TEST_CASE(ipv6_address) constexpr auto ipv6_url = "http://[::1]/index.html"sv; URL url(ipv6_url); EXPECT(url.is_valid()); + EXPECT_EQ(MUST(url.serialized_host()), "[::1]"sv); EXPECT_EQ(url, ipv6_url); } @@ -452,6 +453,7 @@ TEST_CASE(ipv6_address) constexpr auto ipv6_url = "http://[0:f:0:0:f:f:0:0]/index.html"sv; URL url(ipv6_url); EXPECT(url.is_valid()); + EXPECT_EQ(MUST(url.serialized_host()), "[0:f::f:f:0:0]"sv); EXPECT_EQ(url, ipv6_url); } @@ -459,6 +461,7 @@ TEST_CASE(ipv6_address) constexpr auto ipv6_url = "https://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]/index.html"sv; URL url(ipv6_url); EXPECT(url.is_valid()); + EXPECT_EQ(MUST(url.serialized_host()), "[2001:db8:85a3::8a2e:370:7334]"sv); EXPECT_EQ(url, ipv6_url); } @@ -475,14 +478,14 @@ TEST_CASE(ipv4_address) constexpr auto ipv4_url = "http://127.0.0.1/index.html"sv; URL url(ipv4_url); EXPECT(url.is_valid()); - EXPECT_EQ(url.host(), "127.0.0.1"sv); + EXPECT_EQ(MUST(url.serialized_host()), "127.0.0.1"sv); } { constexpr auto ipv4_url = "http://0x.0x.0"sv; URL url(ipv4_url); EXPECT(url.is_valid()); - EXPECT_EQ(url.host(), "0.0.0.0"sv); + EXPECT_EQ(MUST(url.serialized_host()), "0.0.0.0"sv); } { @@ -495,13 +498,13 @@ TEST_CASE(ipv4_address) constexpr auto ipv4_url = "http://256"sv; URL url(ipv4_url); EXPECT(url.is_valid()); - EXPECT_EQ(url.host(), "0.0.1.0"sv); + EXPECT_EQ(MUST(url.serialized_host()), "0.0.1.0"sv); } { constexpr auto ipv4_url = "http://888888888"sv; URL url(ipv4_url); EXPECT(url.is_valid()); - EXPECT_EQ(url.host(), "52.251.94.56"sv); + EXPECT_EQ(MUST(url.serialized_host()), "52.251.94.56"sv); } } diff --git a/Userland/Applications/Assistant/Providers.cpp b/Userland/Applications/Assistant/Providers.cpp index 31acca3a979..c180bea7bcf 100644 --- a/Userland/Applications/Assistant/Providers.cpp +++ b/Userland/Applications/Assistant/Providers.cpp @@ -248,8 +248,8 @@ void URLProvider::query(DeprecatedString const& query, Function() || url.host() == String {}) + url.set_host(String::from_deprecated_string(query).release_value_but_fixme_should_propagate_errors()); if (url.path_segment_count() == 0) url.set_paths({ "" }); diff --git a/Userland/Applications/Browser/CookieJar.cpp b/Userland/Applications/Browser/CookieJar.cpp index 6e5dc6b4448..9180495d94d 100644 --- a/Userland/Applications/Browser/CookieJar.cpp +++ b/Userland/Applications/Browser/CookieJar.cpp @@ -208,7 +208,10 @@ Optional CookieJar::canonicalize_domain(const URL& url) return {}; // FIXME: Implement RFC 5890 to "Convert each label that is not a Non-Reserved LDH (NR-LDH) label to an A-label". - return url.host().to_lowercase(); + if (url.host().has()) + return {}; + + return url.serialized_host().release_value_but_fixme_should_propagate_errors().to_deprecated_string().to_lowercase(); } bool CookieJar::domain_matches(DeprecatedString const& string, DeprecatedString const& domain_string) diff --git a/Userland/Applications/Browser/Tab.cpp b/Userland/Applications/Browser/Tab.cpp index 04fd85c4c61..a00659fb961 100644 --- a/Userland/Applications/Browser/Tab.cpp +++ b/Userland/Applications/Browser/Tab.cpp @@ -107,10 +107,10 @@ void Tab::update_status(Optional text_override, i32 count_waiting) if (count_waiting == 0) { // ex: "Loading google.com" - m_statusbar->set_text(String::formatted("Loading {}", m_navigating_url->host()).release_value_but_fixme_should_propagate_errors()); + m_statusbar->set_text(String::formatted("Loading {}", m_navigating_url->serialized_host().release_value_but_fixme_should_propagate_errors()).release_value_but_fixme_should_propagate_errors()); } else { // ex: "google.com is waiting on 5 resources" - m_statusbar->set_text(String::formatted("{} is waiting on {} resource{}", m_navigating_url->host(), count_waiting, count_waiting == 1 ? ""sv : "s"sv).release_value_but_fixme_should_propagate_errors()); + m_statusbar->set_text(String::formatted("{} is waiting on {} resource{}", m_navigating_url->serialized_host().release_value_but_fixme_should_propagate_errors(), count_waiting, count_waiting == 1 ? ""sv : "s"sv).release_value_but_fixme_should_propagate_errors()); } } diff --git a/Userland/Applications/Spreadsheet/HelpWindow.cpp b/Userland/Applications/Spreadsheet/HelpWindow.cpp index 31ce6683098..845af963606 100644 --- a/Userland/Applications/Spreadsheet/HelpWindow.cpp +++ b/Userland/Applications/Spreadsheet/HelpWindow.cpp @@ -83,7 +83,7 @@ HelpWindow::HelpWindow(GUI::Window* parent) m_webview = splitter.add(); m_webview->on_link_click = [this](auto& url, auto&, auto&&) { VERIFY(url.scheme() == "spreadsheet"); - if (url.host() == "example") { + if (url.host().template has() && url.host().template get() == "example"sv) { auto example_path = url.serialize_path(); auto entry = LexicalPath::basename(example_path); auto doc_option = m_docs.get_object(entry); @@ -122,11 +122,11 @@ HelpWindow::HelpWindow(GUI::Window* parent) widget->add_sheet(sheet.release_nonnull()); window->show(); - } else if (url.host() == "doc") { + } else if (url.host() == String::from_utf8_short_string("doc"sv)) { auto entry = LexicalPath::basename(url.serialize_path()); m_webview->load(URL::create_with_data("text/html", render(entry))); } else { - dbgln("Invalid spreadsheet action domain '{}'", url.host()); + dbgln("Invalid spreadsheet action domain '{}'", url.serialized_host().release_value_but_fixme_should_propagate_errors()); } }; diff --git a/Userland/Applications/Spreadsheet/Spreadsheet.cpp b/Userland/Applications/Spreadsheet/Spreadsheet.cpp index 1142574d353..05ea62a90e3 100644 --- a/Userland/Applications/Spreadsheet/Spreadsheet.cpp +++ b/Userland/Applications/Spreadsheet/Spreadsheet.cpp @@ -264,7 +264,7 @@ Optional Sheet::position_from_url(const URL& url) const return {}; } - if (url.scheme() != "spreadsheet" || url.host() != "cell") { + if (url.scheme() != "spreadsheet" || url.host() != String::from_utf8_short_string("cell"sv)) { dbgln("Bad url: {}", url.to_deprecated_string()); return {}; } @@ -757,7 +757,7 @@ URL Position::to_url(Sheet const& sheet) const { URL url; url.set_scheme("spreadsheet"); - url.set_host("cell"); + url.set_host(String::from_utf8_short_string("cell"sv)); url.set_paths({ DeprecatedString::number(getpid()) }); url.set_fragment(to_cell_identifier(sheet)); return url; diff --git a/Userland/Libraries/LibCore/Proxy.h b/Userland/Libraries/LibCore/Proxy.h index a427569dd13..cf8c9a7a67b 100644 --- a/Userland/Libraries/LibCore/Proxy.h +++ b/Userland/Libraries/LibCore/Proxy.h @@ -36,10 +36,9 @@ struct ProxyData { proxy_data.type = ProxyData::Type::SOCKS5; - auto host_ipv4 = IPv4Address::from_string(url.host()); - if (!host_ipv4.has_value()) + if (!url.host().has()) return Error::from_string_literal("Invalid proxy host, must be an IPv4 address"); - proxy_data.host_ipv4 = host_ipv4->to_u32(); + proxy_data.host_ipv4 = url.host().get(); auto port = url.port(); if (!port.has_value()) diff --git a/Userland/Libraries/LibGUI/TextBox.cpp b/Userland/Libraries/LibGUI/TextBox.cpp index 634dda560c4..9f50acc16b4 100644 --- a/Userland/Libraries/LibGUI/TextBox.cpp +++ b/Userland/Libraries/LibGUI/TextBox.cpp @@ -179,8 +179,9 @@ void UrlBox::highlight_url() if (url.is_valid() && !is_focused()) { if (url.scheme() == "http" || url.scheme() == "https" || url.scheme() == "gemini") { + auto serialized_host = url.serialized_host().release_value_but_fixme_should_propagate_errors().to_deprecated_string(); auto host_start = url.scheme().length() + 3; - auto host_length = url.host().length(); + auto host_length = serialized_host.length(); // FIXME: Maybe add a generator to use https://publicsuffix.org/list/public_suffix_list.dat // for now just highlight the whole host diff --git a/Userland/Libraries/LibHTTP/HttpRequest.cpp b/Userland/Libraries/LibHTTP/HttpRequest.cpp index 0a5460727aa..a21587dcea0 100644 --- a/Userland/Libraries/LibHTTP/HttpRequest.cpp +++ b/Userland/Libraries/LibHTTP/HttpRequest.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -57,7 +58,7 @@ ErrorOr HttpRequest::to_raw_request() const TRY(builder.try_append(m_url.query())); } TRY(builder.try_append(" HTTP/1.1\r\nHost: "sv)); - TRY(builder.try_append(m_url.host())); + TRY(builder.try_append(TRY(m_url.serialized_host()))); if (m_url.port().has_value()) TRY(builder.try_appendff(":{}", *m_url.port())); TRY(builder.try_append("\r\n"sv)); diff --git a/Userland/Libraries/LibManual/Node.cpp b/Userland/Libraries/LibManual/Node.cpp index a182d4db400..adfd519b998 100644 --- a/Userland/Libraries/LibManual/Node.cpp +++ b/Userland/Libraries/LibManual/Node.cpp @@ -82,7 +82,7 @@ ErrorOr> Node::try_create_from_query(Vector> Node::try_find_from_help_url(URL const& url) { - if (url.host() != "man") + if (url.host() != String::from_utf8_short_string("man"sv)) return Error::from_string_view("Bad help operation"sv); if (url.path_segment_count() < 2) return Error::from_string_view("Bad help page URL"sv); diff --git a/Userland/Libraries/LibWeb/DOM/Document.cpp b/Userland/Libraries/LibWeb/DOM/Document.cpp index 67e9b4170bc..1a4df62a65a 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.cpp +++ b/Userland/Libraries/LibWeb/DOM/Document.cpp @@ -2316,8 +2316,7 @@ DeprecatedString Document::domain() const return DeprecatedString::empty(); // 3. Return effectiveDomain, serialized. - // FIXME: Implement host serialization. - return effective_domain.release_value(); + return URLParser::serialize_host(effective_domain.release_value()).release_value_but_fixme_should_propagate_errors().to_deprecated_string(); } void Document::set_domain(DeprecatedString const& domain) diff --git a/Userland/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp b/Userland/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp index b846ca21b47..5f47cad3879 100644 --- a/Userland/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp +++ b/Userland/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp @@ -256,7 +256,7 @@ WebIDL::ExceptionOr>> main_fetch(JS:: // - request’s current URL’s scheme is "http" request->current_url().scheme() == "http"sv // - request’s current URL’s host is a domain - && URL::host_is_domain(request->current_url().host()) + && URL::host_is_domain(request->current_url().serialized_host().release_value_but_fixme_should_propagate_errors()) // FIXME: - Matching request’s current URL’s host per Known HSTS Host Domain Name Matching results in either a // superdomain match with an asserted includeSubDomains directive or a congruent match (with or without an // asserted includeSubDomains directive) [HSTS]; or DNS resolution for the request finds a matching HTTPS RR diff --git a/Userland/Libraries/LibWeb/HTML/BrowsingContext.cpp b/Userland/Libraries/LibWeb/HTML/BrowsingContext.cpp index 2b370ea4de2..e316ccf1f6c 100644 --- a/Userland/Libraries/LibWeb/HTML/BrowsingContext.cpp +++ b/Userland/Libraries/LibWeb/HTML/BrowsingContext.cpp @@ -44,7 +44,7 @@ static bool url_matches_about_blank(AK::URL const& url) && url.serialize_path() == "blank"sv && url.username().is_empty() && url.password().is_empty() - && url.host().is_null(); + && url.host().has(); } // FIXME: This is an outdated older version of "determining the origin" and should be removed. diff --git a/Userland/Libraries/LibWeb/HTML/HTMLHyperlinkElementUtils.cpp b/Userland/Libraries/LibWeb/HTML/HTMLHyperlinkElementUtils.cpp index abdbda21c54..096313b96e9 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLHyperlinkElementUtils.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLHyperlinkElementUtils.cpp @@ -167,15 +167,15 @@ DeprecatedString HTMLHyperlinkElementUtils::host() const auto& url = m_url; // 3. If url or url's host is null, return the empty string. - if (!url.has_value() || url->host().is_null()) + if (!url.has_value() || url->host().has()) return DeprecatedString::empty(); // 4. If url's port is null, return url's host, serialized. if (!url->port().has_value()) - return url->host(); + return url->serialized_host().release_value_but_fixme_should_propagate_errors().to_deprecated_string(); // 5. Return url's host, serialized, followed by ":" and url's port, serialized. - return DeprecatedString::formatted("{}:{}", url->host(), url->port().value()); + return DeprecatedString::formatted("{}:{}", url->serialized_host().release_value_but_fixme_should_propagate_errors(), url->port().value()); } // https://html.spec.whatwg.org/multipage/links.html#dom-hyperlink-host @@ -205,11 +205,14 @@ DeprecatedString HTMLHyperlinkElementUtils::hostname() const // 1. Reinitialize url. // // 2. Let url be this element's url. - // + AK::URL url(href()); + // 3. If url or url's host is null, return the empty string. - // + if (url.host().has()) + return DeprecatedString::empty(); + // 4. Return url's host, serialized. - return AK::URL(href()).host(); + return url.serialized_host().release_value_but_fixme_should_propagate_errors().to_deprecated_string(); } void HTMLHyperlinkElementUtils::set_hostname(DeprecatedString hostname) diff --git a/Userland/Libraries/LibWeb/HTML/Location.cpp b/Userland/Libraries/LibWeb/HTML/Location.cpp index 973ff7874d6..c4727456d4d 100644 --- a/Userland/Libraries/LibWeb/HTML/Location.cpp +++ b/Userland/Libraries/LibWeb/HTML/Location.cpp @@ -153,15 +153,15 @@ WebIDL::ExceptionOr Location::host() const auto url = this->url(); // 3. If url's host is null, return the empty string. - if (url.host().is_null()) + if (url.host().has()) return String {}; // 4. If url's port is null, return url's host, serialized. if (!url.port().has_value()) - return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(url.host())); + return TRY_OR_THROW_OOM(vm, url.serialized_host()); // 5. Return url's host, serialized, followed by ":" and url's port, serialized. - return TRY_OR_THROW_OOM(vm, String::formatted("{}:{}", url.host(), *url.port())); + return TRY_OR_THROW_OOM(vm, String::formatted("{}:{}", TRY_OR_THROW_OOM(vm, url.serialized_host()), *url.port())); } WebIDL::ExceptionOr Location::set_host(String const&) @@ -183,11 +183,11 @@ WebIDL::ExceptionOr Location::hostname() const auto url = this->url(); // 2. If this's url's host is null, return the empty string. - if (url.host().is_null()) + if (url.host().has()) return String {}; // 3. Return this's url's host, serialized. - return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(url.host())); + return TRY_OR_THROW_OOM(vm, url.serialized_host()); } WebIDL::ExceptionOr Location::set_hostname(String const&) diff --git a/Userland/Libraries/LibWeb/HTML/NavigableContainer.cpp b/Userland/Libraries/LibWeb/HTML/NavigableContainer.cpp index 12e55c8df82..4f4f0f5ccde 100644 --- a/Userland/Libraries/LibWeb/HTML/NavigableContainer.cpp +++ b/Userland/Libraries/LibWeb/HTML/NavigableContainer.cpp @@ -217,7 +217,7 @@ static bool url_matches_about_blank(AK::URL const& url) && url.serialize_path() == "blank"sv && url.username().is_empty() && url.password().is_empty() - && url.host().is_null(); + && url.host().has(); } // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#shared-attribute-processing-steps-for-iframe-and-frame-elements diff --git a/Userland/Libraries/LibWeb/HTML/Origin.h b/Userland/Libraries/LibWeb/HTML/Origin.h index 62f32dc03a5..b93ec8bcf7f 100644 --- a/Userland/Libraries/LibWeb/HTML/Origin.h +++ b/Userland/Libraries/LibWeb/HTML/Origin.h @@ -8,13 +8,15 @@ #pragma once #include +#include +#include namespace Web::HTML { class Origin { public: Origin() = default; - Origin(DeprecatedString const& scheme, DeprecatedString const& host, u16 port) + Origin(DeprecatedString const& scheme, AK::URL::Host const& host, u16 port) : m_scheme(scheme) , m_host(host) , m_port(port) @@ -22,10 +24,10 @@ public: } // https://html.spec.whatwg.org/multipage/origin.html#concept-origin-opaque - bool is_opaque() const { return m_scheme.is_null() && m_host.is_null() && m_port == 0; } + bool is_opaque() const { return m_scheme.is_null() && m_host.has() && m_port == 0; } DeprecatedString const& scheme() const { return m_scheme; } - DeprecatedString const& host() const { return m_host; } + AK::URL::Host const& host() const { return m_host; } u16 port() const { return m_port; } // https://html.spec.whatwg.org/multipage/origin.html#same-origin @@ -81,7 +83,7 @@ public: result.append("://"sv); // 4. Append origin's host, serialized, to result. - result.append(host()); + result.append(URLParser::serialize_host(host()).release_value_but_fixme_should_propagate_errors().to_deprecated_string()); // 5. If origin's port is non-null, append a U+003A COLON character (:), and origin's port, serialized, to result. if (port() != 0) { @@ -93,11 +95,11 @@ public: } // https://html.spec.whatwg.org/multipage/origin.html#concept-origin-effective-domain - Optional effective_domain() const + Optional effective_domain() const { // 1. If origin is an opaque origin, then return null. if (is_opaque()) - return Optional {}; + return {}; // FIXME: 2. If origin's domain is non-null, then return origin's domain. @@ -109,7 +111,7 @@ public: private: DeprecatedString m_scheme; - DeprecatedString m_host; + AK::URL::Host m_host; u16 m_port { 0 }; }; @@ -120,7 +122,10 @@ template<> struct Traits : public GenericTraits { static unsigned hash(Web::HTML::Origin const& origin) { - return pair_int_hash(origin.scheme().hash(), pair_int_hash(int_hash(origin.port()), origin.host().hash())); + auto hash_without_host = pair_int_hash(origin.scheme().hash(), origin.port()); + if (origin.host().has()) + return hash_without_host; + return pair_int_hash(hash_without_host, URLParser::serialize_host(origin.host()).release_value_but_fixme_should_propagate_errors().hash()); } }; } // namespace AK diff --git a/Userland/Libraries/LibWeb/HTML/WorkerLocation.cpp b/Userland/Libraries/LibWeb/HTML/WorkerLocation.cpp index 7ac70edf6b3..9f012f4d79e 100644 --- a/Userland/Libraries/LibWeb/HTML/WorkerLocation.cpp +++ b/Userland/Libraries/LibWeb/HTML/WorkerLocation.cpp @@ -4,6 +4,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include @@ -43,15 +44,15 @@ WebIDL::ExceptionOr WorkerLocation::host() const auto const& url = m_global_scope->url(); // 2. If url's host is null, return the empty string. - if (url.host().is_empty()) + if (url.host().has()) return String {}; // 3. If url's port is null, return url's host, serialized. if (!url.port().has_value()) - return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(url.host())); + return TRY_OR_THROW_OOM(vm, url.serialized_host()); // 4. Return url's host, serialized, followed by ":" and url's port, serialized. - return TRY_OR_THROW_OOM(vm, String::formatted("{}:{}", url.host().view(), url.port().value())); + return TRY_OR_THROW_OOM(vm, String::formatted("{}:{}", TRY_OR_THROW_OOM(vm, url.serialized_host()), url.port().value())); } // https://html.spec.whatwg.org/multipage/workers.html#dom-workerlocation-hostname @@ -64,11 +65,11 @@ WebIDL::ExceptionOr WorkerLocation::hostname() const auto const& host = m_global_scope->url().host(); // 2. If host is null, return the empty string. - if (host.is_empty()) + if (host.has()) return String {}; // 3. Return host, serialized. - return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(host)); + return TRY_OR_THROW_OOM(vm, URLParser::serialize_host(host)); } // https://html.spec.whatwg.org/multipage/workers.html#dom-workerlocation-port diff --git a/Userland/Libraries/LibWeb/SecureContexts/AbstractOperations.cpp b/Userland/Libraries/LibWeb/SecureContexts/AbstractOperations.cpp index 17385d72bea..41b2a49fc04 100644 --- a/Userland/Libraries/LibWeb/SecureContexts/AbstractOperations.cpp +++ b/Userland/Libraries/LibWeb/SecureContexts/AbstractOperations.cpp @@ -28,20 +28,29 @@ Trustworthiness is_origin_potentially_trustworthy(HTML::Origin const& origin) return Trustworthiness::PotentiallyTrustworthy; // 4. If origin’s host matches one of the CIDR notations 127.0.0.0/8 or ::1/128 [RFC4632], return "Potentially Trustworthy". - if (auto ipv4_address = IPv4Address::from_string(origin.host()); ipv4_address.has_value() && (ipv4_address->to_u32() & 0xff000000) != 0) - return Trustworthiness::PotentiallyTrustworthy; - if (auto ipv6_address = IPv6Address::from_string(origin.host()); ipv6_address.has_value() && ipv6_address == IPv6Address::loopback()) - return Trustworthiness::PotentiallyTrustworthy; + // FIXME: This would be nicer if URL::IPv4Address and URL::IPv6Address were instances of AK::IPv4Address and AK::IPv6Address + if (origin.host().has()) { + if ((origin.host().get() & 0xff000000) != 0) + return Trustworthiness::PotentiallyTrustworthy; + } else if (origin.host().has()) { + auto ipv6_address = origin.host().get(); + static constexpr AK::URL::IPv6Address loopback { 0, 0, 0, 0, 0, 0, 0, 1 }; + if (ipv6_address == loopback) + return Trustworthiness::PotentiallyTrustworthy; + } // 5. If the user agent conforms to the name resolution rules in [let-localhost-be-localhost] and one of the following is true: // - origin’s host is "localhost" or "localhost." // - origin’s host ends with ".localhost" or ".localhost." // then return "Potentially Trustworthy". // Note: See § 5.2 localhost for details on the requirements here. - if (origin.host().is_one_of("localhost"sv, "localhost.") - || origin.host().ends_with(".localhost"sv) - || origin.host().ends_with(".localhost."sv)) { - return Trustworthiness::PotentiallyTrustworthy; + if (origin.host().has()) { + auto const& host = origin.host().get(); + if (host.is_one_of("localhost"sv, "localhost.") + || host.ends_with_bytes(".localhost"sv) + || host.ends_with_bytes(".localhost."sv)) { + return Trustworthiness::PotentiallyTrustworthy; + } } // 6. If origin’s scheme is "file", return "Potentially Trustworthy". diff --git a/Userland/Libraries/LibWeb/URL/URL.cpp b/Userland/Libraries/LibWeb/URL/URL.cpp index 30937dc2128..a9ec5f547ab 100644 --- a/Userland/Libraries/LibWeb/URL/URL.cpp +++ b/Userland/Libraries/LibWeb/URL/URL.cpp @@ -235,15 +235,15 @@ WebIDL::ExceptionOr URL::host() const auto& url = m_url; // 2. If url’s host is null, then return the empty string. - if (url.host().is_null()) + if (url.host().has()) return String {}; // 3. If url’s port is null, return url’s host, serialized. if (!url.port().has_value()) - return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(url.host())); + return TRY_OR_THROW_OOM(vm, url.serialized_host()); // 4. Return url’s host, serialized, followed by U+003A (:) and url’s port, serialized. - return TRY_OR_THROW_OOM(vm, String::formatted("{}:{}", url.host(), *url.port())); + return TRY_OR_THROW_OOM(vm, String::formatted("{}:{}", TRY_OR_THROW_OOM(vm, url.serialized_host()), *url.port())); } // https://url.spec.whatwg.org/#dom-url-hostref-for-dom-url-host%E2%91%A0 @@ -265,11 +265,11 @@ WebIDL::ExceptionOr URL::hostname() const auto& vm = realm().vm(); // 1. If this’s URL’s host is null, then return the empty string. - if (m_url.host().is_null()) + if (m_url.host().has()) return String {}; // 2. Return this’s URL’s host, serialized. - return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(m_url.host())); + return TRY_OR_THROW_OOM(vm, m_url.serialized_host()); } // https://url.spec.whatwg.org/#ref-for-dom-url-hostname① @@ -473,7 +473,7 @@ HTML::Origin url_origin(AK::URL const& url) 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. // 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(), String {}, 0); } // -> Otherwise diff --git a/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.cpp b/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.cpp index b94a22363c2..933679798d6 100644 --- a/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.cpp +++ b/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.cpp @@ -388,7 +388,7 @@ WebIDL::ExceptionOr XMLHttpRequest::open(String const& method_string, Stri // NOTE: This is handled in the overload lacking the async argument. // 8. If parsedURL’s host is non-null, then: - if (!parsed_url.host().is_null()) { + if (!parsed_url.host().has()) { // 1. If the username argument is not null, set the username given parsedURL and username. if (username.has_value()) parsed_url.set_username(username.value().to_deprecated_string()); diff --git a/Userland/Libraries/LibWebSocket/Impl/WebSocketImplSerenity.cpp b/Userland/Libraries/LibWebSocket/Impl/WebSocketImplSerenity.cpp index 2becc2a4063..80593e3c187 100644 --- a/Userland/Libraries/LibWebSocket/Impl/WebSocketImplSerenity.cpp +++ b/Userland/Libraries/LibWebSocket/Impl/WebSocketImplSerenity.cpp @@ -42,17 +42,19 @@ void WebSocketImplSerenity::connect(ConnectionInfo const& connection_info) VERIFY(on_connection_error); VERIFY(on_ready_to_read); auto socket_result = [&]() -> ErrorOr> { + auto host = TRY(connection_info.url().serialized_host()).to_deprecated_string(); if (connection_info.is_secure()) { TLS::Options options; options.set_alert_handler([this](auto) { on_connection_error(); }); + return TRY(Core::BufferedSocket::create( - TRY(TLS::TLSv12::connect(connection_info.url().host(), connection_info.url().port_or_default(), move(options))))); + TRY(TLS::TLSv12::connect(host, connection_info.url().port_or_default(), move(options))))); } return TRY(Core::BufferedTCPSocket::create( - TRY(Core::TCPSocket::connect(connection_info.url().host(), connection_info.url().port_or_default())))); + TRY(Core::TCPSocket::connect(host, connection_info.url().port_or_default())))); }(); if (socket_result.is_error()) { diff --git a/Userland/Libraries/LibWebSocket/WebSocket.cpp b/Userland/Libraries/LibWebSocket/WebSocket.cpp index 4be5b4613ea..ea318d0f091 100644 --- a/Userland/Libraries/LibWebSocket/WebSocket.cpp +++ b/Userland/Libraries/LibWebSocket/WebSocket.cpp @@ -171,7 +171,7 @@ void WebSocket::send_client_handshake() // 4. Host auto url = m_connection.url(); - builder.appendff("Host: {}", url.host()); + builder.appendff("Host: {}", url.serialized_host().release_value_but_fixme_should_propagate_errors()); if (!m_connection.is_secure() && url.port_or_default() != 80) builder.appendff(":{}", url.port_or_default()); else if (m_connection.is_secure() && url.port_or_default() != 443) diff --git a/Userland/Libraries/LibWebView/ViewImplementation.cpp b/Userland/Libraries/LibWebView/ViewImplementation.cpp index 818a52580bb..0d1a5e039e7 100644 --- a/Userland/Libraries/LibWebView/ViewImplementation.cpp +++ b/Userland/Libraries/LibWebView/ViewImplementation.cpp @@ -344,8 +344,8 @@ void ViewImplementation::handle_web_content_process_crash() builder.append(escape_html_entities(m_url.to_deprecated_string())); builder.append(""sv); builder.append("

Web page crashed"sv); - if (!m_url.host().is_empty()) { - builder.appendff(" on {}", escape_html_entities(m_url.host())); + if (!m_url.host().has()) { + builder.appendff(" on {}", escape_html_entities(m_url.serialized_host().release_value_but_fixme_should_propagate_errors())); } builder.append("

"sv); auto escaped_url = escape_html_entities(m_url.to_deprecated_string()); diff --git a/Userland/Services/RequestServer/ConnectionCache.cpp b/Userland/Services/RequestServer/ConnectionCache.cpp index 1e70788015d..efcb88900ee 100644 --- a/Userland/Services/RequestServer/ConnectionCache.cpp +++ b/Userland/Services/RequestServer/ConnectionCache.cpp @@ -23,7 +23,7 @@ void request_did_finish(URL const& url, Core::Socket const* socket) dbgln_if(REQUESTSERVER_DEBUG, "Request for {} finished", url); - ConnectionKey partial_key { url.host(), url.port_or_default() }; + ConnectionKey partial_key { url.serialized_host().release_value_but_fixme_should_propagate_errors().to_deprecated_string(), url.port_or_default() }; auto fire_off_next_job = [&](auto& cache) { auto it = find_if(cache.begin(), cache.end(), [&](auto& connection) { return connection.key.hostname == partial_key.hostname && connection.key.port == partial_key.port; }); if (it == cache.end()) { diff --git a/Userland/Services/RequestServer/ConnectionCache.h b/Userland/Services/RequestServer/ConnectionCache.h index 9f270a8c387..76675b01da0 100644 --- a/Userland/Services/RequestServer/ConnectionCache.h +++ b/Userland/Services/RequestServer/ConnectionCache.h @@ -37,14 +37,14 @@ struct Proxy { ErrorOr> tunnel(URL const& url, Args&&... args) { if (data.type == Core::ProxyData::Direct) { - return TRY(SocketType::connect(url.host(), url.port_or_default(), forward(args)...)); + return TRY(SocketType::connect(url.serialized_host().release_value_but_fixme_should_propagate_errors().to_deprecated_string(), url.port_or_default(), forward(args)...)); } if (data.type == Core::ProxyData::SOCKS5) { if constexpr (requires { SocketType::connect(declval(), *proxy_client_storage, forward(args)...); }) { - proxy_client_storage = TRY(Core::SOCKSProxyClient::connect(data.host_ipv4, data.port, Core::SOCKSProxyClient::Version::V5, url.host(), url.port_or_default())); - return TRY(SocketType::connect(url.host(), *proxy_client_storage, forward(args)...)); + proxy_client_storage = TRY(Core::SOCKSProxyClient::connect(data.host_ipv4, data.port, Core::SOCKSProxyClient::Version::V5, url.serialized_host().release_value_but_fixme_should_propagate_errors().to_deprecated_string(), url.port_or_default())); + return TRY(SocketType::connect(url.serialized_host().release_value_but_fixme_should_propagate_errors().to_deprecated_string(), *proxy_client_storage, forward(args)...)); } else if constexpr (IsSame) { - return TRY(Core::SOCKSProxyClient::connect(data.host_ipv4, data.port, Core::SOCKSProxyClient::Version::V5, url.host(), url.port_or_default())); + return TRY(Core::SOCKSProxyClient::connect(data.host_ipv4, data.port, Core::SOCKSProxyClient::Version::V5, url.serialized_host().release_value_but_fixme_should_propagate_errors().to_deprecated_string(), url.port_or_default())); } else { return Error::from_string_literal("SOCKS5 not supported for this socket type"); } @@ -173,7 +173,7 @@ ErrorOr recreate_socket_if_needed(T& connection, URL const& url) decltype(auto) get_or_create_connection(auto& cache, URL const& url, auto& job, Core::ProxyData proxy_data = {}) { using CacheEntryType = RemoveCVReferencevalue)>; - auto& sockets_for_url = *cache.ensure({ url.host(), url.port_or_default(), proxy_data }, [] { return make(); }); + auto& sockets_for_url = *cache.ensure({ url.serialized_host().release_value_but_fixme_should_propagate_errors().to_deprecated_string(), url.port_or_default(), proxy_data }, [] { return make(); }); Proxy proxy { proxy_data }; diff --git a/Userland/Services/RequestServer/ConnectionFromClient.cpp b/Userland/Services/RequestServer/ConnectionFromClient.cpp index a3d739cb8c1..0ca1736eed2 100644 --- a/Userland/Services/RequestServer/ConnectionFromClient.cpp +++ b/Userland/Services/RequestServer/ConnectionFromClient.cpp @@ -147,7 +147,7 @@ void ConnectionFromClient::ensure_connection(URL const& url, ::RequestServer::Ca } if (cache_level == CacheLevel::ResolveOnly) { - return Core::deferred_invoke([host = url.host()] { + return Core::deferred_invoke([host = url.serialized_host().release_value_but_fixme_should_propagate_errors().to_deprecated_string()] { dbgln("EnsureConnection: DNS-preload for {}", host); (void)gethostbyname(host.characters()); }); @@ -156,7 +156,8 @@ void ConnectionFromClient::ensure_connection(URL const& url, ::RequestServer::Ca auto& job = Job::ensure(url); dbgln("EnsureConnection: Pre-connect to {}", url); auto do_preconnect = [&](auto& cache) { - auto it = cache.find({ url.host(), url.port_or_default() }); + auto serialized_host = url.serialized_host().release_value_but_fixme_should_propagate_errors().to_deprecated_string(); + auto it = cache.find({ serialized_host, url.port_or_default() }); if (it == cache.end() || it->value->is_empty()) ConnectionCache::get_or_create_connection(cache, url, job); }; diff --git a/Userland/Services/WebContent/ConnectionFromClient.cpp b/Userland/Services/WebContent/ConnectionFromClient.cpp index 48f290141eb..64eadef3362 100644 --- a/Userland/Services/WebContent/ConnectionFromClient.cpp +++ b/Userland/Services/WebContent/ConnectionFromClient.cpp @@ -112,10 +112,10 @@ void ConnectionFromClient::load_url(const URL& url) #if defined(AK_OS_SERENITY) DeprecatedString process_name; - if (url.host().is_empty()) + if (url.host().has() || url.host() == String {}) process_name = "WebContent"; else - process_name = DeprecatedString::formatted("WebContent: {}", url.host()); + process_name = DeprecatedString::formatted("WebContent: {}", url.serialized_host().release_value_but_fixme_should_propagate_errors()); pthread_setname_np(pthread_self(), process_name.characters()); #endif diff --git a/Userland/Shell/AST.cpp b/Userland/Shell/AST.cpp index 7c6163a5273..e39062c3af5 100644 --- a/Userland/Shell/AST.cpp +++ b/Userland/Shell/AST.cpp @@ -679,7 +679,7 @@ ErrorOr BarewordLiteral::highlight_in_editor(Line::Editor& editor, Shell& if (FileSystem::exists(m_text)) { auto realpath = shell.resolve_path(m_text.bytes_as_string_view()); auto url = URL::create_with_file_scheme(realpath); - url.set_host(shell.hostname); + url.set_host(TRY(String::from_deprecated_string(shell.hostname))); editor.stylize({ m_position.start_offset, m_position.end_offset }, { Line::Style::Hyperlink(url.to_deprecated_string()) }); } return {}; @@ -2620,7 +2620,7 @@ ErrorOr PathRedirectionNode::highlight_in_editor(Line::Editor& editor, She if (!path.starts_with('/')) path = String::formatted("{}/{}", shell.cwd, path).release_value_but_fixme_should_propagate_errors(); auto url = URL::create_with_file_scheme(path.to_deprecated_string()); - url.set_host(shell.hostname); + url.set_host(TRY(String::from_deprecated_string(shell.hostname))); editor.stylize({ position.start_offset, position.end_offset }, { Line::Style::Hyperlink(url.to_deprecated_string()) }); } return {}; @@ -3214,7 +3214,7 @@ ErrorOr Juxtaposition::highlight_in_editor(Line::Editor& editor, Shell& sh if (FileSystem::exists(path)) { auto realpath = shell.resolve_path(path); auto url = URL::create_with_file_scheme(realpath); - url.set_host(shell.hostname); + url.set_host(TRY(String::from_deprecated_string(shell.hostname))); editor.stylize({ m_position.start_offset, m_position.end_offset }, { Line::Style::Hyperlink(url.to_deprecated_string()) }); } diff --git a/Userland/Utilities/markdown-check.cpp b/Userland/Utilities/markdown-check.cpp index 7662ffcfae6..fd5d1fc5d88 100644 --- a/Userland/Utilities/markdown-check.cpp +++ b/Userland/Utilities/markdown-check.cpp @@ -191,7 +191,7 @@ RecursionDecision MarkdownLinkage::visit(Markdown::Text::LinkNode const& link_no return RecursionDecision::Recurse; } if (url.scheme() == "help") { - if (url.host() != "man") { + if (url.host() != String::from_utf8_short_string("man"sv)) { warnln("help:// URL without 'man': {}", href); m_has_invalid_link = true; return RecursionDecision::Recurse; diff --git a/Userland/Utilities/pro.cpp b/Userland/Utilities/pro.cpp index b29e452b995..41b4719172f 100644 --- a/Userland/Utilities/pro.cpp +++ b/Userland/Utilities/pro.cpp @@ -343,7 +343,7 @@ ErrorOr serenity_main(Main::Arguments arguments) if (output_name.is_empty() || output_name == "/") { int i = -1; do { - output_name = url.host(); + output_name = url.serialized_host().release_value_but_fixme_should_propagate_errors().to_deprecated_string(); if (i > -1) output_name = DeprecatedString::formatted("{}.{}", output_name, i); ++i;