mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 18:22:45 +00:00 
			
		
		
		
	AK: Serialize URL hosts with 'concept-host-serializer'
In order to follow spec text to achieve this, we need to change the underlying representation of a host in AK::URL to deserialized format. Before this, we were parsing the host and then immediately serializing it again. Making that change resulted in a whole bunch of fallout. After this change, callers can access the serialized data through this concept-host-serializer. The functional end result of this change is that IPv6 hosts are now correctly serialized to be surrounded with '[' and ']'.
This commit is contained in:
		
							parent
							
								
									768f070b86
								
							
						
					
					
						commit
						8751be09f9
					
				
					 36 changed files with 175 additions and 143 deletions
				
			
		
							
								
								
									
										31
									
								
								AK/URL.cpp
									
										
									
									
									
								
							
							
						
						
									
										31
									
								
								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<String> URL::serialized_host() const | ||||
| { | ||||
|     return URLParser::serialize_host(m_host); | ||||
| } | ||||
| 
 | ||||
| void URL::set_port(Optional<u16> 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<Empty>() || 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<Empty>()) | ||||
|         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<Empty>()) { | ||||
|         // 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<Empty>() && 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<Empty>()) { | ||||
|         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<Empty>() && 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(); | ||||
|  |  | |||
							
								
								
									
										7
									
								
								AK/URL.h
									
										
									
									
									
								
							
							
						
						
									
										7
									
								
								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<String> 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<u16>); | ||||
|     void set_paths(Vector<DeprecatedString>, 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<u16> m_port; | ||||
|  |  | |||
|  | @ -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<DeprecatedString> parse_opaque_host(StringView input) | ||||
| static Optional<URL::Host> 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<DeprecatedString> 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<DeprecatedString> parse_host(StringView input, bool is_not_special = false) | ||||
| static Optional<URL::Host> 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<DeprecatedString> 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<DeprecatedString> 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<DeprecatedString> 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<URL> 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<URL> 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<URL> 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<URL> 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<String>() && host.value().get<String>() == "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<URL> 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<Empty>()) { | ||||
|                 url->append_slash(); | ||||
|             } | ||||
|             break; | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -53,7 +53,7 @@ void WebSocketImplQt::connect(WebSocket::ConnectionInfo const& connection_info) | |||
|     if (connection_info.is_secure()) { | ||||
|         auto ssl_socket = make<QSslSocket>(); | ||||
|         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<QTcpSocket>(); | ||||
|         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()); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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<Empty>()); | ||||
|     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<Empty>()); | ||||
|     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<Empty>()); | ||||
|     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<Empty>()); | ||||
|     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<Empty>()); | ||||
|     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<Empty>()); | ||||
|     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<Empty>()); | ||||
|     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<Empty>()); | ||||
|     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<Empty>()); | ||||
|     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); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -248,8 +248,8 @@ void URLProvider::query(DeprecatedString const& query, Function<void(Vector<Nonn | |||
| 
 | ||||
|     if (url.scheme().is_empty()) | ||||
|         url.set_scheme("http"); | ||||
|     if (url.host().is_empty()) | ||||
|         url.set_host(query); | ||||
|     if (url.host().has<Empty>() || 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({ "" }); | ||||
| 
 | ||||
|  |  | |||
|  | @ -208,7 +208,10 @@ Optional<DeprecatedString> 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<Empty>()) | ||||
|         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) | ||||
|  |  | |||
|  | @ -107,10 +107,10 @@ void Tab::update_status(Optional<String> 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()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -83,7 +83,7 @@ HelpWindow::HelpWindow(GUI::Window* parent) | |||
|     m_webview = splitter.add<WebView::OutOfProcessWebView>(); | ||||
|     m_webview->on_link_click = [this](auto& url, auto&, auto&&) { | ||||
|         VERIFY(url.scheme() == "spreadsheet"); | ||||
|         if (url.host() == "example") { | ||||
|         if (url.host().template has<String>() && url.host().template get<String>() == "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()); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -264,7 +264,7 @@ Optional<Position> 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; | ||||
|  |  | |||
|  | @ -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<URL::IPv4Address>()) | ||||
|             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<URL::IPv4Address>(); | ||||
| 
 | ||||
|         auto port = url.port(); | ||||
|         if (!port.has_value()) | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ | |||
| 
 | ||||
| #include <AK/Base64.h> | ||||
| #include <AK/StringBuilder.h> | ||||
| #include <AK/URLParser.h> | ||||
| #include <LibHTTP/HttpRequest.h> | ||||
| #include <LibHTTP/Job.h> | ||||
| 
 | ||||
|  | @ -57,7 +58,7 @@ ErrorOr<ByteBuffer> 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)); | ||||
|  |  | |||
|  | @ -82,7 +82,7 @@ ErrorOr<NonnullRefPtr<PageNode const>> Node::try_create_from_query(Vector<String | |||
| 
 | ||||
| ErrorOr<NonnullRefPtr<Node const>> 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); | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -256,7 +256,7 @@ WebIDL::ExceptionOr<Optional<JS::NonnullGCPtr<PendingResponse>>> 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
 | ||||
|  |  | |||
|  | @ -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<Empty>(); | ||||
| } | ||||
| 
 | ||||
| // FIXME: This is an outdated older version of "determining the origin" and should be removed.
 | ||||
|  |  | |||
|  | @ -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<Empty>()) | ||||
|         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<Empty>()) | ||||
|         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) | ||||
|  |  | |||
|  | @ -153,15 +153,15 @@ WebIDL::ExceptionOr<String> 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<Empty>()) | ||||
|         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<void> Location::set_host(String const&) | ||||
|  | @ -183,11 +183,11 @@ WebIDL::ExceptionOr<String> 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<Empty>()) | ||||
|         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<void> Location::set_hostname(String const&) | ||||
|  |  | |||
|  | @ -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<Empty>(); | ||||
| } | ||||
| 
 | ||||
| // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#shared-attribute-processing-steps-for-iframe-and-frame-elements
 | ||||
|  |  | |||
|  | @ -8,13 +8,15 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <AK/DeprecatedString.h> | ||||
| #include <AK/URL.h> | ||||
| #include <AK/URLParser.h> | ||||
| 
 | ||||
| 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<Empty>() && 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<DeprecatedString> effective_domain() const | ||||
|     Optional<AK::URL::Host> effective_domain() const | ||||
|     { | ||||
|         // 1. If origin is an opaque origin, then return null.
 | ||||
|         if (is_opaque()) | ||||
|             return Optional<DeprecatedString> {}; | ||||
|             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<Web::HTML::Origin> : public GenericTraits<Web::HTML::Origin> { | ||||
|     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<Empty>()) | ||||
|             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
 | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
|  * SPDX-License-Identifier: BSD-2-Clause | ||||
|  */ | ||||
| 
 | ||||
| #include <AK/URLParser.h> | ||||
| #include <LibWeb/HTML/WorkerGlobalScope.h> | ||||
| #include <LibWeb/HTML/WorkerLocation.h> | ||||
| 
 | ||||
|  | @ -43,15 +44,15 @@ WebIDL::ExceptionOr<String> 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<Empty>()) | ||||
|         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<String> 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<Empty>()) | ||||
|         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
 | ||||
|  |  | |||
|  | @ -28,21 +28,30 @@ 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) | ||||
|     // FIXME: This would be nicer if URL::IPv4Address and URL::IPv6Address were instances of AK::IPv4Address and AK::IPv6Address
 | ||||
|     if (origin.host().has<AK::URL::IPv4Address>()) { | ||||
|         if ((origin.host().get<AK::URL::IPv4Address>() & 0xff000000) != 0) | ||||
|             return Trustworthiness::PotentiallyTrustworthy; | ||||
|     if (auto ipv6_address = IPv6Address::from_string(origin.host()); ipv6_address.has_value() && ipv6_address == IPv6Address::loopback()) | ||||
|     } else if (origin.host().has<AK::URL::IPv6Address>()) { | ||||
|         auto ipv6_address = origin.host().get<AK::URL::IPv6Address>(); | ||||
|         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)) { | ||||
|     if (origin.host().has<String>()) { | ||||
|         auto const& host = origin.host().get<String>(); | ||||
|         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".
 | ||||
|     if (origin.scheme() == "file"sv) | ||||
|  |  | |||
|  | @ -235,15 +235,15 @@ WebIDL::ExceptionOr<String> 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<Empty>()) | ||||
|         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<String> 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<Empty>()) | ||||
|         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
 | ||||
|  |  | |||
|  | @ -388,7 +388,7 @@ WebIDL::ExceptionOr<void> 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<Empty>()) { | ||||
|         // 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()); | ||||
|  |  | |||
|  | @ -42,17 +42,19 @@ void WebSocketImplSerenity::connect(ConnectionInfo const& connection_info) | |||
|     VERIFY(on_connection_error); | ||||
|     VERIFY(on_ready_to_read); | ||||
|     auto socket_result = [&]() -> ErrorOr<NonnullOwnPtr<Core::BufferedSocketBase>> { | ||||
|         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<TLS::TLSv12>::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()) { | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -344,8 +344,8 @@ void ViewImplementation::handle_web_content_process_crash() | |||
|     builder.append(escape_html_entities(m_url.to_deprecated_string())); | ||||
|     builder.append("</title></head><body>"sv); | ||||
|     builder.append("<h1>Web page crashed"sv); | ||||
|     if (!m_url.host().is_empty()) { | ||||
|         builder.appendff(" on {}", escape_html_entities(m_url.host())); | ||||
|     if (!m_url.host().has<Empty>()) { | ||||
|         builder.appendff(" on {}", escape_html_entities(m_url.serialized_host().release_value_but_fixme_should_propagate_errors())); | ||||
|     } | ||||
|     builder.append("</h1>"sv); | ||||
|     auto escaped_url = escape_html_entities(m_url.to_deprecated_string()); | ||||
|  |  | |||
|  | @ -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()) { | ||||
|  |  | |||
|  | @ -37,14 +37,14 @@ struct Proxy { | |||
|     ErrorOr<NonnullOwnPtr<StorageType>> tunnel(URL const& url, Args&&... args) | ||||
|     { | ||||
|         if (data.type == Core::ProxyData::Direct) { | ||||
|             return TRY(SocketType::connect(url.host(), url.port_or_default(), forward<Args>(args)...)); | ||||
|             return TRY(SocketType::connect(url.serialized_host().release_value_but_fixme_should_propagate_errors().to_deprecated_string(), url.port_or_default(), forward<Args>(args)...)); | ||||
|         } | ||||
|         if (data.type == Core::ProxyData::SOCKS5) { | ||||
|             if constexpr (requires { SocketType::connect(declval<DeprecatedString>(), *proxy_client_storage, forward<Args>(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>(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>(args)...)); | ||||
|             } else if constexpr (IsSame<SocketType, Core::TCPSocket>) { | ||||
|                 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<void> 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 = RemoveCVReference<decltype(*cache.begin()->value)>; | ||||
|     auto& sockets_for_url = *cache.ensure({ url.host(), url.port_or_default(), proxy_data }, [] { return make<CacheEntryType>(); }); | ||||
|     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<CacheEntryType>(); }); | ||||
| 
 | ||||
|     Proxy proxy { proxy_data }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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); | ||||
|     }; | ||||
|  |  | |||
|  | @ -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<Empty>() || 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 | ||||
|  |  | |||
|  | @ -679,7 +679,7 @@ ErrorOr<void> 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<void> 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<void> 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()) }); | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -343,7 +343,7 @@ ErrorOr<int> 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; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Shannon Booth
						Shannon Booth